JNA实现JAVA调用C/C++动态库

1.JNA

JNA全称Java Native Access,是一个建立在经典的JNI技术之上的Java开源框架(https://github.com/twall/jna)。JNA提供一组Java工具类用于在运行期动态访问系统本地库(native library:如Window的dll)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native library的函数与结构,JNA将自动实现Java接口到native function的映射。

JNA在线帮助文档:https://github.com/java-native-access/jna/blob/master/www/WindowsDevelopmentEnvironment.md
下载地址:https://github.com/java-native-access/jna/tags

2.数据类型

Java和C++的数据类型对照表

C++Java
char *String
wordshort
bytebyte
byte[]byte[]
dwordint
longNativeLong
Void *Pointer
lpvoidPointer
lpDwordIntByReference
HWNDHWND
char[]byte[]
byte *Pointer

Java和C的数据类型对照表

Java类型C类型原生表现
booleanint32位整数(可定制)
bytechar8位整数
charwchar_t平台依赖
shortshort16位整数
intint32位整数
longlong,__int6464位整数
floatfloat32位浮点数
doubledouble64位浮点数
Buffer/Pointerpointer平台依赖(32或64位指针)
没有pointer/array32或64位指针(参数/返回值)邻接内存(结构体成员)
Stringchar*/0结束的数组(nativeencodingorjna.encoding)
WStringwchar_t*/0结束的数组(unicode)
String[]char**/0结束的数组的数组
WString[]wchar_t**/0结束的宽字符数组的数组
Structurestruct*/struct指向结构体的指针(参数或返回值)(或者明确指定是结构体指针)结构体(结构体的成员)(或者明确指定是结构体)
Unionunion等同于结构体
Structure[]struct[]结构体的数组,邻接内存
Callback(*fp)()Java函数指针或原生函数指针
NativeMappedvaries依赖于定义
NativeLonglong平台依赖(32或64位整数)
PointerTypepointer和Pointer相同
int (usually)enum枚举类型

3.使用

3.1 Maven依赖

        <dependency><groupId>net.java.dev.jna</groupId><artifactId>jna</artifactId><version>5.9.0</version></dependency>

3.2 加载动态库

1.创建动态库对应的接口类

    public interface RobotApi extends Library {/**@brief 创建机器人连接实例,返回机器人连接实例指针*@param index(in):所创建的机器人连接索引,从0递增,最大为max_connection_count - 1*@return CRobotAPI* p:空类型的机器人连接实例指针*/CRobotAPI.ByReference CreateCRobotAPI(int index);/**@brief 初始化并连接机器人@param pRobot(in):指向被操作的机器人连接实例的指针*@param robot_addr(in):机器人IP地址*@return 0:连接成功,非0:错误码*/int ConnectRobot(CRobotAPI.ByReference pRobot,Memory robot_addr);/*@param pRobot(in):指向被操作的机器人连接实例的指针*@brief 断开机器人连接*/int DisconnectRobot(CRobotAPI.ByReference pRobot);/** 机器人上电*@param pRobot(in):指向被操作的机器人连接实例的指针*@return 0:上电成功,非0:错误码*/int PowerOn(CRobotAPI.ByReference pRobot);/** 机器人上电*@param pRobot(in):指向被操作的机器人连接实例的指针*@return 0:上电成功,非0:错误码*/int PowerOff(CRobotAPI.ByReference pRobot);}

注意

  • 接口名、参数、返回值要和动态文件相匹配,对应类型参考步骤二
  • 接口类要继承Library或者StdCallLibrary

3.3 接口实例化

因为想要将动态库文件放入自定义文件夹下,所以加载目录设置成自定义目录,下文有介绍
getIndex()方法为获取接口所需参数,无需关注,主要看整体使用流程

@Slf4j
@Service
public class RobotApiService {private static RobotApi instance;@Value("${manage.file.path}")private String path;@PostConstructpublic void init(){String name = path;String os = System.getProperty("os.name").toLowerCase();String arch = System.getProperty("os.arch").toLowerCase();if (os.contains("win")) {// 根据实际的 Windows 动态库名称进行替换name = path + File.separator +"RobotAPI";} else if (os.contains("mac")) {// 根据实际的 macOS 动态库名称进行替换} else if (os.contains("nix") || os.contains("nux") || os.contains("aix")) {// 根据实际的 Linux 动态库名称进行替换name = path + File.separator +"libRobotAPI.so";} else {throw new UnsupportedOperationException("不支持的操作系统: " + os + " " + arch);}instance = Native.load(name, RobotApi.class);}public CRobotAPI.ByReference createCRobotAPI(int index){return instance.CreateCRobotAPI(index);}public int powerOn(int id){return instance.PowerOn(getIndex(id));}public int powerOff(int id){return instance.PowerOff(getIndex(id));}public int connectRobot(String robotAddr,int index){CRobotAPI.ByReference reference = createCRobotAPI(index);StaticLog.info("指针地址{}",reference);return instance.ConnectRobot(reference,mem);}/*** 断开机器人连接*/public int disconnectRobot(int index){CRobotAPI.ByReference pRobot = getIndex(index);return instance.DisconnectRobot(pRobot);}private static void freeMemory(Memory mem) {long peer = Pointer.nativeValue(mem);//手动释放内存Native.free(peer);//避免Memory对象被GC时重复执行Nativ.free()方法Pointer.nativeValue(mem, 0);}
}

配置文件

#服务配置
manage:file:path: D:\workspace\jni-jna-web-master\dll

3.4 控制类

@RestController
@RequestMapping("/robot")
public class RobotController {@Resourceprivate RobotApiService apiService;/*** 连接机械臂** @return 控制指针*/@GetMapping("/connectRobot")public int connectRobot(int index) {String addr = "127.0.0.1";return apiService.connectRobot(addr,index);}/*** 断开机器人连接*/@GetMapping("/disconnectRobot")public int disconnectRobot(int index) {return apiService.disconnectRobot(index);}
}

3.5验证

启动项目
在这里插入图片描述
访问接口
在这里插入图片描述
在这里插入图片描述
成功调用动态库接口

4.注意事项

  • 动态库版本选择要和当前操作系统相匹配,windows和linux不能共用,还要注意操作系统位数,32和64也不能混用。
  • JAVA调用C++代码时需要将动态库编译成C语言格式的,否则C++会修改默认的接口名称导致JNA调用失败。
  • 在使用指针的情况下要手动释放,java是值传递的,没有指针(地址)的概念,但是c/c++是有指针的,所以Pointer是JNA中引入的类,用来表示native方法中的指针,不被JVM所管理需要手动释放。
  • Windows环境下可以通过Native.load(path,class)方法直接加载dll文件,linux环境下需要将自定义的文件目录加入到系统配置中,否则无法读取so文件。
  • 如果动态库返回结构体实例指针则需要创建对应的类去接收,下文有介绍。

5.指南

5.1 Linux下读取SO文件

原理:

JVM在载入动态库时候,会从java.library.path所指定的目录下开始查找,找不到就会报动态库缺少的错误。此外,如果动态库a.so依赖于b.so,则jvm在加载a.so之前,会先加载b.so。也就是说,如果a.so和b.so不在一个目录下,即使在加载a.so时,指定了目录,也会报动态库缺少错误。

增加动态库目录常用方法有两种

  • 修改ld.so.conf文件
    用文本编辑器打开/etc/ld.so.conf或/etc/ld.so.conf.d/下的配置文件(可能需要sudo权限)。

    sudo vim /etc/ld.so.conf

    在文件末尾添加新的动态库目录路径(每个目录一行)。

    /your/custom/library/path

    保存并关闭文件。

    运行ldconfig来更新动态链接器的缓存。

    sudo ldconfig

  • 使用LD_LIBRARY_PATH环境变量
    你可以临时地通过设置LD_LIBRARY_PATH环境变量来添加动态库目录。

    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/your/custom/library/path
    #查看当前环境变量
    echo $LD_LIBRARY_PATH

查看动态库依赖
使用ldd命令可以查看应用程序或动态库的依赖。
在这里插入图片描述
可以查看当前动态库是否缺少依赖,上图缺少GLIBCXX_3.4.21,需要安装,否则无法启动

5.2 接收动态库返回结构体指针并在其他接口中使用

1.JNA模拟结构体

C语言中的定义

class CRobotAPI{
private:unsigned int m_robot_index;char* m_server_addr;bool m_connected;unsigned int m_wobj_num;  //自定义工件坐标系个数,可取值32-200,默认200
};

在java中的模拟

@Data
public class CRobotAPI  extends Structure {public int m_robot_index;public String m_server_addr;public boolean m_connected;//自定义工件坐标系个数,可取值32-200,默认200public int m_wobj_num;// 定义值传递和指针传递类public static class ByReference extends CRobotAPI implements Structure.ByReference {//指针和引用的传递使用ByReference}public static class ByValue extends CRobotAPI implements Structure.ByValue {//拷贝参数传递使用ByValue}/*** 重写getFieldOrder获取字段列表, 很重要,没有会报错*/@Overrideprotected List<String> getFieldOrder() {return Arrays.asList("m_robot_index", "m_server_addr", "m_connected", "m_wobj_num");}
}

代码说明与使用总结

  • C、C++中的结构体成员默认是public的,无需使用public关键字修饰,而在Java的实现中必须使用public关键字修饰,否则会报成员不存在的错误。

  • 在Java中模拟C、C++的结构体时数据类型的映射必须一一对应,成员属性的名字可以不同(但一般为了方便追踪问题,建议定义成和.h头文件中一致)。

  • 在Java中定义的成员属性的顺序必须和C、C++中头文件结构体中定义的顺序一致,不能调整顺序,否则会引起异常。

  • 必须使用@Structure.FieldOrder或者重载FieldOrder方法,并返回一个字符串数组,数组中的成员是结构体的成员属性列表。

  • 关于内存对齐,如果结构体中使用了内存对齐,那么在java中也必须声明一个无参构
    造函数,并调用父类方法指定内存对齐模式,否则有可能引发内存访问异常。

  • 如果需要用到结构体指针,则需要在结构体类的实现中实现静态内部类ByReference,如代码中所示,CRobotAPI .ByReference 就等同于 CRobotAPI *

  • 如果需要用到结构体对象数组,则需要在结构体类的实现中实现静态内部类ByValue,如上述代码所示, CRobotAPI .ByValue 就等同于 CRobotAPI []

动态库返回结构体实例指针
在这里插入图片描述
需要上一步返回的结构体实例指针作为参数传递回去
在这里插入图片描述
在这里插入图片描述

实际过程中可能需要多次使用该指针,可以保存到当前线程内共多次使用。
在这里插入图片描述

5.3 调用C++编译的动态库

如果用c++实现本地方法,需要用extern ”C“来声明,这样是为了不让使用c++编译器来编译本地方法,因为c++编译器编译可能会给方法加上后缀,导致Java无法找到本地方法的实现。

extern "C" {void externC(int a ,int b){}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/226194.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

5.1 C++11强类型枚举

一、C枚举的缺陷 1.类型冲突 枚举值和类型都是全局可见的&#xff0c; 与正常C的namespace、类等都是格格不入的&#xff0c;并且还容易导致冲突。 enum Type { General, Light, Medium, Heavy }; enum Category { General, Pistol, MachineGun, Cannon }; 如果在相同作用域…

【CSS】用 CSS 写一个渐变色边框的输入框

Using_CSS_gradients MDN 多渐变色输入框&#xff0c;群友问了下&#xff0c;就试着写了下&#xff0c;看了看 css 渐变色 MDN 文档&#xff0c;其实很简单&#xff0c;代码记录下&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta ch…

uniapp实现检查版本检测,更新

1.首先需要获取当前app的版本 const systemInfo uni.getSystemInfoSync();// 应用程序版本号// #ifdef APPme.version systemInfo.appWgtVersion;// #endif// #ifdef H5me.version systemInfo.appVersion;// #endif2.在获取到服务器保存的app版本 3.点击按钮验证版本号 //…

JS的箭头函数this:

箭头函数不会创建自己的this&#xff0c;它只会从自己的作用域链的上一层沿用this。 具体看实例&#xff1a; //以前&#xff1a;谁调用的这个函数 this就指向谁// console.log(this);//window// function fn(){// console.log(this);//window 因为这个函数也是window调用…

lwIP 细节之五:accept 回调函数是何时调用的

使用 lwIP 协议栈进行 TCP 裸机编程&#xff0c;其本质就是编写协议栈指定的各种回调函数。将你的应用逻辑封装成函数&#xff0c;注册到协议栈&#xff0c;在适当的时候&#xff0c;由协议栈自动调用&#xff0c;所以称为回调。 注&#xff1a;除非特别说明&#xff0c;以下内…

2024GoCN线下活动全面启动,赠送深圳MeetUp门票

2024年GoCN社区将全面启动一系列令人期待的线下活动---不仅将在北京、上海、深圳、武汉、成都、杭州、广州、西安等地举办 meetup&#xff0c;还将在北京和上海举办 GopherChina 大会。 2024议题征集通道已开启&#xff0c;欢迎各位有实战经验、独特观点的Gopher前来分享~ 2024…

Anaconda中使用Jupyter出现’No module named ‘pymysql‘问题解决

问题截图&#xff1a; 解决办法&#xff1a; 一.找到Anaconda所在文件夹&#xff0c;文件夹处输入 cmd 进入命令控制 二. 在打开的cmd中输入‘conda install pymysql’ 三、输入y 安装完成~ 测试&#xff1a; import pandas as pd from sqlalchemy import create_engine …

EAM系统在地铁设备管理中的应用

在现代城市的交通系统中&#xff0c;地铁作为一种高效、快速、可靠的公共交通工具&#xff0c;扮演着至关重要的角色。为了确保地铁系统的正常运行和可靠性&#xff0c;地铁管理部门需要有效地管理大量的设备和设施。在这个过程中&#xff0c;企业资产管理&#xff08;EAM&…

Python个人代码随笔(观看无益,请跳过)

异常抛错&#xff1a;一般来说&#xff0c;在程序中&#xff0c;遇到异常时&#xff0c;会从这一层逐层往外抛错&#xff0c;一直抛到最外层&#xff0c;由最外层把错误显示在用户终端。 try:raise ValueError("A value error...") except ValueError:print("V…

linux源码包管理

8.2 源码包管理 source code需要经过GC,C编译环境编译才能运行 ​ 可以设定个人设置&#xff0c;但配置复杂 ​ 软件包示例: nginx-1.8.1.tar.gz -----------------------------------------------------------------------------------------------------------------------…

知识付费小程序开发:构建个性化学习平台的技术实践

随着在线学习和知识付费的兴起&#xff0c;开发一款知识付费小程序成为了创新的热点之一。本文将通过使用Node.js、Express和MongoDB为例&#xff0c;演示如何构建一个基础的知识付费小程序后端&#xff0c;并实现用户认证和知识内容管理。 1. 初始化项目 首先&#xff0c;确…

CSS复合选择器(在基础选择器上元素选择的方式不同)

后代选择器&#xff1a; ------------ 此情况下&#xff0c;红色的可以划去 子&#xff08;元素&#xff09;选择器&#xff1a; 并集选择器&#xff1a; 伪类选择器&#xff1a; 如放上字符会变色。 链接伪类选择器&#xff1a; foucus伪类选择器&#xff1a;

matplotlib如何在label中加入字符并换行【已解决】

最近在跑一个超参数的实验&#xff0c;但是发现x轴的刻度就很丑 显然&#xff0c;lr和theta在一行显得很冗余 这个是此时的label x_labels [$\t{lr0.05}\ \tθ10}$, 40, 60] 正常加换行符即可&#xff0c;但是要加上$$ x_labels [$\t{lr0.05}\ $\n$ \tθ10}$, 40, 60] 大…

听力健康“吃”出来

大多数的研究报告都指出&#xff0c;听力下降的最常见原因是年龄和噪音暴露。然而&#xff0c;近年来越来越多的文章开始探讨其他因素对听力的影响。食物不仅是维持人类基本生存的必需品&#xff0c;随着营养学的进步&#xff0c;人们也逐渐认识到食物中的营养与保持健康之间存…

zabbix6入门到精通(3) 预处理

zabbix6入门到精通&#xff08;3&#xff09; 预处理 配置 — 主机 文件系统主项目 vfs.fs.get 测试一下 添加预处理 $[?(.fsname ‘/’)] $[0].inodes.pfree JSONPath参照&#xff1a; https://www.zabbix.com/documentation/6.0/zh/manual/config/items/preprocessi…

【halcon深度学习】目标检测的数据准备过程中的一个库函数determine_dl_model_detection_param

determine_dl_model_detection_param “determine_dl_model_detection_param” 直译为 “确定深度学习模型检测参数”。 这个过程会自动针对给定数据集估算模型的某些高级参数&#xff0c;强烈建议使用这一过程来优化训练和推断性能。 过程签名 determine_dl_model_detection…

Codeforces Round 914 (Div. 2) A~E

A.Forked!&#xff08;思维&#xff09; 题意&#xff1a; 给出骑士的跳跃能力 ( x , y ) (x, y) (x,y) 以及国王和皇后的位置&#xff0c;问有多少个位置可以让骑士可以直接攻击到国王和皇后。 分析&#xff1a; 棋盘非常大 ( 1 0 8 1 0 8 ) (10^{8} \times 10^{8}) (1…

Tomcat-指定启动jdk、修改使用的jdk版本

修改tomcat配置文件setclasspath.sh 配置文件首行增加以下代码&#xff0c;指定启动的jdk&#xff1a; export JAVA_HOME/opt/softwares/jdk1.8.0_211/ export JRE_HOME/opt/softwares/jdk1.8.0_211/jre

drf入门规范

一 Web应用模式 在开发Web应用中&#xff0c;有两种应用模式&#xff1a; 1.1 前后端不分离 1.2 前后端分离 二 API接口 为了在团队内部形成共识、防止个人习惯差异引起的混乱&#xff0c;我们需要找到一种大家都觉得很好的接口实现规范&#xff0c;而且这种规范能够让后端写…

获取和移除cookie的方法

下载npm的cookie插件, 在utils.js文件中引入插件: 封装原始的Cookies.get()方法: 在xxxx.vue文件中引入方法: 使用getCookie方法获取cookie: 封装 移除cookie: export const removeCookie name>{ const options { path: /, domain: xxx.com }; Cookies.remove(name, opti…