Java 通過 JNA 呼叫 DLL 返回 char * 字串亂碼問題的解決
阿新 • • 發佈:2019-01-01
最近一個 Java 專案需要呼叫公司的讀卡器讀取卡號。C 開發提供了一個讀取卡號的 DLL。
Java 呼叫 DLL 無非三種方法:JNI、JNA、JNative
本來 C 開發測試時用了 JNative.jar 來呼叫 DLL,但是網路上的 JNative.jar 都是基於 32 位系統,其 jar 包中的兩個 DLL 也均為 32 位。
在我本地嘗試時都報錯:
Exception in thread "main" java.lang.IllegalStateException: JNative library not loaded, sorry ! at org.xvolks.jnative.JNative.<init>(JNative.java:512) at org.xvolks.jnative.JNative.<init>(JNative.java:440) at JNAtest.testDll(JNAtest.java:18) at JNAtest.main(JNAtest.java:47)
於是 JNative 在 64 位系統上基本上沒有辦法,於是轉向 JNA。
JNA 的好處在於,程式碼都是基於 Java,對於 Java 程式設計師來說簡單易懂。
在開發過程中,目前遇到了一些問題,總結就是 DLL 返回 char * 時,Java 端解析出現亂碼。(甚至是英文字串都亂碼)
首先是 DLL 內的兩個方法:
char * test1(){
char buf[100] = "helloworld" ;
return buf ;
}
char * test2(){
reutrn "helloworld" ;
}
Java 程式碼如下:
public class DLLUtil { private static final String path = ConfigUtil.get("dllpath") ; private static final String name = ConfigUtil.get("dllname") ; public interface CLibrary extends Library{ //定義並初始化介面的靜態變數 // path + File.separator + name = "F:/test/dll/test.dll" CLibrary Instance=(CLibrary)Native.loadLibrary(path + File.separator + name,CLibrary.class); String test1() ; String test2() ; } public static void main(String[] args) { System.setProperty("jna.encoding", "GBK"); String str1 = CLibrary.Instance.test1() ; String str2 = CLibrary.Instance.test2() ; System.out.println("test1_reply:" + str1) ; // 此處一直亂碼 System.out.println("test2_reply:" + str2) ; // 此處正常為 helloworld } }
通過 Java 解析 test1() 方法時,每次都會出現亂碼,即使返回的字串是純英文。但是 test2() 方法就沒有出現問題。
此問題困擾了挺久的時間,後來查詢 JNA API 和 上網查詢,經過測試,解決了 test1() 方法解析亂碼的問題。
這裡需要修改 DLL 和 Java 程式碼。
DLL 程式碼修改後如下:
void test1(char * buf){
char temp[100] = "helloworld" ;
memcpy(buf, temp, strlen(temp)) ;
return ;
}
Java 程式碼修改如下:
public class DLLUtil { private static final String path = ConfigUtil.get("dllpath") ; private static final String name = ConfigUtil.get("dllname") ; public interface CLibrary extends Library{ //定義並初始化介面的靜態變數 // path + File.separator + name = "F:/test/dll/test.dll" CLibrary Instance=(CLibrary)Native.loadLibrary(path + File.separator + name,CLibrary.class); void test1(Pointer p) ; } public static void main(String[] args) { System.setProperty("jna.encoding", "GBK"); // 首先定義指標,開闢記憶體空間,這裡的記憶體空間根據返回的字串來決定 Pointer p = new Memory(11) ;; CLibrary.Instance.test1(p) ; for(int i=0, sumi=11; i< sumi; i++){ System.out.print((char) p.getByteArray(0, 11)[i]); } System.out.println("\n"); } }
通過 Java 獲取 char * 字串,必須要通過 Java 傳入一個 com.sun.jna.Pointer 指標變數,然後在 DLL 中將值賦給此指標變數,然後通過此指標變數獲取值。
至此,一直困擾的亂碼問題解決。
很多資料都說 DLL 返回 char * 在 Java 中通過 String 便可以接受,但是目前測試,沒有通過。