zoukankan      html  css  js  c++  java
  • JAVA使用JNI调用C++动态链接库

    JAVA使用JNI调用C++动态链接库

    使用JNI连接DLL动态链接库,并调用其中的函数

     首先 C++中写好相关函数,文件名为test.cpp,使用g++编译为DLL文件,指令如下:

    g++ -shared -Wl,--kill-at,--output-def,test.def -o test.dll test.cpp
    #如果cpp中要调用其他dll,需要在命令后面添加相关lib描述 

    这样就在当路径下同时生成了test.def 和 test.dll 文件

    顺便说一下,.lib文件可以通过.def文件生成,生成方法是用VS中的

    lib /def:xxx.def /MACHINE:x86(或者X64)命令

    得到.dll文件后,JAVA理论上就可以使用JNI调用了

    public class JNIDemo1 {
     
        public static native short  connectCNC(String ip);
        public static native void   writeData();
     
        static {
            System.loadLibrary("melwin");
            System.loadLibrary("melcfg");
            System.loadLibrary("melsmem");
            System.loadLibrary("chgapivl");
            System.loadLibrary("meldev");
            System.loadLibrary("melmdldr");
            System.loadLibrary("melvnckd");
            System.loadLibrary("ncMocha");
            System.loadLibrary("nccom");
            System.loadLibrary("ncapi32");
            System.loadLibrary("test");
        }//系统会自己判断后缀。
     
        public static void main(String[] args) {
     
            short res=connectCNC("192.168.200.1");
     
            if(res==0){
                System.out.println("Connect Success!	Data Writing...
    ");
                writeData();
            }else{
                System.out.println("Connect Fail.code:"+res);
            }
        }
    } 

    两个native函数是要调用的C++函数

    需要在主类中使用native关键字事先定义

    加载lib文件的后缀不要描述,让JAVA根据OS平台自己判断

    加载lib的命令有先后顺序之分,一定要按照调用层级来书写命令顺序

    如果不清楚dll之间的调用顺序,可以下载“DLL依赖查看工具”来解析DLL

    加载dll时会报错no test in java.library.path

    原因是JNI找不到你的dll文件在哪里,这时候要调整一下eclipse项目工程的build path

    修改jdk中的native library location,指向你的dll路径,截图如下:

    如果编译dll的g++是64位的,jdk是32位的,会报错

    Can't load AMD 64-bit .dll on a IA 32-bit platform

    反之会报错

    Can't load  IA 32-bit .dll on a AMD 64-bit platform

    遇到这类问题,不需要尝试去更换g++编译器,因为引用到的外部dll大部分都是32位的

    而我们的JDK可以同时安装32位和64位的,在系统中并不冲突

    所以应该去下载对应架构的JDK重新编译执行java文件

    编译好java文件后,不要急着运行,还有一些工作要回到C++中完成

    编译得到.class文件,一般eclipse保存一下没有语法错误就生成好.class文件了

    cmd到src根目录,使用javah命令生成.h头文件,你也可以直接编写.h头文件,格式如下:

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_saiyang_newflypig_jnidemo_JNIDemo1 */
     
    #ifndef _Included_com_saiyang_newflypig_jnidemo_JNIDemo1
    #define _Included_com_saiyang_newflypig_jnidemo_JNIDemo1
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     com_saiyang_newflypig_jnidemo_JNIDemo1
     * Method:    connectCNC
     * Signature: (Ljava/lang/String;)S
     */
    JNIEXPORT jshort JNICALL Java_com_saiyang_newflypig_jnidemo_JNIDemo1_connectCNC
      (JNIEnv *, jclass, jstring);
     
    JNIEXPORT void JNICALL Java_com_saiyang_newflypig_jnidemo_JNIDemo1_writeData
      (JNIEnv *, jclass);
     
    #ifdef __cplusplus
    }
    #endif
    #endif

    函数名不要搞错了,JNI要通过这些规范去寻找相关函数

    可以在规范函数中直接调用自己写的其他任意纯净格式的函数

    .h头文件有了,自然就要修改一下cpp文件:

    #define BUILD_MIT_DLL
     
    #include <stdio.h>
    #include <string>
     
    #include "melncapi.h"
    #include "ncmcapi.h"
    #include "melsect.h"
    #include "melssect.h"
    #include "meltype.h"
     
    #include "test.h"
     
    using namespace std;
     
    void readData();
    void writeData();
    short connectCNC(string);
    char* jstring2char(JNIEnv*,jstring);
     
    int main(){
        DWORD res=connectCNC("192.168.200.1");
        if(res != 0) {
            printf("Fail...code:%d
    ",res);
        }
        else {
            printf("Connect Success!
    ");
            writeData();
        }
     
        return 0;
    }
     
    short connectCNC(string ip){
        MELDEVICEDATA MelIoctlData;
        DWORD dwStatus=0;
        MelIoctlData.uniDeviceInfo.Tcp.lPortNo = 683;
        memset(MelIoctlData.uniDeviceInfo.Tcp.IPAddr, 0, 16);
        strcpy(MelIoctlData.uniDeviceInfo.Tcp.IPAddr, ip.data());
        MelIoctlData.dwDeviceType = DEVICETYPE_TCP;
     
        // long OldTimeOut = 0, TimeOut = 2;
        // dwStatus = melIoctl(NULL, ADR_MACHINE(1), DEV_GET_COMMTIMEOUT, &OldTimeOut);
        // if(dwStatus == 0)
        //  melIoctl(NULL, ADR_MACHINE(1), DEV_SET_COMMTIMEOUT, &TimeOut);
        dwStatus = melIoctl(NULL, ADR_MACHINE(1), DEV_SET_COMMADDRESS, &MelIoctlData);
        return dwStatus;
    }
     
    void readData(){
        //long lSectionNum = M_SEC_PLC_DEV_WORD;
        //M_SEC_PLC_DEV_BIT、M_SEC_PLC_DEV_CHAR、M_SEC_PLC_DEV_WORD
        long lSectionNum = M_SEC_PLC_DEV_BIT;
        long lAddress = ADR_MACHINE(1);
        long lAxisFlag = 0;
        short lGetData = 0;
        short y=0;
     
        while(scanf("%d",&y),y!=-1){
            long lSubSectionNum = M_SSEC_PLLNG_Y_1SHOT(y);
            DWORD dwStatus = melGetData(NULL, lAddress, lSectionNum, lSubSectionNum, lAxisFlag, &lGetData, T_LONG);
            printf("%d
    ", lGetData);
        }
    }
     
    void writeData(){
        long lSectionNum = M_SEC_PLC_DEV_BIT;
        long lAddress = ADR_MACHINE(1);
        long lAxisFlag = 0;
        short lSetData;
        short xNum;
     
        while(scanf("%d",&xNum),xNum!=-1){
            scanf("%d",&lSetData);
            long lSubSectionNum = M_SSEC_PLLNG_X_1SHOT(xNum);
            DWORD dwStatus = melSetData(NULL, lAddress, lSectionNum, lSubSectionNum, lAxisFlag, &lSetData, T_LONG);
            if(dwStatus == 0) {
                printf("Write Success!
    ");
            }else{
                printf("Write Fail!code:%d
    ",dwStatus);
            }
        }
    }
     
    JNIEXPORT jshort JNICALL Java_com_saiyang_newflypig_jnidemo_JNIDemo1_connectCNC(JNIEnv* env, jclass, jstring ip){
        return connectCNC(jstring2char(env,ip));
    }
     
    JNIEXPORT void JNICALL Java_com_saiyang_newflypig_jnidemo_JNIDemo1_writeData(JNIEnv *, jclass){
        writeData();
    }
     
    /**
     * 返回值 char* 这个代表char数组的首地址
     *  Jstring2CStr 把java中的jstring的类型转化成一个c语言中的char 字符串
     */
    // jstring To char* 
    char* jstring2char(JNIEnv* env, jstring jstr) 
    { 
        char* rtn = NULL; 
        jclass clsstring = env->FindClass("java/lang/String"); 
        jstring strencode = env->NewStringUTF("utf-8"); 
        jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B"); 
        jbyteArray barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode); 
        jsize alen = env->GetArrayLength(barr); 
        jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE); 
        if (alen > 0) 
        { 
            rtn = (char*)malloc(alen + 1); 
            memcpy(rtn, ba, alen); 
            rtn[alen] = 0; 
        } 
        env->ReleaseByteArrayElements(barr, ba, 0); 
        return rtn; 
    }

    这下就大功告成了,还记的我们一开始执行的g++命令编译dll吗

    重新执行一遍,生成.dll后,执行java程序,应该就可以调用了。

  • 相关阅读:
    httpcontext in asp.net unit test
    initialize or clean up your unittest within .net unit test
    Load a script file in sencha, supports both asynchronous and synchronous approaches
    classes system in sencha touch
    ASP.NET MVC got 405 error on HTTP DELETE request
    how to run demo city bars using sencha architect
    sencha touch mvc
    sencha touch json store
    sencha touch jsonp
    51Nod 1344:走格子(贪心)
  • 原文地址:https://www.cnblogs.com/newflydd/p/5424212.html
Copyright © 2011-2022 走看看