通过JNI调用System.loadLibrary()不会加载本机方法

时间:2015-02-24 19:43:24

标签: c++ java-native-interface

我们有一个创建嵌入式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,一切正常。这非常令人沮丧,因为我在网上看到的所有内容都表明这应该可以正常工作。

1 个答案:

答案 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调用这些!我希望这篇文章能让别人感到沮丧。我仍然对为什么负载看起来成功但后来没有找到符号感到困惑。