• 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程序,应该就可以调用了。

  • 相关阅读:
    RAID磁盘阵列
    Activiti任务认领
    Activiti 5.18启动流程到完成所有任务之间的数据库变化(转)
    tomcat优化(转)
    DB2 OLAP函数的使用
    PreparedStatement批量处理和事务
    获取JavaScript异步函数的返回值
    DB2 sql报错后查证原因与解决问题的方法
    DB2有五种约束
    连接db2数据库出现No buffer space available (maximum connections reached?)
  • 原文地址:https://www.cnblogs.com/newflydd/p/5424212.html
Copyright © 2020-2023  润新知