首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 調用 DLL 實戰:基於 JNative 實現 native 方法調用與參數傳遞詳解

     編輯:Java 相關知識

Java 調用 Windows DLL 主流方案:JNI (標準、繁瑣)、JNA (推薦、純 Java)、JNative (輕量、開源但停維)。本文記錄基於 **JNative** 在 MyEclipse 10 環境下,從項目創建、JAR 引入、DLL 放置、參數類型映射、指針處理到中文亂碼排查的完整實戰過程。

一、方案選型對比

方案特點適用場景
JNI官方標準,需寫 C/C++ 胶水代碼編譯成動態庫,調試難,跨平台需多編譯高性能、系統級、需精細控制內存
JNA純 Java,基於 libffi 運行時動態綁定,**無需編譯本地代碼**,API 豐富,社區活躍現代首選,大多數 DLL/so 調用場景
JNative輕量封裝 JNI,直接映射導出函數,**項目已停止維護**,文檔極少,64 位支持弱老項目維護、極簡調用、學習原理

註:新項目**強烈建議用 JNA**,本文僅作 JNative 歷史經驗記錄。

二、環境準備與項目結構

  1. MyEclipse 10 / Eclipse + JDK 32 位 (JNative 多為 32 位.native 庫)。
  2. 新建 Java Project → 新建 Package com.ehfscliax → 新建 Class Helper (含 main)。
  3. 項目根目錄創建 lib/ 放入 JNative.jar,右鍵 Build Path → Add to Classpath。
  4. **關鍵**:待調用 DLL (EhfscliaxDll.dll) 放在 **項目根目錄** (與 src 同級),而非 lib/ 或 bin/。運行時工作目錄為項目根,System.loadLibrary 會在此查找。

三、DLL 接口定義 (C++ 端)

// EhfscliaxDll.h
extern "C" __declspec(dllexport) const char* getUrl(const char* ip, int port, int id);
extern "C" __declspec(dllexport) const wchar_t* getPlayUrl(const wchar_t* ip, int port, int id);
  • getUrl:返回 const char* (ANSI/UTF-8 窄字符)。
  • getPlayUrl:返回 const wchar_t* (Unicode 寬字符)。

四、JNative 核心 API 與類型映射

關鍵方法:

  • new JNative(dllName, funcName):綁定導出函數。
  • setRetVal(Type):聲明返回類型。
  • setParameter(index, value):按順序設置參數 (索引從 0 開始)。
  • invoke():執行調用。
  • getRetVal():獲取返回值 (String/Number)。
  • getRetValAsInt():獲取返回指針地址 (int/long)。
  • JNative.getUnicodeMemoryAsString(ptrAddress):從內存地址讀取 Unicode 字符串。

五、完整調用代碼

package com.ehfscliax;

import org.xvolks.jnative.JNative;
import org.xvolks.jnative.Type;
import org.xvolks.jnative.exceptions.NativeException;
import org.xvolks.jnative.pointers.Pointer;

public class Helper {
    public static void main(String[] args) throws NativeException {
        JNative.setLoggingEnabled(true); // 開啟調試日志

        // 1. 調用返回 const char* 的函數
        try {
            JNative getUrl = new JNative("EhfscliaxDll.dll", "getUrl");
            getUrl.setRetVal(Type.STRING);
            getUrl.setParameter(0, "127.0.0.1"); // const char*
            getUrl.setParameter(1, 10087);       // int
            getUrl.setParameter(2, 123);         // int
            getUrl.invoke();
            System.out.println("getUrl 返回: " + getUrl.getRetVal());
        } catch (IllegalAccessException e) { e.printStackTrace(); }

        // 2. 調用返回 const wchar_t* 的函數 (難點:指針+寬字符)
        try {
            JNative getPlayUrl = new JNative("EhfscliaxDll.dll", "getPlayUrl");
            getPlayUrl.setRetVal(Type.INT); // 先按指針地址 (int) 取回
            String mgrIp = "127.0.0.1";
            Pointer ptr = Pointer.createPointerFromString(mgrIp); // Java String → wchar_t* 內存塊
            getPlayUrl.setParameter(0, ptr);
            getPlayUrl.setParameter(1, 10087);
            getPlayUrl.setParameter(2, 123);
            getPlayUrl.invoke();
            
            // 從返回的指針地址讀取 Unicode 字符串
            int retPtr = getPlayUrl.getRetValAsInt();
            String url = JNative.getUnicodeMemoryAsString(retPtr);
            System.out.println("getPlayUrl 返回: " + url);
        } catch (IllegalAccessException e) { e.printStackTrace(); }
    }
}

六、運行結果與中文亂碼分析

2014-5-25 22:48:46 [DEBUG] [loadLibrary]: Successfully loaded library 'EhfscliaxDll.dll', functionName = getUrl
getUrl 返回: https://中文127.0.0.1:10087  <-- 正常 (ANSI/UTF-8 兼容)

2014-5-25 22:48:46 [DEBUG] [loadLibrary]: Successfully loaded library 'EhfscliaxDll.dll', functionName = getPlayUrl
getPlayUrl 返回: https://-17001:10087     <-- 亂碼!預期應包含中文

亂碼根因

  • DLL 返回 const wchar_t* (UTF-16LE)。
  • JNative getUnicodeMemoryAsString 內部實現可能假設平台默認編碼或固定 UTF-16,但內存佈局/終止符處理有差異。
  • JNative 年代久遠,對 Unicode 內存讀取支持不完善。

嘗試修復方向 (未在文中完全解決)

  1. 確認 DLL 導出確實是 UTF-16LE (用 Dependency Walker / VS 調試)。
  2. 手動讀取指針內存:Pointer.getMemory() 獲取字節數組,自行 new String(bytes, "UTF-16LE")。
  3. **終極建議**:遷移到 **JNA**,WideString / WString 類型原生支持寬字符,無此類坑。

七、總結與遺產代碼維護建議

  • JNative 實現了「零 C 代碼調用 DLL」,但**停維、文檔缺、Unicode 支持弱、64 位難**。
  • 核心難點在於 **指針類型映射** (Parameter/Pointer) 與 **字符串編碼協議** (ANSI vs Unicode) 的雙重匹配。
  • 若維護老項目必須用 JNative:編寫測試用例覆蓋所有 DLL 接口,鎖定當前 JDK/架構/OS 環境,避免升級導致不可用。
  • **新項目/重構請直接上 JNA**,Native.loadLibrary + 接口映射,幾行代碼搞定,支持回調、結構體、Unicode、跨平台。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
Java 類型JNative Type 常量對應 C 類型
intType.INTint / long
longType.LONGlong long / int64_t
doubleType.DOUBLEdouble
String (char*)Type.STRINGconst char*
PointerType.POINTERvoid* / wchar_t* / 結構體指針