目录

JNA技术难点

1、函数回调

2、结构体

3、指针


JNA技术难点

有过跨平台、跨语言开发的程序员都知道,跨平台、预研调用的难点,就是不同语言之间数据类型不一致造成的问题。绝大部分跨平台调用的失败都是这个问题造成的。关于这一点,不论何种语言、何种技术方案都无法解决这个问题。JNA也不列外。

上面说到接口中使用的函数必须与链接库中的函数原型保持一致,这是JNA甚至所有跨平台调用的难点,因为C/C++的类型与Java的类型是不一样的,你必须转换成java对应类型让它们保持一致,这就是类型映射(Type Mappings),JNA官方给出的默认类型映射表如下:

其中类型映射的难点在于结构体、指针和函数回调。


1、函数回调

c++代码长这样:

// 实时监视接口
VIXHZ_EXPORT long _VixHz_StartRealPlay(long lLoginID, long lChannelNO, int type, HWND hWnd, CallbackFuncRealDataEx func, unsigned long dwUserParm);
// 回调函数
typedef bool (CALLBACK * CallbackFuncRealDataEx)(    unsigned long lRealHandle, unsigned long dwDataType, unsigned char *pBuffer, unsigned long dwBufSize, int width, int height, int ra, unsigned long dwUser);

转java,在CLibrary接口中函数回调函数声明为RealDataCallbackListener接口:

     /*** 用于用户请求实时视频** @param lLoginID 登录ID* @param lChannelNO 通道号* @param type 码流类型,1-主码流,2-辅码流* @param hWnd 窗口句柄 传空指针即可* @param realDataResult 实时数据回调函数* @param dwUserParm 用户数据参数* @return 请求成功返回此次会话session,错误返回错误码(<=0) {@link NetworkErrorEnum}*/NativeLong VixHz_StartRealPlay(NativeLong lLoginID, NativeLong lChannelNO, Integer type, Pointer hWnd, RealDataCallbackListener realDataResult, String dwUserParm);

RealDataCallbackListener接口:

package com.focus.vision.device.sdk.callback;import com.sun.jna.Callback;
import com.sun.jna.NativeLong  ;
import com.sun.jna.Pointer;
import org.springframework.stereotype.Component;/*** 回调函数声明接口*/
@Component
public interface RealDataCallbackListener extends Callback {/*** 获取实时视频数据回调** @param lRealHandle 播放句柄* @param dwDataType 无实际意义* @param pBuffer 实时数据* @param dwBufSize* @param dwUser 用户数据* @return*/boolean getRealData(NativeLong lRealHandle, NativeLong dwDataType, Pointer pBuffer, NativeLong dwBufSize, String dwUser);
}

再在需要调用CLibrary.INSTANCE方法的类中,声明getVixHz_StartRealPlay:

    /*** 用户请求实时视频 回调函数** @param lLoginID 登录ID* @param lChannelNO 通道号* @param type 码流类型,1-主码流,2-辅码流* @param hWnd 窗口句柄 传空指针即可* @param realDataResult 实时数据回调函数* @param dwUserParm 用户数据参数* @return*/public static boolean getVixHz_StartRealPlay(NativeLong lLoginID, NativeLong lChannelNO, Integer type, Pointer hWnd, RealDataCallbackListener realDataResult, String dwUserParm) {NativeLong startRealPlay = CLibrary.INSTANCE.VixHz_StartRealPlay(lLoginID, lChannelNO, type, hWnd, realDataResult, dwUserParm);if (startRealPlay.intValue() > 0) {return true;}return false;}

测试实时预览,函数回调是否能够调用成功:

