1. 程式人生 > >Java 通過 JNA 呼叫 DLL 返回 char * 字串亂碼問題的解決

Java 通過 JNA 呼叫 DLL 返回 char * 字串亂碼問題的解決

    最近一個 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 便可以接受,但是目前測試,沒有通過。