日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java中通过JNA调用dll

發(fā)布時(shí)間:2024/9/5 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java中通过JNA调用dll 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

---恢復(fù)內(nèi)容開始---

1. JNA簡(jiǎn)單介紹

先說JNI(Java Native Interface)吧,有過不同語言間通信經(jīng)歷的一般都知道,它允許Java代碼和其他語言(尤其C/C++)寫的代碼進(jìn)行交互,只要遵守調(diào)用約定即可。首先看下JNI調(diào)用C/C++的過程,注意寫程序時(shí)自下而上,調(diào)用時(shí)自上而下。

?

可見步驟非常的多,很麻煩,使用JNI調(diào)用.dll/.so共享庫都能體會(huì)到這個(gè)痛苦的過程。如果已有一個(gè)編譯好的.dll/.so文件,如果使用JNI技 術(shù)調(diào)用,我們首先需要使用C語言另外寫一個(gè).dll/.so共享庫,使用SUN規(guī)定的數(shù)據(jù)結(jié)構(gòu)替代C語言的數(shù)據(jù)結(jié)構(gòu),調(diào)用已有的?dll/so中公布的函 數(shù)。然后再在Java中載入這個(gè)庫dll/so,最后編寫Java??native函數(shù)作為鏈接庫中函數(shù)的代理。經(jīng)過這些繁瑣的步驟才能在Java中調(diào)用 本地代碼。因此,很少有Java程序員愿意編寫調(diào)用dll/.so庫中原生函數(shù)的java程序。這也使Java語言在客戶端上乏善可陳,可以說JNI是 Java的一大弱點(diǎn)!

那么JNA是什么呢?

JNA(Java Native Access)是一個(gè)開源的Java框架,是Sun公司推出的一種調(diào)用本地方法的技術(shù),是建立在經(jīng)典的JNI基礎(chǔ)之上的一個(gè)框架。之所以說它是JNI的替 代者,是因?yàn)镴NA大大簡(jiǎn)化了調(diào)用本地方法的過程,使用很方便,基本上不需要脫離Java環(huán)境就可以完成。

如果要和上圖做個(gè)比較,那么JNA調(diào)用C/C++的過程大致如下:

?

可以看到步驟減少了很多,最重要的是我們不需要重寫我們的動(dòng)態(tài)鏈接庫文件,而是有直接調(diào)用的API,大大簡(jiǎn)化了我們的工作量。

JNA只需要我們寫Java代碼而不用寫JNI或本地代碼。功能相對(duì)于Windows的Platform/Invoke和Python的ctypes。

?

2. JNA技術(shù)原理

JNA使用一個(gè)小型的JNI庫插樁程序來動(dòng)態(tài)調(diào)用本地代碼。開發(fā)者使用Java接口描述目標(biāo)本地庫的功能和結(jié)構(gòu),這使得它很容易利用本機(jī)平臺(tái)的功能,而不會(huì)產(chǎn)生多平臺(tái)配置和生成JNI代碼的高開銷。這樣的性能、準(zhǔn)確性和易用性顯然受到很大的重視。

此外,JNA包括一個(gè)已與許多本地函數(shù)映射的平臺(tái)庫,以及一組簡(jiǎn)化本地訪問的公用接口。

?

注意:

JNA是建立在JNI技術(shù)基礎(chǔ)之上的一個(gè)Java類庫,它使您可以方便地使用java直接訪問動(dòng)態(tài)鏈接庫中的函數(shù)。

原來使用JNI,你必須手工用C寫一個(gè)動(dòng)態(tài)鏈接庫,在C語言中映射Java的數(shù)據(jù)類型。

JNA中,它提供了一個(gè)動(dòng)態(tài)的C語言編寫的轉(zhuǎn)發(fā)器,可以自動(dòng)實(shí)現(xiàn)Java和C的數(shù)據(jù)類型映射,你不再需要編寫C動(dòng)態(tài)鏈接庫。

也許這也意味著,使用JNA技術(shù)比使用JNI技術(shù)調(diào)用動(dòng)態(tài)鏈接庫會(huì)有些微的性能損失。但總體影響不大,因?yàn)镴NA也避免了JNI的一些平臺(tái)配置的開銷。

