JNA 使用总结
JNA 是基于 JNI(Java Native Interface) 技术的开源工具,能够实现单方向的 Java 调用本地方法(通常是 C/C++ 编写的动态链接库中的函数),在 Windows 中是 .dll 文件,Linux 下是 .so 文件。类似于 JNA 这种跨语言调用的工具还有 JNative 和 SWING 等
JNI 和 JNA 的介绍网络上比较多,本文记录的是自己在使用 JNA 过程中遇到的一些问题及相应的解决办法。注:开发环境为 Windows,IDE 为蒸汽大脑的 IDEA
DLL 文件路径
在调用 DLL 前,我们首先要做的事情就是确认 DLL 的版本和 JDK 的版本是否一致,必须严格区分 32 位和 64 位,否则即使文件路径是正确的,也会抛出无法加载文件的错误
DLL 位数,可以通过 VS 的 CMD 命令dumpbin /headers xxx.dll
来查看
IDEA 设置
- 直接放在
src
路径下面 - 放在指定路径,然后在
Modules
的Dependencies
中添加JARs or directories
选中该文件夹路径
- 直接放在
JDK bin 目录下 JRE 的 bin 路径
这里要注意的是路径为 JDK 目录下的 JRE,如C:\Program Files (x86)\Java\jdk1.8.0_171\jre\bin
java.library.path
实际上就是要将 DLL 文件放在java.library.path
路径下,可以通过-Djava.library.path
参数在 JVM 启动时指定自定义路径,不过该方法不够方便
另外,程序启动后再修改 JVM 的该属性是无效的
获取当前应用的java.library.path
:System.getProperty("java.library.path");
参考:
使用的是 JNA 框架的话还可以通过设置
jna.library.path
,让 JNA 加载指定位置的 DLL,具体用法如下://启用 JNA 加载 DLL 文件 DEBUG 日志
System.setProperty("jna.debug_load", "true");
//根据当前的类路径设置 JNA 查找 DLL 文件路径,
String jnaLibPath = Main.class.getResource("").getPath();
jnaLibPath = jnaLibPath.substring(0, jnaLibPath.indexOf("特殊路径"))
.replaceFirst("/", "") + "dll";// 绝对路径
System.setProperty("jna.library.path", jnaLibPath);
相关的日志如下:
Looking in classpath from sun.misc.Launcher$AppClassLoader@b4aac2 for /com/sun/jna/win32-x86/jnidispatch.dll
Found library resource at jar:file:/D:/"特殊路径"/lib/jna-4.5.2.jar!/com/sun/jna/win32-x86/jnidispatch.dll
Looking for library 'HCNetSDK'
Adding paths from jna.library.path: "指定路径"
Trying "指定路径"\HCNetSDK.dll
Found library 'HCNetSDK' at D:\"路径"\HCNetSDK.dll
以上动态的设置
jna.library.path
,通过命令启动也可以设置java -Djna.library.path=<path to your library> MainClass
回调函数的使用
- 在链接库文件对应的接口中,声明回调函数的接口
回调函数的接口需要继承Callback
或者是StdCallCallback
- 定义回调方法
一般方法名称都是invoke
,这个好像是无所谓的,但是参数一定要跟提供的文档,或者头文件严格一致(对应类型和顺序) - 具体实现
一般是写一个具体类实现接口,然后重写对应的回调方法,有一点需要特别注意的是,应该将实现类的实例设置为成员变量,因为局部变量会被 GC,那么回调方法只会在开始的时候调用几次
输出参数
Java 中只有输入参数和一个返回值,当需要有多个返回时,一般都用集合或者封装成对象来返回。但是 C/C++ 不一样,有输入参数,输出参数,返回值
个人理解:输出参数功能跟返回值差不多,由函数外部定义,传入函数内部,一般都是指针
由于 Java 中是没有指针的,所以我们用 Pointer 类的实例来代替,下面是示例:
//内存不够会导致 JVM 崩溃,EXCEPTION_ACCESS_VIOLATION
Pointer onlineIpList = new Memory(1024 * 5);
int sum = PkV10AccessDll.INSTANCE.PKV10_OnlineList(onlineIpList);
String onLineStr = onlineIpList.getString(0);
logger.info("初始在线控制器数量:" + sum + " " + onLineStr);
//释放内存
long peer = Pointer.nativeValue(onlineIpList);
Native.free(peer);
//避免再次调用该方法
Pointer.nativeValue(onlineIpList, 0);
注意:为 Pointer 实例分配的内存是在 Native Heap 中的,GC 是管不了的,需要自己手动释放内存
数据类型的匹配
这是整个 JNA 使用过程中,比较难的部分
下面列举一些工作中遇到的一些问题:
char *
C/C++ 中有两种const char *
和char *
,一般使用 String 应该就可以了,但有时会报错:非法内存访问。可以试一下字节数组来解决这个问题,在获取字符串的字节数组时需要注意编码问题unsigned
数据处理
Java 中的数据类型都是有符号的,所以在传递无符号整数时,可以用范围更大一点的数据类型去接收返回值- 结构体
结构体中定义的所有字段都需要用
public
修饰
参数的顺序必须严格参照头文件或者文档定义在 JNA 4.* 以后的版本中,通过
getFieldOrder()
方法返回的字段名列表,JNA 在使用时,会计算结构体的大小,为其分配内存。在getFieldList()
方法中会通过反射来获取所有的public
修饰的字段。如果获取的结果与getFieldOrder()
方法返回结果不匹配则会抛出异常,详见 Structure 1000 行代码,JNA Version 为4.5.2
结构体作为输出参数指针
实例化一个结构体对象后,通过实例的write()
方法来为其分配内存。在调用使用该结构体的方法时,参数设置为实例的getPointer()
返回的指针。最后通过实例的read()
方法获取数据
- 数组
- 二维数组
数组是行优先的,二维数组按总长度对应成 Java 中的一维数组,示例:// public int[][] a = new int[2][3]; 错误
public int[] a = new int[2 * 3]; // 正确
- 结构体中定义结构体数组
直接看示例:public NET_DVR_SCHEDTIME[] struAlarmTime = (NET_DVR_SCHEDTIME[]) new NET_DVR_SCHEDTIME().toArray(10);
- 二维的结构体数组
结合前面说到的两种方式,示例:public NET_DVR_SCHEDTIME[] struAlarmTime = (NET_DVR_SCHEDTIME[]) new NET_DVR_SCHEDTIME().toArray(MAX_DAYS * MAX_TIMESEGMENT);
- 二维数组
参考
- JNA 提供的数据结构映射示例
- JNA 4.5.2 API
- 海康威视和大华官网提供了 SDK ,可以参考其 JNA 或 JNI 使用示例
JNA 使用总结的更多相关文章
- JNA 如何 加载多个 存在依赖的 DLL 库
JNA 的出现,极大的简化了原有的 JNI 技术.下面是JNA github地址:https://github.com/java-native-access/jna 1. 简单的一个例子: /** S ...
- 使用jna调用dll,jdk位数和dll位数的关系
最近在学习jna,发现dll文件能能否成功调用取决于jdk位数. 32位jdk只能使用32位的dll,64位jdk只能使用64位的dll,否则位数不对应的话报的错是 "Exception i ...
- JNA开发中的问题积累
[Qboy原创] 2013年12月28日 在开发一个项目过程中需要调用第三方的C的dll.由于是第一次在项目中使用JNA,很多都安装开发文档来做,但是出现了很多的问题. 由于很多接口还没调完,还不知道 ...
- JNA使用
JNA与C对应的数据类型: 注意: 使用byte[]对应C++中的char* 可以返回函数执行的结果值 一.添加JNA需要的jar包 1.jna.jar 2.plat ...
- JNA参数传递问题,Java数组
版权声明:本文为博主原创文章,未经博主允许不得转载. 本文主要讲述使用JNA模拟结构体并将结构体数组作为参数传递给对应的方法. C语言结构体定义如下: typedef struct Rect { in ...
- JNA结构体参数传递,Java数组
JNA以结构体数组为参数进行调用: ////// C++ // student 结构体定义 typedef struct { int age; char name[20]; }Student; // ...
- java 用JNA方法调用C++动态链接库
JNA(Java Native Access)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个框架.非常强大.易用,功能上类似与.NET的P/Invoke.你只 ...
- jna 使用实例,
有与项目组需要用到C++的一个模块, 需要将一个2维数组传到dll 里面 ,返回一个字符串, 恶心了1天终于完成了, 记录一下,同时也希望能给你带来帮助. java 代码如下, package tes ...
- JNA—JNI终结者
JNA—JNI终结者 介绍 给大家介绍一个最新的访问本机代码的Java框架—JNA. JNA(Java Native Access)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的J ...
- JNA入门实例
JNA(Java Native Access):建立在JNI之上的Java开源框架,SUN主导开发,用来调用C.C++代码,尤其是底层库文件(windows中叫dll文件,linux下是so[shar ...
随机推荐
- Vue与React的区别
对已经了解的内容,做几点介绍,并不全面,后期会不断更新~ React与Vue都是组件化的开发框架,整体功能类似. 一.数据处理模式(单项 or 双向): React推崇单项数据流的处理模式,数据不可以 ...
- 修改centos7容器的时间和宿主机时间一致
一.问题 centos7系统容器时间与宿主机系统时间不一致,就进去查看一番,发现时区和宿主机上的时间不一致,下面就来解决一下 二.现象 1.查看centos宿主机的时间 输入如下命令查看 # date ...
- codewars--js--Human Readable Time—Math对象,parseInt()
问题描述: Write a function, which takes a non-negative integer (seconds) as input and returns the time i ...
- Java日志介绍(3)-Logback
Logback 继承自Log4j,它建立在有十年工业经验的日志系统之上.它比其它所有的日志系统更快并且更小,包含了许多独特并且有用的特性. 1.配置 1.1.加载配置 Logback能够在初始化期间自 ...
- JavaScript之if流程控制演练,if写在区间内怎么解决
什么是编程?通俗意见上来讲,就是把人的思维与步骤通过代码的形式书写展示出来,JavaScript的流程控制包含条件判断if,switch选择,循环for while:if(表达式 条件)=>真{ ...
- MySQL锁与事务隔离级别
一.概述 1.锁的定义 锁是计算机协调多个进程或线程并发访问某一资源的机制. 在数据库中,除了传统的计算资源(如CPU.RAM.IO等)的争用以外,数据也是一种供需要用户共享的资源.如何保证数据并发访 ...
- VScode搭建OpenCV环境
用vscode来写opencv代码需要自己编译OpenCV,主要用到MinGW-w64和CMake工具.由于可能存在的版本兼容问题,下载这些工具前最好先访问网站: https://github.com ...
- opencv中的图像矩(空间矩,中心矩,归一化中心矩,Hu矩)
严格来讲矩是概率与统计中的一个概念,是随机变量的一种数字特征.设 x 为随机变量,C为常数,则量E[(x−c)^k]称为X关于C点的k阶矩.比较重要的两种情况如下: 1.c=0,这时a_k=E(X^k ...
- 【python基础语法】国庆扩展练习题
''' 一.国庆知识小拓展 1. 用户登陆程序需求: 1. 输入用户名和密码; 2. 判断用户名和密码是否正确? (name='root', password='123') 3. 为了防止暴力破解, ...
- python 复习 day1
import timeimport json # 二:嵌套取值操作students_info=[['egon',18,['play',]],['alex',18,['play','sleep']]] ...