近日项目中需要用java调用c/c++编写的dll库,所有了解到jna这个东东,下面是使用的一些经验:

一、java使用Jna需要两个jar包,eg:jna-3.5.1.jar和platform-3.5.1.jar 下载地址,添加完依赖包后把需调用的dll放到项目根目录下就是和src同级目录下

java调用dll报错找不到指定的模块 jna调用dll找不到指定的模块_Java

二、报错:Unable to load DLL 'xxx.dll': 找不到指定的模块,可能有一下几个问题:

     1、使用的jdk和dll位数不同,64位的jdk只能调用64位的dll,32一样。

     2、dll的位置放的不对(也有说放在c盘的systen32下的)

  3、电脑缺少dll依赖的组件(例如我重装完系统怎么调用都不成功,最后发现缺少了Visual C++ Redistributable Packages for Visual Studio 2013这个组件 下载地址,也有可能是其他组件可以用VS的插件查看,具体请百度)

三、java-c 数据类型映射   jna操作文档 下载地址

常见的映射就不说了,这里说一下我项目中用到的:

1、char*&

//dll中
int pack_clou102(char*& sendstr)

//java中接口    PointerByReference 表示指针的引用类型
public int pack_clou102(PointerByReference send);

//获取send      
String str = send.getValue().getString(0);
//send.getValue()获取的是一个指针   而getString(0)是获取指针的值   这里不可以用
send.getValue().toString()//会导致乱码

2、char*

//根据dll的操作来决定,官方char*对应String

//但是下面这个例子中用byte[]才可以

//dll中
//UINT8是指无符号8位二进制整型  在这里映射String会出现编码问题的,所以这里用byte[]
int unpack_clou102(char* recvbuf)
{
	UINT8* pbuf = (UINT8*)recvbuf;   
	UINT8 ucCheckSum = 0;// 校验和

。。。
}

//java中
 public int unpack_clou102(byte[] recvbuf);

3、传参char[]

//dll
int pack_clou102(char[20] send){
。。。
return 0;
}

//有时候会遇到dll中用char[]传字符串的,java中是用byte[],这时候可以借用“”.getbytes()


//jna
byte[20] bytes = "2016-08-29 11:06:23".getbytes();
int mun = pack_clou102(bytes);

4、传参结构体    可以参考  

//DLL中
struct CompanyStruct{  
     long id;  
     wchar_t* name;  
     UserStruct* users[100];  
     int count;  
};
//java中
public static class CompanyStruct2 extends Structure{  
  public NativeLong id;  
  public WString name;  
  public UserStruct.ByReference[] users=new UserStruct.ByReference[100];  
  public int count;  
}
//测试代码
CompanyStruct2.ByReference companyStruct2=new CompanyStruct2.ByReference();  
companyStruct2.id=new NativeLong(2);  
companyStruct2.name=new WString("Yahoo");  
companyStruct2.count=10;  
UserStruct.ByReference pUserStruct=new UserStruct.ByReference();  
pUserStruct.id=new NativeLong(90);  
pUserStruct.age=99;  
pUserStruct.name=new WString("杨致远");  
// pUserStruct.write();  
for(int i=0;i<companyStruct2.count;i++){  
companyStruct2.users[i]=pUserStruct;  
}  
  
TestDll1.INSTANCE.sayCompany2(companyStruct2);

 

执行测试代码,报错了。这是怎么回事?
考察JNI 技术,我们发现Java 调用原生函数时,会把传递给原生函数的Java 数据固定
在内存中,这样原生函数才可以访问这些Java 数据。对于没有固定住的Java 对象,GC 可以
删除它,也可以移动它在内存中的位置,以使堆上的内存连续。如果原生函数访问没有被固
定住的Java 对象,就会导致调用失败。
固定住哪些java 对象,是JVM 根据原生函数调用自动判断的。而上面的CompanyStruct2
结构体中的一个字段是UserStruct 对象指针的数组,因此,JVM 在执行时只是固定住了
CompanyStruct2 对象的内存,而没有固定住users 字段引用的UserStruct 数组。因此,造成
了错误。
我们需要把users 字段引用的UserStruct 数组的所有成员也全部固定住,禁止GC 移动
或者删除。
如果我们执行了pUserStruct.write();这段代码,那么就可以成功执行上述代码。
Structure 类的write()方法会把结构体的所有字段固定住,使原生函数可以访问。