使用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程序,應該就能夠調用了。