最近项目中用到了Jni,需要用java调用Opencv生成的dll文件完成图像处理。
问题描述:UnsatisfiedLinkError:no msvcp120d in java.library.path
dll文件是同事提供的,他同时提供了一个demo project。最初是直接报错:java.lang.UnsatisfiedLinkError: no msvcp120d in java.library.path,这个原因很简单,没有导入相应的依赖库。
解决方案:
使用软件dependencywalker可以查看一个dll所有依赖的dll文件,查看后是一些opencv相关的dll库,导入后问题会解决。
注意事项:
一、首先要明白System.loadlibrary(),还有它和System.load()的区别
1.它们都可以用来装载库文件,不论是JNI库文件还是非JNI库文件。在任何本地方法被调用之前必须先用这个两个方法之一把相应的JNI库文件装载。
2.System.load 参数为库文件的绝对路径,可以是任意路径。
例如你可以这样载入一个windows平台下JNI库文件:
System.load("C://Documents and Settings//TestJNI.dll");。
3. System.loadLibrary 参数为库文件名,不包含库文件的扩展名。
例如你可以这样载入一个windows平台下JNI库文件
System. loadLibrary ("TestJNI");
4.TestJNI.dll 必须是在java.library.path这一jvm变量所指向的路径中。
可以通过如下方法来获得该变量的值:
System.getProperty("java.library.path");
默认情况下,在Windows平台下,该值包含如下位置:
1)和jre相关的一些目录
2)程序当前目录
3)Windows目录
4)系统目录(system32)
5)系统环境变量path指定目录
5.如果你要载入的库文件静态链接到其它动态链接库,例如TestJNI.dll 静态链接到dependency.dll, 那么你必须注意:
1)如果你选择
System.load("C://Documents and Settings// TestJNI.dll");
那么即使你把dependency.dll同样放在C://Documents and Settings//下,load还是会因为找不到依赖的dll而失败。因为jvm在载入TestJNI.dll会先去载入TestJNI.dll所依赖的库文件dependency.dll,而dependency.dll并不位于java.library.path所指定的目录下,所以jvm找不到dependency.dll。
你有两个方法解决这个问题:一是把C://Documents and Settings//加入到java.library.path的路径中,例如加入到系统的path中。二是先调用System.load("C://Documents and Settings// dependency.dll"); 让jvm先载入dependency.dll,然后再调用System.load("C://Documents and Settings// TestJNI.dll");
2)如果你选择System. loadLibrary ("TestJNI");
那么你只要把dependency.dll放在任何java.library.path包含的路径中即可,当然也包括和TestJNI.dll相同的目录。
综上所述,因为我已经调用System.loadLibrary("javaCallcpp"),所以我需要做的就是把其他的dll放在同一个目录下就可以了。
二、一般解决方法
1.设定环境变量。
比如:所编辑的Dll在目录“D:/cppProjects/nativecode/release”内,将这个路径复制添加到电脑的环境变量中的path变量内即可。
2.设定项目属性。(开发推荐)
右击项目名|选择属性properties|在左边列表内选择“Java Build Path”|在右边选项卡用选择“source”|点开项目名前的“+”号,选择“Native library location”,“Edit”选择上面“D:/cppProjects/nativecode/release”路径。(当然如果将dll拷贝到workspace下也可以用相对路径。也可右击“src”设定其properties内Native Library项。)
问题描述:UnsatisfiedLinkError: com.ibm.isbtest.Java2cpp.Contour
由于同时的Java2cpp是在默认包中,现在虽然可以执行,但是不能import到其他的类中,所以我放到了一个其他包中,然后就报这个错了,查了下原因是找不到这个方法,这个很奇怪,网上讲这个问题大部分是因为生成dll时候编译器的问题,比如gcc、mingw中的设置,使用vs重新生成就好了,但是和我的情况不同,我的是确实可以调用成功,只不过是换了一个包
解决方案:
最后在一个评论中找到原因了:有的时候没有注意包名,就是在生生成DLL的时候,javah ***.class这个类里是有包名的,于是生成的DLL也只能在那个包名下那个名字的类里使用了。这个DLL是不能随便放到其他类里用的。
解决方案:使用默认包中的类
从 J2SE 1.4 开始,Java 编译器不再支持 import 进未命包名的类、接口。 详见 J2SE 1.4 与 J2SE 1.3 兼容性文档,第 8 点第二部分:http://java.sun.com/javase/compatibility_j2se1.4.html。而至于为什么 unnamed package 还没有被去除掉?因为这可以很方便地编写一些小程序,也可以方便初学者进行学习。
但是,使用还是可以的,就是使用反射,比如:
在默认包里有个类:
public class DefaultPackage {
public void disp() {
System.out.println("Hello World!");
}
}
而如果你想再包test下的类中调用disp()方法可以这样:
package test;
import java.lang.reflect.*;
public class TestDefaultPackage {
public static void main(String[] args) throws Exception {
Class c = Class.forName("DefaultPackage");
Method m = c.getDeclaredMethod("disp", null);
m.invoke(c.newInstance(), null);
}
}
导致问题的原理:
UnsatisfiedLinkError:在把本机调用链接到对应的本机定义时,类装入器扮演着重要角色。如果程序试图装入一个不存在或者放错的本机库时,在链接阶段的解析过程会发生 UnsatisfiedLinkError。
JVM 规范指定 UnsatisfiedLinkError 是:对于声明为 native 的方法,如果 Java 虚拟机找不到和它对应的本机语言定义,就会抛出该异常。当调用本机方法时,类装入器会尝试装入定义了该方法的本机库。如果找不到这个库,就会抛出这个错误。
演示了抛出 UnsatisfiedLinkError 的测试用例 :
UnsatisfiedLinkError.java
public class UnsatisfiedLinkErrorTest {
public native void call_A_Native_Method();
static {
System.loadLibrary("myNativeLibrary");
}
public static void main(String[] args) {
new UnsatisfiedLinkErrorTest().call_A_Native_Method();
}
}
这段代码调用本机方法 call_A_Native_Method(),该方法是在本机库 myNativeLibrary 中定义的。因为这个库不存在,所以在程序运行时会发生以下错误:
The java class could not be loaded. java.lang.UnsatisfiedLinkError:
本机库的装入由调用 System.loadLibrary() 方法的类的类装入器启动,就是 UnsatisfiedLinkErrorTest 的类装入器。根据使用的类装入器,会搜索不同的位置:
对于由 bootstrap 类装入器装入的类,搜索 sun.boot.library.path。
对于由扩展类装入器装入的类,先搜索 java.ext.dirs,然后是 sun.boot.library.path,然后是 java.library.path。
对于由系统类装入器装入的类,搜索 sun.boot.library.path,然后是 java.library.path。
UnsatisfiedLinkErrorTest 类是由系统类装入器装入的。要装入所引用的本机库,这个类装入器先查找 sun.boot.library.path,然后查找 java.library.path。因为在两个位置中都没有需要的库,所以类装入器抛出 UnsatisfiedLinkageError。
综上所述:
1unsatisfiedlinkError:dll名,那说明你没有把dll放到合适的位置,一般就和要调用原生函数的类放在一起,当然前提是你成功的生成dll了
2unsatisfiedlinkError:方法名,这个时候你其实dll已经成功生成了,而且位置也正确,它的意思就是你没有定义那个函数,你可能会说,我明明定义了,其实当你发现问题所在,你只能自虐了,肯定是你在C文件中定义函数时有些字母大小写错了,因为其他地方是自动生成的,不会出错。尤其是直接从网上拷贝源程序时经常发生这种问题,有些作者不负责任,把有错误的程序也贴上去。