ackage com.focus.vision.device.sdk;import com.focus.vision.device.sdk.callback.RealDataCallbackListener;
import com.focus.vision.device.sdk.callback.TalkDataCallbackListener;
import com.focus.vision.device.sdk.enums.AlarmCallbackEnum;
import com.focus.vision.device.sdk.enums.PTZConfigTypeEnum;
import com.focus.vision.device.sdk.enums.PTZParamConfigTypeEnum;
import com.focus.vision.device.sdk.enums.RecordTypeEnum;
import com.focus.vision.device.sdk.structure.QueryStructure;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.stereotype.Component;
import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)
@SpringBootTest
@Component
public class DeviceSdkTests {private static Logger logger = LoggerFactory.getLogger(DeviceSdkTests.class);@Testpublic void testDeviceSdk() {// 初始化SDKboolean initSDKResult = CLibrary.INSTANCE.VixHz_InitSDK();if (initSDKResult) {logger.info("SDK初始化成功...");// 用户根据自己的需要设置SDK消息回调CLibrary.INSTANCE.VixHz_SetMessageCallback(AlarmCallbackEnum.CALLBACK_DEVC_DISCONNECT.getKey(), null, "0");// 测试登录设备,返回登录句柄NativeLong loginResult = testLoginDevice();if (loginResult.intValue() == -1) {return;}// 测试打开和关闭实时预览testRealPlay(loginResult);// 用户登出设备CLibrary.INSTANCE.VixHz_Logout(loginResult);}// 退出释放SDK资源CLibrary.INSTANCE.VixHz_UnInitSDK();}/*** 测试登录设备,返回登录句柄** @return*/private NativeLong testLoginDevice() {NativeLong loginResult = CLibrary.INSTANCE.VixHz_LoginDevice("172.18.30.16", 80, "admin", "123456", "{}");if (loginResult.intValue() <= 90000) {logger.error("设备注册失败...loginResult= {}", loginResult);return new NativeLong(-1);}logger.info("设备注册成功...loginResult = {}", loginResult);return loginResult;}/*** 测试打开和关闭实时预览*/private void testRealPlay(NativeLong loginResult) {boolean startRealPlay = getVixHz_StartRealPlay(loginResult, new NativeLong(0), 1, Pointer.NULL, new RealDataCallbackListener() {@Overridepublic boolean getRealData(NativeLong lRealHandle, NativeLong dwDataType, Pointer pBuffer, NativeLong dwBufSize, String dwUser) {return true;}}, "");if (!startRealPlay) {logger.error("实时预览失败...startRealPlay = {}", startRealPlay);return;}logger.info("实时预览成功...startRealPlay = {}", startRealPlay);// 关闭实时预览NativeLong stopRealPlayResult = CLibrary.INSTANCE.VixHz_StopRealPlay(loginResult, new NativeLong(100));if (stopRealPlayResult.intValue() <= 0) {logger.error("关闭实时预览失败...stopRealPlayResult = {}", stopRealPlayResult);}logger.info("关闭实时预览成功...stopRealPlayResult = {}", stopRealPlayResult);}           

运行结果:

OK,函数回调成功啦~~


2、结构体

c++代码:

// c++接口不完整,作为一个java模仿着写了个结构体,如有问题,请不吝赐教
struct Vix_QueryInfoParam
{void *bebpic;void *begtime;void *endtime;int recordtype;int source;void *channelno;int protocolType;
};

java代码,编写结构体:

package com.focus.vision.device.sdk.structure;import com.focus.vision.device.sdk.enums.RecordTypeEnum;
import com.sun.jna.Structure;
import java.util.Arrays;
import java.util.List;/*** 查询信息结构体*/
public class QueryStructure extends Structure {/*** 是否为图片查询,0-否*/public Pointer bebpic;/*** 开始时间*/public Pointer begtime;/*** 结束时间*/public Pointer endtime;/*** 录像类型  {@link RecordTypeEnum}*/public Integer recordtype;/*** 录像源(只能为2,表设备录像)*/public Integer source;/*** 通道号*/public Pointer channelno;/*** 协议类型(只能为1,表cfi协议)*/public Integer protocolType;/*** 内部类实现指针类型接口*/public static class ByReference extends QueryStructure implements Structure.ByReference{}/*** 内部类实现值类型接口*/public static class ByValue extends QueryStructure implements Structure.ByValue{}/*** 定义取值次序,需要与c++中对齐,不然报NoSuchFieldError** @return*/@Overrideprotected List<String> getFieldOrder() {return Arrays.asList(new String[] {"bebpic", "begtime", "endtime", "recordtype", "source", "channelno", "protocolType"});}}

注意:结构体定义取值次序,需要与c++中对齐,不然报NoSuchFieldError错误!

重点(此处总结也不理解什么时候用ByReference,什么时候用ByValue,,,请大佬赐教):

1.只要涉及到结构体的传递,必须使用ByReference或者ByValue中的一种

2.指针和引用的传递使用ByReference

3.拷贝参数传递使用ByValue

java代码,声明含结构体函数:

     /*** 用于用户查询视频录像,将要查询的录像信息结构体封装成xml格式的string,在回调函数中再将其解析为录像信息结构体** @param queryInfo 查询信息结构体* @param pRecQueryCallback 录像查询回调函数 * @param dwUser 用户数据 TODO 类型待确认  NativeLong = 0* @return*/NativeLong VixHz_QueryRecrodFile(QueryStructure.ByReference queryInfo, Pointer pRecQueryCallback, String dwUser);

java代码,调用结构体函数:

