Java通过JNA调用dll库函数
接上文《Java通过JNI调用dll库函数》,最终选择通过JNA调用第三方dll库。当然,dll位数还是必须和jvm一致,一开始厂商提供的是32位的,虽说64位系统也能装32位JRE,但多少有点让人不爽,所以最后让厂商提供了64位的dll。
References:
首先导入jna.jar,这个自行下载,这里就不提供了。然后就是添加调用的dll到工程目录下,也没什么好说的。
如果是像我这样使用别人提供的dll,可能还需要检测下dll的位数,这里提供一种检测方式,由于电脑安装了Visual Studio,所以直接用VS的开发人员提示符命令行检测,命令为dumpbin /header [dll名称]
32位和64位检测结果如下所示:
Java调用dll中的方法需要提供一个Java接口,Hello World代码请看参考链接,实际项目的部分代码如下:
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public interface PcCompare extends Library {
/**
* 调用dll的实例,加载路径如下<br>
* (PcCompare.class.getResource("/").getPath() + "dll/ARTH_DLL.dll") 实际路径<br>
* .replaceAll("%20", " ") 去除路径中的空格转义符<br>
* .substring(1) getPath方法返回绝对路径时第一位固定为/,将其去掉<br>
*/
PcCompare INSTANCE = (PcCompare) Native.loadLibrary((PcCompare.class.getResource("/").getPath() + "dll/ARITH_LIB.dll").replaceAll("%20", " ").substring(1), PcCompare.class);
/**
* 比对两个指纹特征值,<b style="color:red">64位dll调用</b>
* @param Src 特征文件指针(256bytes)
* @param Dst 特征文件指针(256bytes)
* @param SecuLevel 安全等级(1/2/3/4/5)(1:最低,5.最高)
* @param MatchScore 返回比对得分
* @return 0:不匹配或安全等级指定错误, 1:指定的两枚特征匹配
*/
int UserMatch(Pointer Src, Pointer Dst, byte SecuLevel, int[] MatchScore);
}
这是一个指纹比对的接口,首先是继承Library,如果动态链接库里的函数是以stdcall方式输出的,那么就继承StdCallLibrary。然后接口内部需要一个公共静态常量:INSTANCE,通过这个常量,就可以获得这个接口的实例,从而使用接口的方法,也就是调用外部dll的函数。
该常量通过Native.loadLibrary()这个API函数获得,该函数有2个参数:
- 第 一个参数是动态链接库dll/so的名称,但不带.dll或.so这样的后缀,这符合JNI的规范,因为带了后缀名就不可以跨操作系统平台了。搜索动态链 接库路径的顺序是:先从当前类的当前文件夹找,如果没有找到,再在工程当前文件夹下面找win32/win64文件夹,找到后搜索对应的dll文件,如果 找不到再到WINDOWS下面去搜索,再找不到就会抛异常了。比如上例中printf函数在Windows平台下所在的dll库名称是msvcrt,而在 其它平台如Linux下的so库名称是c。
- 第二个参数是本接口的Class类型。JNA通过这个Class类型,根据指定的.dll文件,动态创建接口的实例。该实例由JNA通过反射自动生成。
接口中只需要定义你要用到的函数或者公共变量,不需要的可以不定义。这里只留一个比对函数作示例,其他变量和函数都省略。
需要注意的就是参数类型的定义,如何将C/C++中的类型对应到Java中的类型,可以参考上面参考链接中的对应关系,或者查阅其他资料,由于接触有限,这里也没法给出齐全的对应关系。
用到的部分对应如下:
java | C/C++ |
---|---|
Pointer | unsigned char* |
byte | char |
int[] | int* |
接口完成后就可以调用了,一段调用的代码如下:
int[] pMatchScore = { -1 };
int match2Fp = PcCompare.INSTANCE.UserMatch(pBase.getPointer(), pTarget.getPointer(), (byte)2, pMatchScore);
完全是C风格的Java代码,不过通常执行的时候可能有各种问题,对接是比较麻烦的。比如找不到dll,那么有可能是dll路径不对,或者Web项目没有发布到工程目录下,如下图:
Maven管理的项目添加下dependency就行了,手动管理jar的话需要配置下,以Eclipse为例,设置deployment,如下:
或许还有其他原因,这个只能自行去踩坑了。
需要注意的是,如果dll中抛出异常了,会导致jvm crash,包括tomcat在内的所有应用都会挂掉,所以应该严格校验调用dll方法传入的参数,最好是dll调用单独写个方法,里面做一遍完整的校验。附一篇jvm crash的分析,虽然我看不懂,哈哈(需翻墙访问)。
https://www.slideshare.net/RednaxelaFX/java-crash