使用JNA簡(jiǎn)單調(diào)用DLL里的函數(shù)
1、在VC下創(chuàng)建一個(gè)動(dòng)態(tài)鏈接庫(kù)項(xiàng)目testJNA
2、在頭文件里聲明函數(shù)
extern "C" _declspec(dllexport) int add(int first, int second);
紅色字體部分是必須的,包括定義結(jié)構(gòu)體時(shí)也需要。應(yīng)該是說此函數(shù)是發(fā)布的。
3、在源碼里實(shí)現(xiàn)函數(shù)
int add(int first, int second) { printf("(c) test jna : %d + %d = %d", first, second, first + second); return first + second; }
4、生成dll文件
5、定義一個(gè)表示鏈接庫(kù)的接口
接口TestJnaLib繼承自com.sun.jna.Library,此接口有一個(gè)實(shí)例?
TestJnaLib INSTANCE = (TestJnaLib)Native.loadLibrary("testJNA.dll", TestJnaLib.class);
此實(shí)例由jna通過反射自動(dòng)生成。
6、定義對(duì)應(yīng)dll里的方法
int add(int first, int second);
7、調(diào)用本地方法
TestJnaLib.INSTANCE.add(3, 5);
Jna回調(diào)Java方法:
1、在C語言部分定義帶回調(diào)函數(shù)的函數(shù)
extern "C" _declspec(dllexport) void methodWithCallback(int (*fp)(int left, int right), int left, int right);
紅色加粗部分是函數(shù)指針。
2、Java部分定義一個(gè)回調(diào)接口
必須繼承自com.sun.jna.Callback接口
public interface FunCallBack extends Callback { int invoke(int left, int right); }
Invoke方法里的參數(shù)順序與C函數(shù)的對(duì)應(yīng)
3、定義回調(diào)接口的實(shí)現(xiàn)
public class CallbackFunImpl implements FunCallBack { @Override public int invoke(int left, int right) { System.out.printf("in java :%d + %d = %d\n", left, right, left + right); return left + right; } }
4、在表示鏈接庫(kù)實(shí)現(xiàn)的接口里定義要回調(diào)的本地函數(shù)
void methodWithCallback(Callback callback, int left, int right);
本地函數(shù)的函數(shù)指針用Callback 接口替代。
5、調(diào)用帶函數(shù)指針的本地函數(shù)
TestJnaLib.INSTANCE.methodWithCallback(new CallbackFunImpl(), 4, 6)
?
1、編寫需要使用Jni的Java類文件
public class JniCall { static { System.loadLibrary("testJNA"); } public native static int add(int first, int second); public static void main(String[] args) { int first = 3; int second = 4; System.out.printf("print in java : %d + %d = %d", first, second, add(first, second)); } }
本地方法必須聲明為native。
2、編譯出class文件,用javah從class文件生成C的頭文件JniCall.h
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class JniCall */ #ifndef _Included_JniCall #define _Included_JniCall #ifdef __cplusplus extern "C" { #endif /* * Class: JniCall * Method: add * Signature: (II)I */ JNIEXPORT jint JNICALL Java_JniCall_add (JNIEnv *, jclass, jint, jint); typedef struct Student { char * name; int age; int height; }StudentObj; #ifdef __cplusplus } #endif #endif
3、在VC下建立一個(gè)動(dòng)態(tài)鏈接庫(kù)項(xiàng)目testJNA
倒數(shù)第二個(gè)。
4、把生成的JniCall.h和$JAVA_HOME/include/jni.h、$JAVA_HOME/include/win32jni_md.h拷貝到vc項(xiàng)目testJNA的目錄下
5、編寫C的本地實(shí)現(xiàn)
#include "stdafx.h" BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { return TRUE; } JNIEXPORT jint JNICALL Java_JniCall_add(JNIEnv *, jclass, jint first, jint second) { printf("print in c : %d + %d = %d \n", first, second, first + second); return first + second; }
6、構(gòu)建testJNA項(xiàng)目,生成testJNA.dll文件
7、把testJNA.dll拷貝到$JAVA_HOME/jre/bin目錄下
8、運(yùn)行Java類,調(diào)用本地方法
D:\Java\jdk1.6.0_02\bin>java JniCall
print in c??? : 3 + 4 = 7
print in java : 3 + 4 = 7
D:\Java\jdk1.6.0_02\bin>
JNA相關(guān)知識(shí):
使用JNA調(diào)用原生函數(shù)的模式:
JNA不實(shí)用native關(guān)鍵字。
JNI使用native關(guān)鍵字,使用一個(gè)個(gè)java方法來代表外部的原生函數(shù)。
JNA使用一個(gè)java接口來代表一個(gè)動(dòng)態(tài)鏈接庫(kù)發(fā)布的所有函數(shù)。
對(duì)于不需要的原生函數(shù),可以不在java接口中聲明java方法原型。
使用JNI,需要使用System.loadLibrary方法,把專門為JNI編寫的動(dòng)態(tài)鏈接庫(kù)載入進(jìn)來,這個(gè)動(dòng)態(tài)鏈接庫(kù)實(shí)際上是真正需要的動(dòng)態(tài)鏈接庫(kù)的代理。
使用JNA類庫(kù)的Native類的loadLibrary方法,是直接把需要的動(dòng)態(tài)鏈接庫(kù)載入進(jìn)來。使用JNA不需要編寫作為代理的動(dòng)態(tài)鏈接庫(kù),不需要編寫一行原生代碼。
跨平臺(tái)、跨語言調(diào)用原則:
盡量使用基本、簡(jiǎn)單的數(shù)據(jù)類型;
盡量少跨平臺(tái)、跨語言傳遞數(shù)據(jù)!
Java調(diào)用原生函數(shù)時(shí),會(huì)把數(shù)據(jù)固定在內(nèi)存中,這樣原生函數(shù)才可以訪問這些Java數(shù)據(jù)。這些數(shù)據(jù),JVM的GC不能管理,會(huì)造成內(nèi)存碎片。
C語言的結(jié)構(gòu)體是一個(gè)嚴(yán)格的規(guī)范,定義了內(nèi)存的次序。因此,JNA中模擬的結(jié)構(gòu)體的變量順序絕對(duì)不能錯(cuò)。
Java調(diào)用動(dòng)態(tài)鏈接庫(kù)中的C函數(shù),實(shí)際上就是把一段內(nèi)存作為函數(shù)的參數(shù)傳遞給C函數(shù)。動(dòng)態(tài)鏈接庫(kù)以為這個(gè)參數(shù)就是C語言傳過來的參數(shù)。
Structure類的write()方法會(huì)把結(jié)構(gòu)體的所有字段固定住,是原生函數(shù)可以訪問。
JNI技術(shù)是雙向的,既可以從Java代碼中調(diào)用原生函數(shù),也可以從原生函數(shù)中直接創(chuàng)建Java虛擬機(jī),并調(diào)用Java代碼。
原生函數(shù)可以通過函數(shù)指針實(shí)現(xiàn)函數(shù)回調(diào),調(diào)用外部函數(shù)來執(zhí)行任務(wù)。這就是策略模式。
Callback:
任務(wù)回調(diào)定義必須繼承自com.sun.jna.Callback接口,子類必須定義單個(gè)公有方法或一個(gè)名為callback的公有方法。必須持有到回調(diào)對(duì)象的一個(gè)存活引用。一個(gè)回調(diào)應(yīng)該不拋出異常。
com.sun.jna.Library:
代表本地鏈接庫(kù)的定義??捎萌缦滦问蕉x:
MyNativeLibrary INSTANCE = (MyNativeLibrary)Native.loadLibrary("mylib", MyNativeLibrary.class);
雖然結(jié)構(gòu)體和結(jié)構(gòu)體字段的名字可以是任意的,但他們應(yīng)該盡可能接近本地定義。參數(shù)名也一樣。
此接口支持在Java端多線程、并發(fā)調(diào)用本地方法。如果本地類庫(kù)不是線程安全的,可用
Native.synchronizedLibrary(com.sun.jna.Library)
來阻止多線程同時(shí)訪問本地代碼。
鏈接庫(kù)的搜索路徑:
jna.library.path 用戶定義的路徑;
Jna.platform.library.path 平臺(tái)定義的路徑,
com.sun.jna.Structure:
代表本地結(jié)構(gòu)體的對(duì)等Java類。
當(dāng)作為參數(shù)或返回值使用時(shí),這個(gè)類被傳遞給struct * (指針);當(dāng)作為另一個(gè)結(jié)構(gòu)體的字段時(shí),它被傳遞給struct (值傳遞)。
標(biāo)記接口Structure.ByReference和Structure.ByValue被用來提醒默認(rèn)的行為。
Structure屬性傳遞給本地字段必須是public的??梢杂上旅婵蛇x的修飾符:
volatile JNA不會(huì)修改字段,除非被要求,通過writeField(String);
final?? JNA會(huì)通過read()覆寫字段,字段在java這邊是不可變的。
Structure屬性的順序與類型必須與本地結(jié)構(gòu)體的字段一一對(duì)應(yīng)。
?
?
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