?

3. JNA簡(jiǎn)單使用

JNA的項(xiàng)目已遷移至Github,目前最新版本是4.1.0,已有打包好的jar文件可供下載。

JNA把一個(gè).dll/.so文件看做是一個(gè)Java接口,下面以一個(gè)簡(jiǎn)單的實(shí)例來說明怎么使用。

當(dāng)然要從最經(jīng)典的HelloWorld開始,我們調(diào)用C的printf函數(shù)打印出“HelloWorld”(官方的例子)

?

新建java project

然后finish

新建文件HelloWorld.java

在項(xiàng)目下創(chuàng)建lib文件夾,將jna.jar放入其中

在項(xiàng)目Properties->java Build Path->Add External JARs 中添加jna.jar

然后OK

編輯HelloWorld.java,并運(yùn)行,結(jié)果如下:

?

運(yùn)行程序,如果沒有帶參數(shù)則只打印出“Hello, World Hello jna!”,如果帶了參數(shù),則會(huì)打印出所有的參數(shù)。

?

下面來解釋下這個(gè)程序。

(1)需要定義一個(gè)接口,繼承自Library 或StdCallLibrary

默認(rèn)的是繼承Library ,如果動(dòng)態(tài)鏈接庫里的函數(shù)是以stdcall方式輸出的,那么就繼承StdCallLibrary,比如眾所周知的kernel32庫。比如上例中的接口定義:

public interface CLibrary extends Library { }

?

(2)接口內(nèi)部定義

接口內(nèi)部需要一個(gè)公共靜態(tài)常量:INSTANCE,通過這個(gè)常量,就可以獲得這個(gè)接口的實(shí)例,從而使用接口的方法,也就是調(diào)用外部dll/so的函數(shù)。

該常量通過Native.loadLibrary()這個(gè)API函數(shù)獲得,該函數(shù)有2個(gè)參數(shù):

  • 第 一個(gè)參數(shù)是動(dòng)態(tài)鏈接庫dll/so的名稱,但不帶.dll或.so這樣的后綴,這符合JNI的規(guī)范,因?yàn)閹Я撕缶Y名就不可以跨操作系統(tǒng)平臺(tái)了。搜索動(dòng)態(tài)鏈 接庫路徑的順序是:先從當(dāng)前類的當(dāng)前文件夾找,如果沒有找到,再在工程當(dāng)前文件夾下面找win32/win64文件夾,找到后搜索對(duì)應(yīng)的dll文件,如果 找不到再到WINDOWS下面去搜索,再找不到就會(huì)拋異常了。比如上例中printf函數(shù)在Windows平臺(tái)下所在的dll庫名稱是msvcrt,而在 其它平臺(tái)如Linux下的so庫名稱是c。
  • 第二個(gè)參數(shù)是本接口的Class類型。JNA通過這個(gè)Class類型,根據(jù)指定的.dll/.so文件,動(dòng)態(tài)創(chuàng)建接口的實(shí)例。該實(shí)例由JNA通過反射自動(dòng)生成。

CLibrary INSTANCE = (CLibrary) Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class);

接口中只需要定義你要用到的函數(shù)或者公共變量,不需要的可以不定義,如上例只定義printf函數(shù):

void printf(String format, Object... args);

注意參數(shù)和返回值的類型,應(yīng)該和鏈接庫中的函數(shù)類型保持一致。

?

(3)調(diào)用鏈接庫中的函數(shù)

定義好接口后,就可以使用接口中的函數(shù)即相應(yīng)dll/so中的函數(shù)了,前面說過調(diào)用方法就是通過接口中的實(shí)例進(jìn)行調(diào)用,非常簡(jiǎn)單,如上例中:

?

CLibrary.INSTANCE.printf("Hello, World\n"); for (int i=0;i < args.length;i++) { CLibrary.INSTANCE.printf("Argument %d: %s\n", i, args[i]); }

