第一部分:简单了解DLL里的函数
1、创建一个动态链接库项目testJNA
2、在头文件里声明函数
IRIS_SDK_API int STDCALL ik_release_enroll_device (IK_ENROLL_DEVICE_HANDLE dev_handle);
3、在源码里实现函数
int STDCALL ik_release_enroll_device (IK_ENROLL_DEVICE_HANDLE dev_handle) {
printf(“。。。。”);
return 1;
}
4、生成dll文件:testJNA.dll
5、将要调用的函数所在dll以及这些dll所依赖的dll都拷贝到windows/system32下(一定要将所有依赖的dll都找到,可以到网上下载个工具)
第二部分:JNA调用DLL的函数
JNA使用一个java接口来代表一个动态链接库发布的所有函数。
对于不需要的原生函数,可以不在java接口中声明java方法原型。
5、定义一个表示链接库的接口,如果dll是以stdcall方式输出函数,那么就继承StdCallLibrary。否则就继承默认的Library接口。此接口里定义一个实例
TestJnaLib INSTANCE = (TestJnaLib)Native.loadLibrary((Platform.isWindows() ? "testJNA.dll" : "c"), TestJnaLib.class);//标蓝的地方一定要这么写
此实例由jna通过反射自动生成。
此接口支持在Java端多线程、并发调用本地方法。如果本地类库不是线程安全的,可用
TestJnaLib INSTANCE2 = Native.synchronizedLibrary(INSTANCE);
来阻止多线程同时访问本地代码。
6、在表示链接库的接口里定义对应dll里的方法
int ik_release_enroll_device(Object devHandle);
7、调用本地方法
TestJnaLib.INSTANCE.ik_release_enroll_device(devHandle);
第三部分:DLL函数对JNA的回调
1、dll中定义了如下带回调函数的函数
IRIS_SDK_API int STDCALL ik_init_enroll_module (ik_ipaddress_t listened_addr,
int (__stdcall * IK_IDEN_CALLBACK_FUNC)
(IK_IDEN_DEVICE_HANDLE dev_handle, // 设备句柄
int type, // 回调类型
void *param); )
红色加粗部分是函数指针。
2、Java部分定义一个回调接口
必须继承自com.sun.jna.Callback接口 (如果回调函数是以stdcall输出,有时候可能引起jvm崩溃,可以改成继承StdCallCallback接口试试,)
子接口必须定义单个公有方法或一个名为callback的公有方法。必须持有到回调对象的一个存活引用。一个回调应该不抛出异常。
public interface FunCallBack extends StdCallCallback{
public int callback(IntByReference devHandle,int type,Pointer para);
}
callback方法里的参数顺序,类型及返回类型必须与C函数的相关类型对应上
3、定义回调接口的实现
public class CallbackFunImpl implements FunCallBack {
@Override
public synchronized int callback(IntByReference devHandle, int type, Pointer para){
//已知para是指向IkIdenresultArray类型的指针
IkIdenresultArray ikArray = new IkIdenresultArray(para);
int count = ikArray.result_count;
if(ikArray.presult == null || count <= 0){
log.info("识别结果数不大于0!");
return 0;
}
IkIdenresult[] rels = (IkIdenresult[])ikArray.presult.toArray(count);
if (rels == null || rels.length == 0) {
log.info("识别结果为空!");
return 0;
}
}
}
上文涉及的结构体的定义如下:
//识别结果列表
public static class IkIdenresultArray extends Structure{
public IkIdenresultArray(Pointer p) {
super(p);
useMemory(p);
read();
}
public IkIdenresultArray() {
super();
}
public static class ByReference extends IkIdenresultArray implements Structure.ByReference { }
public static class ByValue extends IkIdenresultArray implements Structure.ByValue{ }
public IkIdenresult.ByReference presult = new IkIdenresult.ByReference(); // 识别结果列表(表示指针)
public int result_count ; // 识别结果个数
}
4、在表示链接库实现的接口里定义要回调的本地函数
void methodWithCallback(StdCallCallback callback, int left, int right);
本地函数的函数指针用Callback 接口替代。
5、调用带函数指针的本地函数
TestJnaLib.INSTANCE.methodWithCallback(new CallbackFunImpl(), 4, 6)
最好将标红的地方定义一个static的变量引用它,否则回调函数的内存区域可能被GC回收而引起JVM崩溃,调用如下:
StdCallCallback callback = new CallbackFunImpl();
TestJnaLib.INSTANCE.methodWithCallback(callback, 4, 6)
第四部分:JAVA与C类型的对应
1、 基本类型的对应可以到网上查找对应表
2、 特殊的struct类型
1>Structure属性传递给本地字段必须是public的。
2>虽然结构体和结构体字段的名字可以是任意的,但他们应该尽可能接近本地定义。参数名及参数顺序对应一样。
3> Structure类的write()方法会把结构体的所有字段固定住,是原生函数可以访问。
4> C语言是不知道你Java的引用的。它只能访问固定的内存地址。因此,在使用JNI和JNA时,都会把Java对象锁住。GC不再管理。不删除,也不移动位置。即把传递给C的struct对象A固定住内存,如果这个A种还嵌套了struct对象,则也被固定住,但是如果嵌套的是struct的引用B,则不会固定,需要把B手动固定,如:
B.ByReference b = new B.ByReference();
b.write();
5> 结构体的定义如:
//识别结果
public static class IkIdenresult extends Structure{
//属性都定义为public类型
public IkIdenresult() {
super();
}
public IkIdenresult(Pointer p) {
super(p);
useMemory(p);
read();
}
public static class ByReference extends IkIdenresult implements Structure.ByReference { }//通过ByReference定义的是指针
public static class ByValue extends IkIdenresult implements Structure.ByValue{ }//通过ByValue定义的是对象
public IntByReference dev_handle; // 识别设备句柄
public byte []dev_sn = new byte[128];// 设备标志
public byte []iden_date_time = new byte[32]; // 识别时间
public int person_id; // personID
public byte [] person_name= new byte[128]; // 人员姓名
public int feature_id; // featureID
public int eye_flag; // 眼睛标志
}