使用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!\tData Writing...\n");
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\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程序,应该就可以调用了。
src="http://player.youku.com/embed/XNzkxMTc3OTM2" allowfullscreen="" frameborder="0" height="498" width="510">