近日項目中需要用java調用c/c++編寫的dll庫,所有了解到jna這個東東,下面是使用的一些經驗:
一、java使用Jna需要兩個jar包,eg:jna-3.5.1.jar和platform-3.5.1.jar 下載地址,添加完依賴包后把需調用的dll放到項目根目錄下就是和src同級目錄下
二、報錯: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()方法會把結構體的所有字段固定住,使原生函數可以訪問。