JAVA使用JNI調用C++動態連接庫

JAVA使用JNI調用C++動態連接庫

使用JNI鏈接DLL動態連接庫,並調用其中的函數html

 

 首先 C++中寫好相關函數,文件名爲test.cpp,使用g++編譯爲DLL文件,指令以下:java

 

g++ -shared -Wl,--kill-at,--output-def,test.def -o test.dll test.cpp
#若是cpp中要調用其餘dll,須要在命令後面添加相關lib描述 

 

這樣就在當路徑下同時生成了test.def 和 test.dll 文件api

順便說一下,.lib文件能夠經過.def文件生成,生成方法是用VS中的數組

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

 

獲得.dll文件後,JAVA理論上就可使用JNI調用了eclipse

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!\tData Writing...\n");
            writeData();
        }else{
            System.out.println("Connect Fail.code:"+res);
        }
    }
} 

 

兩個native函數是要調用的C++函數ide

須要在主類中使用native關鍵字事先定義函數

 

加載lib文件的後綴不要描述,讓JAVA根據OS平臺本身判斷工具

加載lib的命令有前後順序之分,必定要按照調用層級來書寫命令順序ui

若是不清楚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\n",res);
    }
    else {
        printf("Connect Success!\n");
        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\n", 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!\n");
        }else{
            printf("Write Fail!code:%d\n",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程序,應該就能夠調用了。

相關文章
相關標籤/搜索