這就是JNA使用的簡(jiǎn)單例子,可能有人認(rèn)為這個(gè)例子太簡(jiǎn)單了,因?yàn)槭褂玫氖窍到y(tǒng)自帶的動(dòng)態(tài)鏈接庫,應(yīng)該還給出一個(gè)自己實(shí)現(xiàn)的庫函數(shù)例子。其實(shí)我覺得這個(gè)完全沒有必要,這也是JNA的方便之處,不像JNI使用用戶自定義庫時(shí)還得定義一大堆配置信息,對(duì)于JNA來說,使用用戶自定義庫與使用系統(tǒng)自帶的庫是完全一樣的方法,不需要額外配置什么信息。比如我在Windows下建立一個(gè)動(dòng)態(tài)庫程序:

?

用vs創(chuàng)建DLL工程:

??????文件->新建->項(xiàng)目->visual c++->win32->win32控制臺(tái)應(yīng)用程序(win32項(xiàng)目也可以)

??????填寫項(xiàng)目名稱MyDLL->確定->下一步->DLL(附加選項(xiàng) 對(duì)空項(xiàng)目打鉤)->完成。

??????到這里DLL工程就創(chuàng)建完畢了,下面新建兩個(gè)文件MyDLL.cpp和MyDLL.h。

??????MyDLL.cpp內(nèi)容如下:

然后Bulid -->Bulid MyDLL,dll文件就在debug文件夾下生成了編譯成一個(gè)dll文件(比如Mydll.dll),將Mydll.dll放到工程的bin目錄下,然后編寫JNA程序調(diào)用即可:

然后修改代碼并運(yùn)行如下:

得到輸出。

?

4. JNA技術(shù)難點(diǎn)

有過跨語言、跨平臺(tái)開發(fā)的程序員都知道,跨平臺(tái)、語言調(diào)用的難點(diǎn),就是不同語言之間數(shù)據(jù)類型不一致造成的問題。絕大部分跨平臺(tái)調(diào)用的失敗,都是這個(gè)問題造成的。關(guān)于這一點(diǎn),不論何種語言,何種技術(shù)方案,都無法解決這個(gè)問題。JNA也不例外。

上面說到接口中使用的函數(shù)必須與鏈接庫中的函數(shù)原型保持一致,這是JNA甚至所有跨平臺(tái)調(diào)用的難點(diǎn),因?yàn)镃/C++的類型與Java的類型是不一樣的,你必須轉(zhuǎn)換類型讓它們保持一致,比如printf函數(shù)在C中的原型為:

void printf(const char *format, [argument]);

你不可能在Java中也這么寫,Java中是沒有char *指針類型的,因此const char *轉(zhuǎn)到Java下就是String類型了。

這就是類型映射(Type Mappings),JNA官方給出的默認(rèn)類型映射表如下:

還有很多其它的類型映射,需要的請(qǐng)到JNA官網(wǎng)查看。

另外,JNA還支持類型映射定制,比如有的Java中可能找不到對(duì)應(yīng)的類型(在Windows API中可能會(huì)有很多類型,在Java中找不到其對(duì)應(yīng)的類型),JNA中TypeMapper類和相關(guān)的接口就提供了這樣的功能。

?

5. JNA能完全替代JNI嗎?

這可能是大家比較關(guān)心的問題,但是遺憾的是,JNA是不能完全替代JNI的,因?yàn)橛行┬枨筮€是必須求助于JNI。

使用JNI技術(shù),不僅可以實(shí)現(xiàn)Java訪問C函數(shù),也可以實(shí)現(xiàn)C語言調(diào)用Java代碼。

而JNA只能實(shí)現(xiàn)Java訪問C函數(shù),作為一個(gè)Java框架,自然不能實(shí)現(xiàn)C語言調(diào)用Java代碼。此時(shí),你還是需要使用JNI技術(shù)。

JNI是JNA的基礎(chǔ),是Java和C互操作的技術(shù)基礎(chǔ)。有時(shí)候,你必須回歸到基礎(chǔ)上來。

?

6.? 參考文獻(xiàn)

(1)JNI的替代者—使用JNA訪問Java外部功能接口

---恢復(fù)內(nèi)容結(jié)束---

轉(zhuǎn)載于:https://www.cnblogs.com/anywell/p/10242635.html

總結(jié)

以上是生活随笔為你收集整理的java中通过JNA调用dll的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。