JNA 实际开发中若干问题解决方法(二)

简介:

jna_title

1. JNA 中 byte [] 类型映射问题

在之前的文章中,我们知道 JNA 中,char * 和 char 类型都可以映射为 byte[] 类型, 通常来说也就是 byte[] 具有通用性。
在实际开发中,我们通常使用 "string".toBytes() 来进行转化。如下示例:

// 代码 1-1 ,某驱动函数原型
int NewKey(char *room,char *gate,char *stime,char *guestname,char *guestid, char *lift, int  overflag, int Breakfast, long *cardno,char * track1,char * track2);

按照之前的映射为 Java 方法,

// 代码 1-2 , 映射为 Java 方法

int NewKey(byte [] room,byte [] gate,byte [] stime,byte [] guestname,byte [] guestid, byte [] lift, int  overflag, int Breakfast, NativeLongByReference cardno,byte []  track1,byte []  track2);

此时,如果我们调用的参数如下,

// 代码 1-3 ,NewKey 方法调用

int result = NewKey("010101".getBytes(),"00","201810231200201810241200".getBytes(), "".getBytes(),"".getBytes(),1, nativeLongByReference, null,null);

看上去是没有问题的, 我们满怀信心的调用,却发现调用始终失败。于是我们对调用的 dll 动态链接库再用 C 封装一层,用 java 代码间接调用原始 dll。 打印日志发现,调用原始dll的入参如下,

// 代码 1-4 , 调用 原始 dll 入参日志
Adell_NewKey (010101,00,2018102312002018102412002c� � ,,,1, nullptr ,nullptr)

很明显的发现,在某个参数后面多了一串乱码导致调用失败。此时,我们可以用一个蹩脚的方法暂时先解决,那就是把 byte [] 类型改为 String, 如下所示,

// 代码 1-4 ,参数从 byte[] 改为 String后,从 dll 入参日志
// java method invoke
NewKey("010101".getBytes(),"00","201810231200201810241200", "".getBytes(),"".getBytes(),1, nativeLongByReference, null,null);

// parameters log from dll method signature 
NewKey (010101,00,201810231200201810241200,,,1, nullptr ,nullptr)

问题似乎这样简单的解决的了, 但我们还没明白为什么使用 jdk 中的原生 toBytes() 会在结尾加上其他字节?只能求助社区。

2. 真相大白

在 Issues on byte [] when invoke by JNA 这个帖子中, 感谢 JNA 的作者 Matthias Bläsing 耐心回答。

在 C 中的 char 数组是以一个 NULL 字符作为结束中止标识, 使用 jdk 中的 toBytes() 并不会在byte 数组后加结束符,导致读取参数数组越界,强制结束。所以在getBytes()转为 byte 数组后我们需要
人为在结尾加上0来作为中止字符,当然 Matthias Bläsing 也给出了 JNA 解决方法 , Native#toByteArray 做了部分重写。

我们查看下 JNA 对转 byte 数组是怎么处理的,

// 代码 1-5 , Native#toByteArray 源码
 /**
     * @param s The string
     * @return A NUL-terminated byte buffer equivalent to the given String,
     * using the given encoding.
     * @see #getBytes(String, String)
     */
    public static byte[] toByteArray(String s, String encoding) {
   
        byte[] bytes = getBytes(s, encoding);
        byte[] buf = new byte[bytes.length+1];
        System.arraycopy(bytes, 0, buf, 0, bytes.length);
        return buf;
    }

拷贝 byte 数组,并且在 byte 数组结尾加上 0 作为终止符。

3. char ** 映射

char ** 指针引用映射 JNA 类型为 StringArray , 而不是 String 。

4. 一些建议

通常情况下 ,我们的 dll 动态链接库文件都放在 resources 资源目录下, 当然也建议放在项目的根目录下。如下文件目录结构,

根目录

|
│  external.lib
│  pom.xml
└─src
    ├─main
    │  ├─java
    │  │    └─InvokerDLL
    │  │          InvokerDLL.java
    │  │
    │  └─resources
    │          external.dll