     /*** 测试录像查询、下载和停止、回放和停止----录像处理参数程序 */private void testRecordPlay(NativeLong loginResult) {// 查询 TODO 类型不符QueryStructure.ByReference queryStructure = new QueryStructure.ByReference();queryStructure.bebpic = "0";queryStructure.begtime = "1434816000";queryStructure.endtime = "1434902399";queryStructure.channelno = "0";queryStructure.recordtype = RecordTypeEnum.RECORD_ALARM_EXTERNAL.getKey();queryStructure.source = 2;queryStructure.ProtocolType = 1;NativeLong queryRecrodFileResult = CLibrary.INSTANCE.VixHz_QueryRecrodFile(queryStructure, Pointer.NULL, "");if (queryRecrodFileResult.intValue() <= 0) {logger.error("录像查询失败...queryRecrodFileResult = {}", queryRecrodFileResult);return;}logger.info("录像查询成功...queryRecrodFileResult = {}", queryRecrodFileResult);}

实际开发中使用ByReference一直报参数类型转换异常,最后使用ByValue接口调用成功。。。

其实我们可以看到,JNA使用的主要难点在于结构体定义和传递,只要弄清楚如何对付结构体,剩下的事情也就水到渠成了。说了这么多,其实就想告诉大家,在做跨语言调用时,尽量还是要封装一下函数、结构体,让数据传递时更为简单。


3、指针

3.1 void *var ==>Pointer

c++中此类型即可转化为Pointer,此处举一个之前的栗子吧:

CLibrary接口中声明:

int GetCcrInfo(Pointer str, Pointer out,Pointer mfr);int OpenCcr(Pointer str);int CloseCcr();

以上调用:

static CLibrary INSTANCE = (CLibrary) Native.loadLibrary("CCR_SDKx64.dll", CLibrary.class);
public static CCrInfoBean getCCR() {CCrInfoBean cCrInfoBean = new CCrInfoBean();Pointer pMac = new Memory(20);Pointer pMaxChannel = new Memory(20);Pointer pstr = new Memory(255);Pointer pmfr = new Memory(4);try {INSTANCE.OpenCcr(pstr);// 读取ccr信息INSTANCE.GetCcrInfo(pMac, pMaxChannel,pmfr);INSTANCE.CloseCcr();if (checkMac(pMac.getString(0))) { // mac地址合法cCrInfoBean.setMaxChannel(pMaxChannel.getInt(0));cCrInfoBean.setMac(pMac.getString(0));int nManufacture = pmfr.getInt(0);logger.info("Read ccr manufacture Success!"+ nManufacture);cCrInfoBean.setManufactureList(getManufactureList(nManufacture));logger.info("Read ccr Success!"+ JSON.toJSONString(cCrInfoBean));} else {cCrInfoBean.setMaxChannel(pMaxChannel.getInt(0));Set<ManufactureTypeEnum> manufactureTypeEnums = new HashSet();manufactureTypeEnums.add(ManufactureTypeEnum.IPC2_0);cCrInfoBean.setManufactureList(manufactureTypeEnums);cCrInfoBean.setMaxChannel(300);//默认给300路logger.info("Read ccr mac not match!"+ JSON.toJSONString(cCrInfoBean));}} catch (Exception e) {Set<ManufactureTypeEnum> manufactureTypeEnums = new HashSet();manufactureTypeEnums.add(ManufactureTypeEnum.IPC2_0);cCrInfoBean.setManufactureList(manufactureTypeEnums);cCrInfoBean.setMaxChannel(300);//默认给300路logger.error("Read ccr error! return a default CCR"+ JSON.toJSONString(cCrInfoBean));}return cCrInfoBean;}

这是一直在用的栗子,可直接食。

3.2 char**是二重指针,也就是指向指针变量的指针

char**==》PointerByReference

PointerByReference如何转String?

// 获取指针
Pointer value = strOutInfo.getValue();PointerByReference strLicense = new PointerByReference();
String license = strLicense.getValue().getString(0, "UTF-8");

后续想到什么继续补充吧,,,

JNA终结

JNA实战

JNA实战笔记汇总(二)——JNA和C / C ++的数据类型映射(dll函数回调、结构体、指针)相关推荐

  1. JNA实战笔记汇总一 简单认识JNA|成功调用JNA

    一.简介 先说JNI(Java Native Interface)吧,有过不同语言间通信经历的一般都知道,它允许Java代码和其他语言(尤其C/C++)写的代码进行交互,只要遵守调用约定即可.首先看下 ...

  2. JNA实战笔记汇总(一)—— JNA简介及demo环境创建

    目录 1.简介 2.原理 3.配置环境,创建demo 3.1 搞清楚.dll/.so文件适用环境 3.2 创建一个普通的maven项目 3.2.1 将.dll/.so文件放在resources根路径下 ...

  3. 神经网络与深度学习笔记汇总二

    神经网络与深度学习笔记汇总二 正交化(方便调整参数) 迭代 单实数评估指标(判断几种手段/方法哪个更好) 指标选取 训练集.开发集.测试集作用与用途 评估指标 判断算法是好是坏 迁移学习 总结 往期回 ...

  4. c语言学习笔记【结构体02】结构体指针变量与结构体变量的函数参数,C语言学习笔记结构体02结构体指针变量与结构体变量的函数参数.docx...

    C 语言学习笔记[结构体02]结构体指针变量与结构体变量 的函数参数 C 语言学习笔记之结构体指针变量一提指针,那可 是 C 语言的核心了,有多少学子曾拜倒在指针的脚下.单纯的说指针,其实并不难,但是 ...

  5. c++ jna 数据类型_JNA实战笔记汇总一 简单认识JNA|成功调用JNA

    一.简介 先说JNI(Java Native Interface)吧,有过不同语言间通信经历的一般都知道,它允许Java代码和其他语言(尤其C/C++)写的代码进行交互,只要遵守调用约定即可.首先看下 ...

  6. 转:tensorflow深度学习实战笔记(二):把训练好的模型进行固化

    原文地址:https://blog.csdn.net/chenyuping333/article/details/82106863 目录 一.导出前向传播图 二.对模型进行固化 三.pb文件转tfli ...

  7. Wireshark网络分析实战笔记(二)显示过滤器

    转自:彬彬在线 显示过滤表达式中操作符: == eq 等于 != ne 不等于 > gt 高于 < lt 低于 >= ge 不高于 <= le 不低于   contains 包 ...

  8. Golang 笔记 2 函数、结构体、接口、指针

    一.函数 Go中函数是一等(first-class)类型.我们可以把函数当作值来传递和使用.Go中的函数可以返回多个结果.   函数类型字面量由关键字func.由圆括号包裹声明列表.空格以及可以由圆括 ...

  9. PYNQ实战笔记(二)————使用HLS构建加法器

    这篇博文主要用于总结HLS构建加法器的过程与细节,最后使用PYNQ开发板进行验证.本文参考官方提供的教程.链接如下:click here http://www.tul.com.tw/ProductsP ...

最新文章

  1. java类加载的表现形式
  2. nagios总结与基本配置模板-V2
  3. flash 游戏 ui 制作方案
  4. java应用cpu使用率过高问题排查
  5. VS建lUML画类图
  6. 一看就会之—利用IIS服务发布网站(实践篇)上
  7. 贪吃蛇python语言代码_Python贪吃蛇简单的代码
  8. Linux下PS命令详解 (转)
  9. 特稿:OceanBase 连破纪录的背后,是技术人的砥砺前行 | 凌云时刻
  10. Eclipse常用插件下载
  11. 显卡硬解4k测试软件,看看GPU解码!GTX970显卡实测硬解4K视频
  12. 使用Laya引擎开发微信小游戏(上)
  13. git将master分支develop的操作
  14. 爆笑:学生假条和老师批示
  15. 谈谈四天三夜的感受【记录篇】
  16. stm32f105vct6例程_STM32F105VCT6_USB_TEST
  17. NC344 Z字形输出字符串
  18. C语言strcat库函数讲解
  19. sws_scale():bad dst image pointers
  20. 设置x轴文字换行展示

热门文章

  1. 【AD封装】DC电源接口、音频接口(带3D)
  2. 高通平台开发系列讲解(系统篇)高通平台启动流程
  3. VBA之正则表达式(1)-- 基础篇
  4. 在线API文档、技术文档工具ShowDoc
  5. linux添加windows字体文件夹里,如何把Windows下的字体添加到Linux中
  6. 握手引理_图论中的握手引理–握手定理
  7. 注解以及Java中常用注解使用
  8. 图像基本操作——图像边界填充和图像融合
  9. C-Free5.0注册码
  10. 树莓派指定挂载点挂载移动硬盘