JAVA通过JNA调用C/C++生成的DLL库网上介绍的方法已经很多了。
但是轮到自己调用的时候,就各种问题困扰了整整一天。
我把遇到的问题分享一下,万一朋友们遇到和我一样的问题,可以参考解决。
基本使用方式
1. 使用CDecl方式导出的DLL
package com.sun.jna.examples;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
public class HelloWorld {
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary)
Native.load((Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class);
void printf(String format, Object... args);
}
public static void main(String[] args) {
CLibrary.INSTANCE.printf("Hello, %s", "world");
}
}
2. 使用StdCall方式导出的DLL
将父类改成StdCallLibrary (此处有坑,稍后解释)
public interface CLibrary extends StdCallLibrary
3. 单例调用模式
CLibrary SYNC_INSTANCE = (CLibrary) Native.synchronizedLibrary(INSTANCE);
常见错误
1. dll库无法加载
java.lang.UnsatisfiedLinkError: Unable to load library …
解决方法:
- 改成绝对路径 ,比如 “D:\\dlls\\xxx.dll”
- java启动参数加入-Djna.library.path=“D:\\dlls”
- 将dll放入java.exe相同的目录
2. 不是有效的32位程序
java.lang.UnsatisfiedLinkError: %1 不是有效的 Win32 应用程序。
解决方法:
- JDK的版本要和调用dll的版本一致
3. 找不到指定的程序
java.lang.UnsatisfiedLinkError: Error looking up function
这个就是上面说到的那个坑,使用StdCall方式导出的DLL,暴露出的方法名是自动生成的(c++不太懂,大概是这么回事),比如定了一个方法名:TestPrint(), 这时候实际生成的方法名就变成了 _TestPrint@12 (下划线+方法名+@+参数栈数),用 depends.exe 这个软件可以也可以看出来。
网上搜了许久也没有一个确切的答案,debug的时候,NativeLibrary.java 586行getFunction方法中,传入的functionName 是TestPrint,我想是不是这时候要是传入_TestPrint@12就可以了。 然后再看继承的StdCallLibrary.java,里面有一个StdCallFunctionMapper类型的常量:FUNCTION_MAPPER, 而这个StdCallFunctionMapper的69行getFunctionName方法中,正是返回了_TestPrint@12这种形式的名称,按说应该要走到这边就没问题了,但是一直debug不进来。翻了翻源代码,终于找到在 Native.load() 方法中,可以传入一个自定义的Map,最后终于找到了这个方法。
解决方法:
- Native.load() 方法传入第三个参数:
MyLib INSTANCE = (MyLib) Native.load("D:\\dlls\\MyLib"), MyLib.class,
Collections.singletonMap(OPTION_FUNCTION_MAPPER, FUNCTION_MAPPER));
其中:OPTION_FUNCTION_MAPPER 是 com.sun.jna.Library里的常量,FUNCTION_MAPPER 是 com.sun.jna.win32.StdCallLibrary 里的常量。
其实这个Map参数在官网的说明中也提到了。但是没有具体例子的话,很难知道在哪里使用,以及如何使用吧。
4. 不支持二维数组
java.lang.IllegalArgumentException: Unsupported array argument type:
由于C那边二维数组的内部存储类型是用一维数组来模拟的,所以Java调用的时候先要把二维数据转换成一维数组才行。具体就是两个for循环遍历了。例子可以参考这个:http://hant.ask.helplib.com/java/post_11075319
5. 结构体等还没用到,待补充
参考
官网:https://github.com/java-native-access/jna
吐槽
调查中,使用depend查看dll依赖,一度以为是缺少API-MS-WIN-*** 文件导致的,一个一个下载实在太费力了,放弃。
后来下载了visual studio 用c#调用这个dll,出乎意料的好用:
[DllImport("D:\\dlls\\MyLib.dll")]
public static extern void TestPrint(double[,] input, int rows, int cols, double TM, double[,] output);
说明不是dll缺少的原因,才继续回到Java,看看到底问题出在了哪里。
记录一下, 小司机不才,如果大家有更好的解决方式,请评论。