5. 参考

  1. Issues on byte [] when invoke by JNA
目录
相关文章
|
缓存 JSON 前端开发
CORS 详解,终于不用担心跨域问题了
CORS 详解,终于不用担心跨域问题了
9776 1
CORS 详解,终于不用担心跨域问题了
|
XML C# 数据格式
掌握了在Windows平台上查看DLL依赖的方法
掌握了在Windows平台上查看DLL依赖的方法
4578 4
|
存储 SQL Java
Springboot Mybatis 、JPA 调用存储过程,实战教程
Springboot Mybatis 、JPA 调用存储过程,实战教程
2147 0
Springboot Mybatis 、JPA 调用存储过程,实战教程
|
网络协议 域名解析 网络架构
DNS详解: A记录,子域名,CNAME别名,PTR,MX,TXT,SRV,TTL
DNS DNS,Domain Name System或者Domain Name Service(域名系统或者域名服务)。域名系统为Internet上的主机分配域名地址和IP地址。由于网络中的计算机都必须有个IP地址,来识别, 互相之间才能通信,但让我们记住一大串的IP地址来访问网站显然是不可能的,所以用户使用域名地址,而DNS系统的功能就是自动把域名地址翻译为IP地址。
21564 0
|
安全 Java 持续交付
如何实现上传jar直接部署成功,这篇文章直接带你上手springboot实现jar包热更新!
本文详细讲解了在Spring Boot应用中实现Jar包热更新的实践方法。通过自定义类加载器(`HotClassLoader`),动态加载和卸载指定目录下的Jar包,结合Spring Bean动态注册机制,使新加载的类能够被Spring容器管理。同时,提供了文件上传接口,方便用户手动触发Jar包更新。文章还强调了安全性、依赖管理和线程安全等注意事项,并给出了测试步骤和总结,帮助开发者高效实现热更新功能,减少服务中断和提升开发效率。
|
8月前
|
人工智能 数据可视化 机器人
Qclaw:你的本地龙虾管家,一键管理 OpenClaw
Qclaw是OpenClaw的图形化管理工具,专为新手设计:一键安装、拳击龙虾钳引导界面、分步可视化配置(模型/API/飞书钉钉/群策略/Skills等),支持定时任务、费用看板、记忆管理与人设加载,让小白轻松上手AI机器人。
2719 2
Qclaw:你的本地龙虾管家,一键管理 OpenClaw
|
Windows
该文件没有与之关联的应用来执行该操作。请安装应用,若已经安装应用,请在“默认应用设置“页面中创建关联
【10月更文挑战第22天】当你遇到“该文件没有与之关联的应用来执行该操作”的提示时,通常是因为文件类型未关联应用或文件损坏。解决方法包括:1. 确定文件类型并安装合适的应用程序,如Microsoft Office、Adobe Acrobat Reader等;2. 如果已安装应用,进入系统设置关联文件类型;3. 检查文件完整性,重新下载或验证文件格式。
8753 1
|
存储 负载均衡 算法
一致性哈希汇总
本文介绍了多种一致性哈希算法,包括Consistent Hashing Ring、Rendezvous、Jump、Multi-probe、Maglev、Anchor和Dx。这些算法各有特点,如Jump Hash实现了完美的key分布,而DxHash结合了多种算法的优点,支持动态扩缩容。文章还分析了各算法的性能指标,如内存使用、初始化时间、查询时间和调整集群大小的效率,以及均衡性和单调性。最后讨论了副本、权重和负载均衡策略的应用。
1488 62
一致性哈希汇总
|
缓存 Linux iOS开发
【C/C++ 集成内存调试、内存泄漏检测和性能分析的工具 Valgrind 】Linux 下 Valgrind 工具的全面使用指南
【C/C++ 集成内存调试、内存泄漏检测和性能分析的工具 Valgrind 】Linux 下 Valgrind 工具的全面使用指南
3638 1
|
JSON Java 数据格式
实现自定义序列化和反序列化控制的5种方式
实现自定义序列化和反序列化控制的5种方式