一、动态库引入错误
第一种找不到Exception:
Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'TEST_API1': Native library (win32-x86-64/TEST_API1.dll) not found in resource path ([file:/C:/Program%20Files/Java/jdk1.8.0_131/jre/lib/charsets.jar, file:/C:/Program%20Files/Java/jdk1.8.0_131/jre/lib/deploy.jar, .......
引起原因及解决方式:1、动态库名字错误;2、动态库放置位置不对,很多博客上都说将对应的动态库放置到C:/windows/System32(64位操作系统)或者C:\Windows\SysWOW64(32位操作系统)等目录下,其实不建议这样去做。因为在JNA中当调用 Native.loadLibrary(…)方法时,最终执行的方法是在NativeLibrary类中的loadLibrary方法,该方法先会判断是不是相对路径,然后从启动项加载上去查找动态库,最后才会在系统路径下查找。最后才会判断系统平台类型,确定加载什么样(.dll / .so)什么版本的动态库。所以最好的放置位置是放到使用的JDK版本的安装目录的bin目录下,如:C:\Program Files\Java\jdk1.8.0_131\bin;
第二种找不到Exception:
`Exception in thread “main” java.lang.UnsatisfiedLinkError: C:\Windows\System32\TEST_API1.dll: %1 不是有效的 Win32 应用程序。
…`引起原因及解决方式: 动态库确实已经放置到正确目录下,但是依旧加载不到。 1、因为加载的动态库版本和使用的JDK版本不匹配导致; 2、版本使用一致,但是动态库自己依赖的一些其他动态库版本不对或者不可用;如:TEST_API1.dll动态库使用依赖公共库msvcr100d.dll,但是你的环境下并没有msvcr100d.dll或者msvcr100d.dll不可用,也会导致异常出现加载不到。(此时,使用depends.exe工具查看使用的动态库需要的其他dll是否加载完善。)
第三种找动态库错误Exception:
Exception in thread "main" java.lang.UnsatisfiedLinkError: C:\Windows\System32\TEST_API1.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform 引起原因及解决方式:JDK使用的是64位版本,DLL使用的是32位版本。版本相互匹配,要么都是用32位做开发,要么都是64位做开发。
二、接口中的方法和动态库中的函数名不一致异常
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'TEST_OpenService1': 找不到指定的程序。 该种异常就是很明显的,在java中定义的接口中的方法名和动态库中的函数名不一致导致的。 解决方式:使用depends.exe软件查看正确的函数名。 【注】在32位的动态库中,函数名或很怪异,包括特殊字符‘@’和数字等。遇到此种情况,在java接口类中可以定义一个内部类:UnionFunctionMapper 实现FunctionMapper 接口即可,如下:
class UnionFunctionMapper implements FunctionMapper {
@Override
public String getFunctionName(NativeLibrary library, Method method) {
if (Platform.is64Bit()) {
//dll为64位时候,返回原函数名
return method.getName();
} else {
//dll为32时,返回更改后的函数名
HashMap<String, String> map = new HashMap<String, String>();
map.put("TEST_OpenService", "_TEST_OpenService@12");
map.put("TEST_CloseService", "_TEST_CloseService@0");
map.put("TEST_GetSystemUserAll", "_TEST_GetSystemUserAll@4");
// ......
String name = map.get(method.getName());
return name == null ? method.getName() : name;
}
}
}
然后在加载动态库的时候使用如下方式:
Map<String, Object> options = new HashMap<>();
options.put(Library.OPTION_FUNCTION_MAPPER, new TESTLib.UnionFunctionMapper());
// TESTLib 为定义的接口类,继承了StdCallLibrary类或者Library类,其中有内部类UnionFunctionMapper;
// libPath为动态库的名字,可以加一个文件夹目录。
TESTLib testLib= (TESTLib) Native.loadLibrary(libPath, TESTLib.class, options);
三、构造的Structure和动态库中的使用不匹配
JNA: Callback com.hexf.adapter.api.callback.MethodA$MethodAck@179d3b25 threw the following exception: java.lang.IndexOutOfBoundsException: Bounds exceeds available space : size=269, offset=538 at com.sun.jna.Memory.boundsCheck(Memory.java:185) at com.sun.jna.Memory.share(Memory.java:114) … C 中的struct对应 java 中的 structure结构,在构造的时候需要注意以下几点: 1、C 中的指针类型对应java中的特定类型,如:int *a----->IntByReference; 2、字节长度必须匹配,如:C中的char是一个字节,对应Java中应该使用byte,而不是char(Java中是两字节)。 3、结构体构造的类中最好实现两个内部类,如下: public static class ByReference extends TestStruct implements Structure.ByReference{ } public static class ByValue extends TestStruct implements Structure.ByValue{ } 其中如果某函数的参数是指针类型,则使用TestStruct .ByReference ,否则使用TestStruct .ByValue ,默认使用TestStruct 的时候表示的是指针类型。 4、Structure中的成员变量必须全部是public类型,这是规定。 5、Structure中的getFieldOrder()方法中的list最好(必须)是按照C中的参数顺序添加,因为C中申请的是连续地址。 6、C中struct的 char Password[30];类型最好是使用public byte[] Password = new byte[30];来对应构造,不建议使用String来表示。此外,Java基础数组初始化大小 new byte[30]。
四、构造的回调继承错误的类
对于构造回调接口来说,需要确定C中的动态库使用的是哪种实现方式才可以,一般可继承的Java类分为两种,分别为StdCallLibrary 以及 Callback。 其中,StdCallLibrary 对应C中的 __stdcall 类型的回调函数定义。 继承接口错误可能在调用的时候不会报错,但是程序执行之后会异常结束,常见的为:Process finished with exit code -1073741819 (0xC0000005) 正常程序正确执行结束之后应该为:Process finished with exit code 0. 网络上很多博客对错误码的解释为 0xC0000005 程序的内存原因或者受 金山词霸 软件的影响,不可否认,但是没有安装金山词霸的 同学们就需要查看一下自己的继承类是不是对的,如果实在不知道动态库是如何实现的可以用最笨的方式,分别尝试继承StdCallLibrary 以及 Callback 两种就可确定。 此外,这种也会导致的一种表象为JVM 崩溃,也即会弹出一个对话框,是Java SE Binary运行异常, 故障模块:StackHash_0a9e 异常代码: c0000005