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

news2024/9/24 15:26:09

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;
    
    @PostConstruct
    public 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 {
    @Resource
    private 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,默认200
    public 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获取字段列表, 很重要,没有会报错
     */
    @Override
    protected 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.coloradmin.cn/o/1315433.html

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

相关文章

【C++11特性篇】盘点C++11中三种简化声明的方式【auto】【decltype】【nullptr】(3)

前言 大家好吖&#xff0c;欢迎来到 YY 滴C系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; 目录 一.auto&#xff06;范围for二.decltyp…

flume:Ncat: Connection refused.

一&#xff1a;nc -lk 44444 和 nc localhost 44444区别 nc -lk 44444 和 nc localhost 44444 是使用 nc 命令进行网络通信时的两种不同方式。 1. nc -lk 44444&#xff1a; - 这个命令表示在本地监听指定端口&#xff08;44444&#xff09;并接受传入的连接。 - -l 选项…

使用Redis构建简单的社交网站

文章目录 第1关&#xff1a;创建用户与动态第2关&#xff1a;处理用户关系第3关&#xff1a;状态与信息流 第1关&#xff1a;创建用户与动态 编程要求 在Begin-End区域编写 create_user(login_name, real_name) 函数&#xff0c;实现创建新用户的功能&#xff0c;具体参数与要…

添加E1000网卡进行测试,只有VMXNET3性能的四分之一

正文共&#xff1a;1444 字 14 图&#xff0c;预估阅读时间&#xff1a;2 分钟 我们前面介绍了VMware ESXi 6.7中的适配器类型性能&#xff08;VMWare ESXi中&#xff0c;不同的虚拟网卡性能竟然能相差三倍&#xff01;&#xff09;&#xff0c;当时的配置项主要为E1000e和VMXN…

格式化Echarts的X轴显示,设置显示间隔

业务需求&#xff1a;x轴间隔4个显示&#xff0c;并且末尾显示23时 x轴为写死的0时-23时&#xff0c;使用Array.from data: Array.from({ length: 24 }).map((_, i) > ${i}时) 需要在axisLabel 里使用 interval: 0, // 强制显示所有刻度标签&#xff0c;然后通过 formatter …

Java集合(六)Hashtable、ConcurrentHashMap

文章目录 Hashtable一、Hashtable介绍1.1 Hashtable是什么1.2 Hashtable特点1.3 Hashtable常见方法 二、Hashtable源码分析2.1 节点2.2 构造方法2.3 获取元素2.4 存入元素2.5 判断是否包含某个key/value2.6 替换元素2.7 删除元素2.8 获取value集合2.9 哈希2.10 扩容 Concurrent…

ADUM1200ARZ数字隔离器:重新定义技术标准

ADUM1200ARZ数字隔离器成为技术进步领域的关键组件。其创新设计和多方面功能重新定义了数字隔离技术的格局&#xff0c;提供了满足不同工业需求的众多功能。让我们通过本文直观的了解ADUM1200ARZ的功能与技术标准。 窄体且符合ROHS&#xff1a;设定新基准 该数字隔离器采用窄体…

HNU-数据库系统-实验2-数据库安全性/完整性定义与检查

数据库系统 课程实验2数据库安全性/完整性定义与检查 计科210X 甘晴void 202108010XXX 目录 文章目录 数据库系统 课程实验2<br>数据库安全性/完整性定义与检查实验目的实验环境实验内容2.1数据库安全性2.1.1 自主存取控制实验1&#xff09;实验内容与要求2&#xff0…

verilog语法进阶-分布式ram原语

概述 官方提供的原语 RAM16X1S_1 #(.INIT(16h0000) // Initial contents of RAM) RAM16X1S_1_inst (.O(O), // RAM output.A0(A0), // RAM address[0] input.A1(A1), // RAM address[1] input.A2(A2), // RAM address[2] input.A3(A3), // RAM address[3…

数据结构-迷宫问题

文章目录 1、题目描述2、题目分析3、代码实现 1、题目描述 题目链接&#xff1a;迷宫问题 、 注意不能斜着走&#xff01; 2、题目分析 &#xff08;1&#xff09;0为可以走&#xff0c;1不能走且只有唯一一条通路 &#xff08;2&#xff09;我们可以通过判断上下左右来确定…

基于ssm电子资源管理系统源码和论文

idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 环境&#xff1a; jdk8 tomcat8.5 基于ssm电子资源管理系统源码和论文758 摘要 随着互联网技术的高速发展&#xff0c;人们生活的各方面都受到互联网技术的影响。现在人们可以通过互联网技术就能实现不出家门…

微信公众号怎样使用文章模版?怎样建立文章模版 公众号模版示例 如何使用

打开我们的微信公众平台并登陆&#xff0c;找到我们的管理->素材管理 如图所示 2 在新建图文素材的右边有三个点&#xff0c;我们把鼠标移动到此处并点击 我们会看到新建分享图文和管理图文模版 选中并点击管理图文模版 如图所示 3 我们会看到如下界面 如图所示 4 …

【C++入门到精通】 线程库 | thread类 C++11 [ C++入门 ]

阅读导航 引言一、thread类的简单介绍二、线程函数详细介绍1. start() 函数&#xff08;1&#xff09;头文件&#xff08;2&#xff09;函数原型 2. join() 函数&#xff08;1&#xff09;头文件&#xff08;2&#xff09;函数原型 3. detach() 函数&#xff08;1&#xff09;头…

列表优先于数组

在Java中&#xff0c;列表&#xff08;List&#xff09;通常优于数组&#xff0c;因为列表提供了更灵活的操作和动态调整大小的能力。下面是一个例子&#xff0c;展示了为什么在某些情况下使用列表比数组更好&#xff1a; import java.util.ArrayList; import java.util.List;…

交友网站的设计与实现(源码+数据库+论文+开题报告+说明文档)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

1125矩阵乘法(C语言)

一&#xff1a;题目 二&#xff1a;思路分析 1.对于学习过线性代数的人来说&#xff0c;对应公式十分熟悉&#xff0c;而对于没接触过线性代数的人来说&#xff0c;我们地一步要做的是从题目所给的公式中找规律 2.这个题目&#xff0c;给了我们三个变量n&#xff0c;m&#xf…

图扑物联 | WEB组态可视化软件

什么是组态&#xff1f; 组态的概念来自于20世纪70年代中期出现的第一代集散控制系统&#xff08;Distributed Control System&#xff09;&#xff0c;可理解为“配置”、“设置”等&#xff0c;是指通过人机开发界面&#xff0c;用类似“搭积木”的简单方式来搭建软件功能&a…

运营微信视频号要注意哪些问题?

视频号运营的5个雷点你别踩! 今天和你说的视频号运营的5大雷点 你踩过没? 这5点虽然和野花似的 但也不能踩哦 雷点1:违规行为 雷点2:抄袭剽窃 雷点3:没有明确目标受众 雷点4:短视频质量过低 雷点5:缺少社交互动 相信不管是视频号还是别的平台都通用哈

QML中Image动态显示图片内容

1.定义一个ColorImageProvider类 #ifndef COLORIMAGEPROVIDER_H #define COLORIMAGEPROVIDER_H#include <QObject> #include <QImage> #include <QQuickImageProvider>#include <QTimer>class ColorImageProvider :public QObject, public QQuickImag…

26.Java安卓程序设计-基于SSM框架Android的网店系统设计与实现

1. 引言 1.1 背景 介绍网店系统的背景&#xff0c;说明为什么设计这个系统以及系统的重要性。 1.2 研究目的 阐述设计基于SSM框架的Android网店系统的目标和意义。 2. 需求分析 2.1 行业背景 分析网店行业的特点和需求&#xff0c;以及目前市场上同类系统的不足之处。 …