博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JNA 基础篇<二> 结构体
阅读量:6915 次
发布时间:2019-06-27

本文共 4707 字,大约阅读时间需要 15 分钟。

hot3.png

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

为何在将参数传递到方法时,要先调用param.write()?

      Java 调用原生函数时,会把传递给原生函数的Java 数据固定在内存中,这样原生函数才可以访问这些Java 数据。对于没有固定住的Java 对象,GC 可以删除它,也可以移动它在内存中的位置,以使堆上的内存连续。如果原生函数访问没有被固定住的Java 对象,就会导致调用失败。Structure 类的write()方法会把结构体的所有字段固定住,使原生函数可以访问。

欢迎指出本文有误的地方,转载请注明原文出处

转载于:https://my.oschina.net/7001/blog/672427

你可能感兴趣的文章
BLOB存储图片文件二进制数据是非对错
查看>>
修改MySQL端口后重启不了
查看>>
mac 基本
查看>>
双向链表基础
查看>>
onpropertychange 和 onchange
查看>>
查看linux信息
查看>>
小程序-canvas在IOS手机层级最高无法展示问题
查看>>
ASP.NET MVC4 IN ACTION学习笔记-第六波[Ajax in ASP.NET MVC][2/3]
查看>>
最小二乘法(一维)
查看>>
Redis在windows下安装过程
查看>>
每日一“酷”之textwrap
查看>>
微信公众平台消息接口开发(3)
查看>>
javascript获取浏览器窗口大小
查看>>
第一个mvc理解程序
查看>>
编码格式简介(ANSI、GBK、GB2312、UTF-8、GB18030和 UNICODE)(转)
查看>>
CSS:兼容主流浏览器的背景颜色透明
查看>>
go安装
查看>>
ES 中的 POST 和 PUT 的区别
查看>>
Spring Boot 构建电商基础秒杀项目 (一) 项目搭建
查看>>
Systemd on ubuntu
查看>>