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.rhkb.cn/news/218480.html

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

相关文章

CSS学习笔记整理

CSS 即 层叠样式表/CSS样式表/级联样式表&#xff0c;也是标记语言&#xff0c; 用于设置HTML页面中的文本内容&#xff08;字体、大小、对齐方式等&#xff09;、图片的外形&#xff08;宽高、边框样式、边距&#xff09;以及版面的布局和外观显示样式 目录 准备工作 Chrome调…

时间序列预测 — CNN-LSTM实现多变量多步光伏预测(Tensorflow)

目录 1 数据处理 1.1 导入库文件 1.2 导入数据集 1.3 缺失值分析 2 构造训练数据 ​3 模型训练 3.1 CNN-LSTM网络 3.2 模型训练 4 模型预测 专栏链接&#xff1a;https://blog.csdn.net/qq_41921826/category_12495091.html 1 数据处理 1.1 导入库文件 import scip…

『番外篇二』Swift “黑魔法”之动态获取类实例隐藏属性的值

概览 在 Swift 代码的调试中,我们时常惊叹调试器的无所不能:对于大部分“黑盒”类实例的内容,调试器也都能探查的一清二楚。 想要自己在运行时也能轻松找到 Thread 实例“私有”属性的值吗(比如 seqNum)? 在本篇博文中您将学到如下内容: 概览1. 借我,借我,一双慧眼吧…

深入理解LightGBM

1. LightGBM简介 GBDT (Gradient Boosting Decision Tree) 是机器学习中一个长盛不衰的模型&#xff0c;其主要思想是利用弱分类器&#xff08;决策树&#xff09;迭代训练以得到最优模型&#xff0c;该模型具有训练效果好、不易过拟合等优点。GBDT不仅在工业界应用广泛&#…

IT 人员与加密程序:如何战胜病毒

&#x1f510; 加密程序是攻击者在成功攻击组织时使用最多的恶意软件类型。它们通常会发送到一个庞大的电子邮件地址数据库&#xff0c;看起来像 Word 或 Excel 文档或 PDF 文件。 想象一下&#xff0c;你是会计部门的一名员工。这种格式的文件在电子文档管理系统中被广泛使用…

如何查看Linux中glibc的Version

用ldd --version ldd --version 运行libc.so 你没有看错&#xff0c;libc.so是一个可执行程序。 但前提是你要找到它。因为它并不在PATH所包含的目录下。 ppdell:~$ ldd which cat | grep libclibc.so.6 > /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0e6fb34000)ppdell:~…

dcoker-compose一键部署EFAK —— 筑梦之路

简介 EFAK&#xff08;Eagle For Apache Kafka&#xff0c;以前称为 Kafka Eagle&#xff09;是一款由国内公司开源的Kafka集群监控系统&#xff0c;可以用来监视kafka集群的broker状态、Topic信息、IO、内存、consumer线程、偏移量等信息&#xff0c;并进行可视化图表展示。独…

跟随鼠标动态显示线上点的值(基于Qt的开源绘图控件QCustomPlot进行二次开发)

本文为转载 原文链接&#xff1a; 采用Qt快速绘制多条曲线&#xff08;折线&#xff09;&#xff0c;跟随鼠标动态显示线上点的值&#xff08;基于Qt的开源绘图控件QCustomPlot进行二次开发&#xff09; 内容如下 QCustomPlot是一个开源的基于Qt的第三方绘图库&#xff0c;能…

干货 | 一文搞定 pytest 自动化测试框架(一)

简介 pytest 是一个成熟的全功能 Python 测试工具&#xff0c;可以帮助您编写更好的程序。它与 Python 自带的 Unittest 测试框架类似&#xff0c;但 pytest 使用起来更简洁和高效&#xff0c;并且兼容 unittest 框架。pytest 有以下实用特性&#xff1a; pytest 能够支持简单…

Spring容器中scope为prototype类型Bean的回收机制

