1. 程式人生 > >JNA呼叫C++動態庫

JNA呼叫C++動態庫

   1、Java呼叫本地C/C++動態庫的方法
   大概主要有兩種JNI(Java Native Interface)和JNA(Java Native Access),最後介紹一種大招。本文沒有程式碼,只有引用人的文章,demo人家都已經寫的很清楚了,我就是總結下以備自己檢視。
   之後說下名稱粉碎(name mangling)和效能損失。

 2、 JNI(Java Native Interface)
   JNI是java本地化的原始方法,是java與本地語言之間呼叫的橋樑。注意是之間,也就是說除了Java呼叫本地語言動態庫,本地語言(不僅限於C、C++,還有諸如Delphi、VB等)同樣也可以呼叫Java。


   其實現步驟大致分四步:
I. 編寫java部分的介面程式碼,核心是宣告native的方法,靜態或例項級別均可。通過System.loadlibrary()指定需要呼叫的動態庫名稱。
II. 生成二進位制檔案.class,並根據.class用javah生成對應的.h標頭檔案。
III. 編寫原生代碼,生成動態庫。將動態庫放至path變數的路徑下,以供查詢。
IV. java完成呼叫。

 這裡說明的是javah針對的是二進位制檔案.class而不是原始碼檔案.java,執行javah時要在最頂層包的上一級目錄,輸入時要連包名一併輸入,程式會自動完成包和class的解析。

    可供參考的文章:
使用 Java Native Interface 的最佳實踐(JNI)

JNI相關引數傳遞
陣列
呼叫執行時異常
找不到jni.h的解決辦法(fatal error C1083: Cannot open include file: 'jni.h':)

    關於一些常見的異常,尤其是C++庫用mingw和cygwin的g++編譯出來,可能會出現java.lang.UnsatisfiedLinkError: XXXclass.XXXmethod()這個錯誤,VC則不會出現這個問題。這是由於編譯時的一個奇怪的名稱粉碎現象導致的,g++會在方法名前新增“@”符號,導致java呼叫失敗,解決方法:
    生成不帶@的函式宣告
 :
    gcc -Wl,--kill-at -shared -o jnihello.dll Native.c

    
關於找不到jni.h的問題,他們在jdk的include目錄下,包括jni.h、 win32\jni_md.h 、win32\jawt_md.h
    將這三個問檔案放在Visual Studio.net的安裝目錄下的\Vc xxx \include 下,
    mingw放在安裝目錄下的include目錄中就可以,反正跟stdlib.h這樣常用的庫在一起就沒問題了。

  3、JNA(Java Native Access) Sun官方的java本地訪問專案(http://jna.java.net/),是一個庫,用它可以大大簡化java呼叫本地庫的過程。這個東西有點像windows的loadlibrary和linux下的dlopen,都是動態裝在庫,可以在執行時根據需要裝在符合介面的庫。

    所以,根據描述,使用方法如下:
I.編寫符合函式原型的介面(interface),並根據呼叫方式選擇是擴充套件StdCallLibrary介面(stdcall)還是Library介面(其他方式)。
II.編寫動態庫,並放至path下。C++請使用 extern   "C"   __declspec( dllexport ) 作為匯出函式字首,以免編譯器的名稱粉碎導致java呼叫不能。*
III. 通過java的jna庫的Native.loadlibrary()方法裝載動態庫。

   可供參考的文章: 
          JNA介紹
          還是JNA的介紹和Demo
          開源專案JNA-中文翻譯版

    比JNI簡單一些,效率損失根據實際情況也可以忽略.
    多說一句,這樣編出來的C++動態庫也可以在c#中被順利呼叫。C#通過[DllImport]特性呼叫C++庫時,需按照第 II 條規則編寫程式碼。
    4、跨語言呼叫大招你還想要大招?告訴你一個可別打我,就是建立子程序,然後對接子程序輸出流,入參通過建立程序時的附加引數傳入,返回結果自然就是本應該在命令列上出現的字串了。
    其實就是編了個可執行的命令列,然後重定向了流,用途廣泛。

5、名稱粉碎(Name Mangling)
    叫名字粉碎、名字改編也一樣,主要是面嚮物件語言對應過載的機制。
    如果用記事本開啟C++的匯入庫檔案.o/.lib,以字元而非二進位制方式檢視,就可以找到我們的匯出函式的名稱,也可以發現其粉碎規則,名稱粉碎對於不同的編譯器方式不同,會干擾跨語言呼叫。

    可以參考的文章:
Visual C++名字修飾
Name mangling

6、效能損失
 最後,java損失跨平臺性呼叫本地庫換來了功能的強大,但是效能損失多少,以及在java和c++上都能做的事情是交給誰做,頻繁呼叫本地方法好不好,都是之後有時間需要測試的。
 [補充於2012年8月]
 最近又回來做一些C#呼叫C++的專案,其實,對於直接呼叫或者起子程序再對接流的方法,效率幾乎是沒有太大差別的,關於這個,想一想動態庫的原理就可以明白。所以在不要求絕對效率和效能的情況下,不比太過在意,只需要選擇合適的方案即可。