使用JNI调用dll(C++)
简单说下业务背景,有一个用C++写的加密算法(据说速度快),需要用JAVA来调用,并将值展示在页面。这块内容网上一搜一大堆,给个别人写的教程,非常详细,照着做,可以实现一个DEMO,传送门。
但是,这仅仅只是一个DEMO,在实际应用中,应该怎么做。
dll文件的位置
网上说法是将调用的dll放在tomcat的bin目录下,但是在实际生产中,我们希望能将调用的dll文件放在工程里,这样,就不会对环境产生依赖。
废话不多说,直接上代码。
public class CallThirdDll {
public native String callThirdDll(String str, int mode);
//这是大多数的作法,但是dll必须放在java.library.path目录
/*static {
//这里只需要写上你生成的那个中介dll名称即可
System.loadLibrary("CallThirdDll");
}*/
public CallThirdDll()
{
//我这里将dll放在包下面
String packagePath = "/com/test/common/jni/";
//这里注意,先写第三方的dll名称,再写自己JNI产生的dll,否则,系统会提示找不到CallThirdDll.dll
String[] filenames = new String[]{"ThirdDll.dll", "CallThirdDll.dll"};
// 取得src下的物理路径,
String path = CallThirdDll.class.getResource("/").getPath();
// 将路径里的%20替换成空格
path = path.replaceAll("%20", " ");
System.out.println(path);
for (String filename : filenames)
{
//这里的Load里在的地址是绝对地址
System.load(path + packagePath + filename);
}
}
}
这样的好处,显而易见。
但是我却没有使用这种方式,应为生产环境是WAS,使用这种方式,找不到dll所在目录。
于是采用下面的方式来取得dll的绝对路径,虽然可以正常打印出dll的目录,但是却报java.lang.UnsatisfiedLinkError
。我郁闷了,打印出来的目录明明可以找到dll的,为什么会报这个错,如果有知情的,求告知。最终,我还是把所用的dll放在JDK的Bin目录下面,代码采用System.loadLibrary("MediumDll");
的方式来调用dll的。
CallThirdDll.class.getClassLoader().getResource("/com/test/common/jni/JACVinToPin.dll").toURI().getPath();
关于32位 OR 64位的dll
如果你的JDK是32位的,那么请使用对应的32位的dl,不要问为什么,因为不那样做,会报错。具体的异常代码,我就不贴了。
另外,在用javah来编译你的头文件时,请采用实际环境中的JDK下的javah.exe来编译。
既然说到javah编译头文件,再多说一句,请切到你的编译出来的class的包所在的上级目录来执行javah com.test.jni.CallThirdDll。
即classes这个目录下执行javah命令。
内存泄漏
如果说使用JNI最令人头疼的事情是什么,就是出了问题,不知道怎么排查。我调用dll可以正常返回值,但是却会导致JVM异常退出,我一开始觉得是内存溢出,我在这件事情上搞了一整天,最终发现完全不是的。
是因为我在C++中(我不了解C++)里面进行char声明的时候,内存大小申请小了,就是下面这句话。
char* m_char = (char*)malloc(sizeof(char)*10);
好吧,至于其他要Release的地方,请自行释放。