JNA 基础篇<二> 结构体
2016-05-09 00:00
417 查看
摘要: 本节将介绍JNA如何模拟结构体,共用体,回调函数
1.JNA模拟结构体
原生函数定义:
java使用下面的方式模拟结构体:
JNA中,定义一个类型继承Structure 类,用这个类来模拟C 语言的结构体。必须注意,Structure 子类中的公共字段的顺序,必须与C 语言中的结构的顺序一致。否则会报错!因为,Java 调用动态链接库中的C 函数,实际上就是一段内存作为函数的参数传递给C函数。同时,C 语言的结构体是一个严格的规范,它定义了内存的次序。因此,JNA 中模拟的结构体的变量顺序绝对不能错。
当数据类型相同时,若交换位置,那么程序不会报错,但是数据会传递到错误的参数中。
Structure 类代表了一个原生结构体。当Structure 对象作为一个函数的参数或者返回值传递时,它代表结构体指针。当它被用在另一个结构体内部作为一个字段时,它代表结构体本身。另外,Structure 类有两个内部接口Structure.ByReference 和Structure.ByValue。这两个接口仅仅是标记,如果一个类实Structure.ByReference 接口,就表示这个类代表结构体指针。如果一个类实现Structure.ByValue 接口,就表示这个类代表结构体本身。用这两个接口的实现类,可以明确定义我们的Structure 实例表示的是结构体的指针还是结构体本身。上面的例子中,由于Structure 实例作为函数的参数使用,因此是结构体指针。
所以可直接使用param对象作为参数传递
也可以使用
明确指出param对象是结构体指针而不是结构体本身。
2.JNA模拟Union
原生代码定义:
java使用JNA模拟Union
3.JNA模拟C回调函数
这里只简单介绍如何模拟回调函数,具体细节会在下一章节再详细介绍
C回调函数定义
该回调函数的含义是,根据dwDataType的不同,pOutBuffer将指定不同的结构体对象,输出不同的结果。pInBuffer用户向回调函数中写数据。
Java模拟回调函数实现:
在C中 void *pOutBuffer表示可以指向任何类型,在运行时动态确定。因此在这里使用具体类型DevRegInfo pOutBuffer模拟void *pOutBuffer。
定义DeviceRegisterCB实现类:
到此为止,整个NativeLong startListen(CmsListenParam listenPara)方法涉及的结构体,回调函数已经定义完成,下面将参数传递到java方法调用原生函数:
为何在将参数传递到方法时,要先调用param.write()?
Java 调用原生函数时,会把传递给原生函数的Java 数据固定在内存中,这样原生函数才可以访问这些Java 数据。对于没有固定住的Java 对象,GC 可以删除它,也可以移动它在内存中的位置,以使堆上的内存连续。如果原生函数访问没有被固定住的Java 对象,就会导致调用失败。Structure 类的write()方法会把结构体的所有字段固定住,使原生函数可以访问。
欢迎指出本文有误的地方,转载请注明原文出处https://my.oschina.net/7001/blog/672427
1.JNA模拟结构体
原生函数定义:
struct{ ipaddress struAddress;//结构体中包含结构体 device_register_cb fnCB;//回调函数 void *pUser; BYTE byRes[32]; }CmsListenParam, *CmsListenParam;
struct{ char szIP[128]; WORD wPort; BYTE byRes[2]; }ipaddress, *ipaddress;
java使用下面的方式模拟结构体:
public static class CmsListenParam extends Structure{ public IpAddress struAddress; public DeviceRegisterCB fnCB; public Pointer pUser; public byte[] byRes=new byte[32]; }
public static class IpAddress extends Structure{ public byte[] szIP=new byte[128]; public short wPort; public byte[] byRes=new byte[2]; }
JNA中,定义一个类型继承Structure 类,用这个类来模拟C 语言的结构体。必须注意,Structure 子类中的公共字段的顺序,必须与C 语言中的结构的顺序一致。否则会报错!因为,Java 调用动态链接库中的C 函数,实际上就是一段内存作为函数的参数传递给C函数。同时,C 语言的结构体是一个严格的规范,它定义了内存的次序。因此,JNA 中模拟的结构体的变量顺序绝对不能错。
当数据类型相同时,若交换位置,那么程序不会报错,但是数据会传递到错误的参数中。
Structure 类代表了一个原生结构体。当Structure 对象作为一个函数的参数或者返回值传递时,它代表结构体指针。当它被用在另一个结构体内部作为一个字段时,它代表结构体本身。另外,Structure 类有两个内部接口Structure.ByReference 和Structure.ByValue。这两个接口仅仅是标记,如果一个类实Structure.ByReference 接口,就表示这个类代表结构体指针。如果一个类实现Structure.ByValue 接口,就表示这个类代表结构体本身。用这两个接口的实现类,可以明确定义我们的Structure 实例表示的是结构体的指针还是结构体本身。上面的例子中,由于Structure 实例作为函数的参数使用,因此是结构体指针。
所以可直接使用param对象作为参数传递
CmsListenParam param=new CmsListenParam();
也可以使用
jCmsListenParam param=new CmsListenParam.ByReference();
明确指出param对象是结构体指针而不是结构体本身。
2.JNA模拟Union
原生代码定义:
struct{ WORD wType; BYTE byRes[4]; union{ struct{ DWORD alarmNo; BYTE byRes[128]; }alarmRet; struct{ DWORD motNo; BYTE byRes[128]; }motionRet; struct{ DWORD chanNo; BYTE ruleID; }vcaRet; struct{ BYTE roomIndex; DWORD segmentNo; WORD segmetSize; }inquestRet; }seniorRet; }Search_Event_Ret,*Search_Event_Ret;
java使用JNA模拟Union
public static class NET_DVR_SEARCH_EVENT_RET extends Structure { public short wType; public byte[] byRes = new byte[4]; public static class SeniorRet extends Union { public static class AlarmRet extends Structure { public int alarmNo; public byte[] byRes = new byte[128]; } public static class MotionParam extends Structure { public int motRet; public byte[] byRes = new byte[128]; } public static class VcaRet extends Structure { public int chanNo; public byte ruleID; } public static class InquestRet extends Structure { public byte roomIndex; public int segmentNo; public short segmetSize; } } }
3.JNA模拟C回调函数
这里只简单介绍如何模拟回调函数,具体细节会在下一章节再详细介绍
C回调函数定义
typedef BOOL (CALLBACK *device_register_cb)( LONG iUserID, DWORD dwDataType, void *pOutBuffer,//输出参数缓冲区 DWORD dwOutLen,//输出参数大小 void *pInBuffer,//输入参数缓冲区 DWORD dwInLen,//输入参数大小 void *pUser );
该回调函数的含义是,根据dwDataType的不同,pOutBuffer将指定不同的结构体对象,输出不同的结果。pInBuffer用户向回调函数中写数据。
Java模拟回调函数实现:
public static interface DeviceRegisterCB extends StdCallCallback{ public boolean invoke(NativeLong iUserID,int dwDataType, DevRegInfo pOutBuffer,int dwOutLen, ServerInfo pInBuffer,int dwInLen,Pointer pUser); }
在C中 void *pOutBuffer表示可以指向任何类型,在运行时动态确定。因此在这里使用具体类型DevRegInfo pOutBuffer模拟void *pOutBuffer。
定义DeviceRegisterCB实现类:
public static interface DeviceRegisterCBImpl extends StdCallCallback{ public boolean invoke(NativeLong iUserID,int dwDataType, DevRegInfo pOutBuffer,int dwOutLen, ServerInfo pInBuffer,int dwInLen,Pointer pUser){ System.out.println("数据类型:"+dwDataType); //...编写具体的业务 return true; } }
到此为止,整个NativeLong startListen(CmsListenParam listenPara)方法涉及的结构体,回调函数已经定义完成,下面将参数传递到java方法调用原生函数:
CmsListenParam param=new CmsListenParam(); byte [] ip =stringToByteArray("127.0.0.1",128);//字符串转化为byte,这里长度对应结构体中参数的长度 System.arraycopy(pAlarmIP, 0, param.struIPAdress.szIP, 0, ip.length); param.struIPAdress.wPort =7200; param.fnMsgCb=new DeviceRegisterCBImpl(); param.pUserData=Pointer.NULL; param.byProtocolType=1; param.write(); //为何调用该方法? CmsServer.instance.startListen(param);
public static byte[] stringToByteArray(String str,int len){ byte b[]=new byte[len]; for(int i=0;i<str.length();i++){ b[i]=Byte.parseByte((int)str.charAt(i)+""); } return b; }
为何在将参数传递到方法时,要先调用param.write()?
Java 调用原生函数时,会把传递给原生函数的Java 数据固定在内存中,这样原生函数才可以访问这些Java 数据。对于没有固定住的Java 对象,GC 可以删除它,也可以移动它在内存中的位置,以使堆上的内存连续。如果原生函数访问没有被固定住的Java 对象,就会导致调用失败。Structure 类的write()方法会把结构体的所有字段固定住,使原生函数可以访问。
欢迎指出本文有误的地方,转载请注明原文出处https://my.oschina.net/7001/blog/672427
相关文章推荐
- java使用JNA(Java Native Access)调用dll的方法
- Java调用c++库
- Java调用C语言Dll库回调函数
- JNA异常java.lang.UnsatisfiedLinkError: Unable to load library
- JNA调用C语言动态链接库学习实践总结(指针模拟)
- JAVA中的jna
- 关于JNA、JNI的使用注意
- JNA回调线程的类加载器(ClassLoader)为空
- Android Studio里使用JNA
- JNA--JNI终结者
- JNA操作windows 剪切板并生成emf图片
- JNI的替代者—使用JNA访问Java外部函数接口
- 关于JNA调用32位和64位动态链接库,即*.dll
- 【CSDN常见问题解答】使用JNA调用Windows动态库
- JNA使用问题及解决
- JAVA调用动态链接库
- JNA调用C动态库dll、so
- LINUX 下 JNA 调用 so--正确版
- Java 调用 C/C++ 之 JNA 系列实战篇 —— 起步 (一)
- Java 调用 C/C++ 之 JNA 系列实战篇 —— 输入wchar_t* (二)