我们有一个创建嵌入式JVM的本机C ++应用程序。这个JVM中的类使用SWIG包装器回调到C ++对象方法(尽管SWIG使用并不重要;它可以很容易地是javah生成的本机函数存根)。例如,我们有一个Java类,其本机方法如下:
package net.foo;
public class CppWrapJNI {
public final static native long foo(
long l, Stuff jarg1_, String s
);
}
C ++ DLL中有相应的实现:
extern "C" {
__declspec(dllexport) jlong JNICALL
Java_net_foo_CppWrapJNI_1foo(
JNIEnv *jenv, jclass jcls,
jlong jarg1, jobject jarg1_, jstring jarg2
) {
return 1;
}
}
让我们说这个DLL名为" foo.dll"。
然后我们尝试让JVM通过JNI加载DLL,使用如下代码:
jclass cls = env->FindClass("java/lang/System");
jmethodID mid = env->GetStaticMethodID(
cls, "loadLibrary", "(Ljava/lang/String;)V"
);
jstring jstr = env->NewStringUTF("foo.dll");
env->CallStaticVoidMethodV(cls, mid, jstr);
这一切都有效,并且对loadLibrary()的调用没有报告错误(此处未显示JNI异常处理,但我们这样做)。但是,稍后对CppWrapJNI.foo()的调用将失败,并显示如下错误:
java.lang.UnsatisfiedLinkError:
net.foo.CppWrapJNI.foo(JLnet/foo/Stuff;Ljava/lang/String;)J
奇怪的是,如果我纯粹用Java编写测试工具,以同样的方式调用loadLibrary,一切正常。这非常令人沮丧,因为我在网上看到的所有内容都表明这应该可以正常工作。
答案 0 :(得分:1)
最后,我查看了System.loadLibrary的源代码:
@CallerSensitive
public static void loadLibrary(String libname) {
Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);
}
啊哈! Reflection.getCallerClass()是关键。没有来电课程!它来自直接的C ++。解决方案相当简单,在System.load *()周围编写一个外观,我们可以从C ++调用它,它将转向并调用System方法:
public class SystemFacade {
public static void load(String path) {
java.lang.System.load(path);
}
public static void loadLibrary(String name) {
java.lang.System.loadLibrary(name);
}
}
瞧,它有效。据推测,java通过JNI调用而不是C ++的任何变体也可以避免这个问题。 @CallerSensitive标签似乎是一个线索;不要通过JNI调用这些!我希望这篇文章能让别人感到沮丧。我仍然对为什么负载看起来成功但后来没有找到符号感到困惑。