文章目录 一、背景二、AutowireCapableBeanFactory 方法 autowireBean 分析三、Spring 容器中 scope 为 prototype 类型 Bean 的回收机制四、总结 一、背景 最近做 DDD 实践时&#xff0c;遇到业务对象需要交给 Spring 管理才能做一些职责内事情。假设账号注册邮箱应用层代码流…

DDD落地:爱奇艺打赏服务,如何DDD架构?

尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格&#xff0c;遇到很多很重要的面试题&#xff1a; 谈谈你的DDD落地经验&#xff1f; 谈谈你对DDD的理解&#x…

【Kubernetes】存储类StorageClass

存储类StorageClass 一、StorageClass介绍二、安装nfs provisioner&#xff0c;用于配合存储类动态生成pv2.1、创建运行nfs-provisioner需要的sa账号2.2、对sa授权2.3、安装nfs-provisioner程序 三、创建storageclass&#xff0c;动态供给pv四、创建pvc&#xff0c;通过storage…

二百一十五、Flume——Flume拓扑结构之复制和多路复用的开发案例(亲测,附截图)

一、目的 对于Flume的复制和多路复用拓扑结构&#xff0c;进行一个小的开发测试 二、复制和多路复用拓扑结构 &#xff08;一&#xff09;结构含义 Flume 支持将事件流向一个或者多个目的地。 &#xff08;二&#xff09;结构特征 这种模式可以将相同数据复制到多个channe…

开辟“护眼绿洲”,荣耀何以为师?

文 | 智能相对论 作者 | 佘凯文 俗话说&#xff0c;眼睛是心灵的窗户&#xff0c;可如今&#xff0c;人们对于这扇“窗户”的保护&#xff0c;似乎越来越不重视。 据人民日报今年发布的调查显示&#xff0c;中国眼病患病人数2.1亿&#xff0c;近视患者人数多达6亿&#xff0…

多功能神器,强劲升级,太极2.x你值得拥有!

嗨&#xff0c;大家好&#xff0c;今天给大家分享一个好用好玩的软件。那就是太极2.x软件&#xff0c;最近在1.0版本上进行了全新升级&#xff0c;升级后的功能更强更稳定&#xff0c;轻度用户使用基本功能就已经足够了&#xff0c;我们一起来看看吧&#xff01; 首页 首页左…

input、el-input输入框输入规则

一、input 只能输入框只能输入正整数&#xff0c;输入同时禁止了以0开始的数字输入&#xff0c;防止被转化为其他进制的数值。 <!-- 不能输入零时--> <input typetext οninput"valuevalue.replace(/^(0)|[^\d]/g,)"><!-- 能输入零时--> <inp…

【SpringBoot】之Mybatis=Plus集成及使用(入门级)

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《SpringBoot开发之Mybatis-Plus系列》。&#x1…

WPF仿网易云搭建笔记(2):组件化开发

文章目录 前言专栏和Gitee仓库依赖属性实战&#xff1a;缩小&#xff0c;全屏&#xff0c;关闭按钮依赖属性操作封装主窗口传递this本身给TitleView标题控件主要代码MainWindow.xmalMainWindow.cs依赖属性方法封装TitleView.csTitleViewModelTitleViewModel实现效果 前言 这次…

有什么好用的资产设备管理系统?工单管理系统在设备管理上有什么作用?

设备管理对于企业而言是非常重要的&#xff0c;像互联网企业、医院、化工企业、制造企业等&#xff0c;都需要用到贵重设备或者仪器。这些设备仪器不仅本身造价成本高&#xff0c;还和生产活动息息相关&#xff0c;所以必须做好日常的维护管理才能确保企业生产活动正常进行。而…

计算机网络:物理层(三种数据交换方式)

今天又学到一个知识&#xff0c;加油&#xff01; 目录 前言 一、电路交换 二、报文交换 三、分组交换 1、数据报方式 2、虚电路方式 3、比较 总结 前言 为什么要进行数据交换&#xff1f; 一、电路交换 电路交换原理&#xff1a;在数据传输期间&#xff0c;源结点与…