JNA的使用经历

最尽项目中应用到在Java中调用C/C++的DLL,其中的应用经历记录下来,顺便总结一下思考的过程,文中不会过多的说明基本的JNA调用实现。
相关知识可以参考:https://jna.dev.java.net/
建议在使用之前好好看一下,这样可以少走很多的弯路。。。

应用背景:
 移动充值项目中需要用一个DES的加密算法,而这个算法又不是标准的,客户那边提供过来一个用C++写的原程序,而我们的系统是用Java实现的,所以必需要进行转换,才能够使用。


解决办法:

要想在Java中使用该DES算法有以下几种方法:
1、将C++代码转化成Java代码;
2、将C++的原代码封装成DLL,然后在Java中调用;

由于加密算法相当复杂,有相当多的指针运算、位运算等等,要想把它改造成Java程序,困难重重,简单尝试之后,就放弃了该方法,转而将其封装成DLL,在Java中调用;

在Java中调用C++的DLL也有两种方法:
1、通过JNI实现调用;
2、通过JNA实现调用;

“不堪回首的JNI
我们知道,使用JNI调用.dll共享类库是非常非常麻烦和痛苦的。
如果有一个现有的.dll文件,如果使用JNI技术调用,我们首先需要另外使用C++语言写一个.dll共享库,使用SUN规定的数据结构替代C++语言的数据结构,调用已有的 dll中公布的函数。然后再在Java中载入这个适配器dll,再编写Java  native函数作为dll中函数的代理。经过2个繁琐的步骤才能在Java中调用本地代码。”

以上是在网络上找到的对JNI的评价,而JNA就相对好用很多,只是有一个JNA的JAR包,然后对应申明DLL中应用的函数,然后在Java中就可以使用了。


实现步骤: 
1、首先找一个C++的编译器,将其编译成DLL,选用的工具是MircroSoft VisualC++(原因是客户入指供的C++原码中有MFC相关的内容,使用BorlandC++Build编译无法通过),对外输出要使用的函数(方法),本实例中应用到的函数如下:

// 输出缓冲区(Out)的长度 >=((datalen+7)/8)*8,即比datalen大的且是8的倍数的最小正整数
//当keylen>8时系统自动使用3次DES加/解密,否则使用标准DES加/解密.超过16字节后只取前16字节
bool Des_Go(char *Out, char *In, long datalen, const char *Key, intkeylen, bool bType);

2、在Java中写对应的DES_Go函数:
 这里花费了较长的时间,主要是类型对应上,对于C中的 "Char*"在JNA中可以用String与之对应,但由于char*Out是要输出的数据,在C/C++中是分配了一段内存地址,然后用指针指向其对应的首地址; 以下是几种尝试方式:

a、用String来对应C++中"Char *":
Boolean Des_Go(String Out, String In, int datalen, final StringKey, int keylen, Boolean bType);
在Java中调用不报错,但取不到Out中的值,首先怀疑是DLL有问题,于是用Delphi写了一个应用该DLL的小程序(由于Delphi调用DLL相对比较简单,而且Delphi中有PChar可以与"Char*"对应),测试下来成功,故排处DLL有问题的想法,后来想原因可能是这样的,Java将基本数据类型都放在栈中,其分配的大小是固定的,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,所以取不到值。

 

b、用PointerByReference来对应C++中"Char *":
Boolean Des_Go(PointerByReference Out, String In, int datalen,final String Key, int keylen,
       Boolean bType);
在JNA中就是用PointerByReference 来对应C/C++中的指针(后来才知道对应的是Char**,但也是可以使用的,只是在取值时要用getPointer(),而不能用getValue(),用后者调用会报:Invalidmemoryaccess),但在Java中通过该方式调用时,不稳定时有报错,加密是成功了,但同时加密后的结果,用来解密时还原不出来之前的结果,感觉很困惑,估计还是类型上的问题,加密之后显示的是乱码,然后对这些乱码进行解密就无法返回原来的值。(原因:由于一些ASCII码在字符串无法显示出来,如控制字符等等,所以用这样的字符串进行解密,十有八九是无法还原的)。考虑既然字符串会显示乱码,那是否可以用字节数组呢,这样就可以屏蔽这个问题。

 

c、用byte[]来对应C++中"Char *":
Boolean Des_Go(byte[] Out, byte[] In, int datalen, final byte[]Key, int keylen, Boolean bType);

实验下来,终于得到自己想要的结果,“山穷水尽疑无路,柳岸花明又一村。”那个兴奋啊。。。

 

附上Java中JNA的使用原码,通过这个源码可以看出用JNA的方式调用C/C++的DLL是多么的简单、方便。

import com.sun.jna.Library;import com.sun.jna.Native;public class DESUtil {  private static DESLibrary NATIVE;  static {    try {       Native.setProtected(true);       NATIVE = (DESLibrary) Native.loadLibrary("DES", DESLibrary.class);    } catch (UnsatisfiedLinkError e) {       System.out.println("DES not found");    }  }}
 

 

public interface DESLibrary extends Library {     Boolean Des_Go(byte[] Out, byte[] In, int datalen, final byte[] Key, int keylen, Boolean bType);}
 
public static void main(String[] args) {    DESUtil.NATIVE.Des_Go(.......)}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值