JNA呼叫C++動態庫
阿新 • • 發佈:2019-01-24
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++的專案,其實,對於直接呼叫或者起子程序再對接流的方法,效率幾乎是沒有太大差別的,關於這個,想一想動態庫的原理就可以明白。所以在不要求絕對效率和效能的情況下,不比太過在意,只需要選擇合適的方案即可。
大概主要有兩種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
將這三個問檔案放在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++的專案,其實,對於直接呼叫或者起子程序再對接流的方法,效率幾乎是沒有太大差別的,關於這個,想一想動態庫的原理就可以明白。所以在不要求絕對效率和效能的情況下,不比太過在意,只需要選擇合適的方案即可。