为什么在 Windows 上调用 MinGW 编译的函数(不加载库)时会出现 UnsatisfiedLinkError?

0xb*_*7ed 5 c java windows dll java-native-interface

我正在 Windows 上使用 Eclipse 制作一个简单的 JNI 测试应用程序。我的 C++ 编译器是 MinGW 4.6.2。UnsatisfiedLinkError当我尝试在我的测试 DLL 中调用一个函数时,Java 抛出了一个 DLL(DLL 本身加载没有问题)。我已经验证我的 DLL 导出了一个与javah实用程序生成的函数同名的“C”函数。

尝试调用该函数怎么可能会产生链接错误?(另外,有没有办法获得更多关于没有找到什么符号的详细信息?有一个几乎UnsatisfiedLinkError没用的秃头声明。)

这是定义本机函数的Java:

package com.xyz.jsdi_test;

import java.io.File;

public class JSDI
{
    public static native void func(
        String str,
        int i,
        Integer ii,
        long j /* 64 bits */,
        Long jj,
        byte[] b
    );
    public static void dummy()
    {
        System.out.println("JSDI.dummy()");
    }
    static
    {
        File f = new File("..\\jsdi\\bin\\jsdi.dll");
        System.out.println("Preparing to load: " + f);
        System.load(f.getAbsolutePath());
        System.out.println("Successfully loaded: " + f);
    }
Run Code Online (Sandbox Code Playgroud)

这是来自的相应输出javah

...
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_xyz_jsdi_test_JSDI
 * Method:    func
 * Signature: (Ljava/lang/String;ILjava/lang/Integer;JLjava/lang/Long;[B)V
 */
JNIEXPORT void JNICALL Java_com_xyz_jsdi_1test_JSDI_func
  (JNIEnv *, jclass, jstring, jint, jobject, jlong, jobject, jbyteArray);

#ifdef __cplusplus
}
#endif
Run Code Online (Sandbox Code Playgroud)

...以及我如何实现该功能...:

extern "C"
{

JNIEXPORT void JNICALL Java_com_xyz_jsdi_1test_JSDI_func(
    JNIEnv * env,
    jclass _class,
    jstring str,
    jint i,
    jobject ii,
    jlong j,
    jobject jj,
    jbyteArray b
)
{
    // don't do anything...let's just try to get called successfully...
}

} // extern "C"
Run Code Online (Sandbox Code Playgroud)

这是我尝试调用它的方式。

...

public static void main(String[] args)
{
    JSDI.dummy(); // cause class to load, which should cause System.load() to run.
    JSDI.func("hello", 0, 0, 0L, 0L, (byte[])null);
}
Run Code Online (Sandbox Code Playgroud)

最后,这是输出:

Preparing to load: ..\jsdi\bin\jsdi.dll
Successfully loaded: ..\jsdi\bin\jsdi.dll
JSDI.dummy()
java.lang.UnsatisfiedLinkError: com.xyz.jsdi_test.JSDI.func(Ljava/lang/String;ILjava/lang/Integer;JLjava/lang/Long;[B)V
   at com.xyz.jsdi_test.JSDI.func(Native Method)
   at com.xyz.jsdi_test.SimpleTest.main(SimpleTest.java:24)
Run Code Online (Sandbox Code Playgroud)

0xb*_*7ed 4

解决了——哇!

事实证明,MSVC 在__stdcall函数名称前添加了一个下划线。MinGW 没有。Windows JVM 显然需要“_”前缀。当我在函数名称前面添加“_”并使用 MinGW 重建时,一切都运行得很好。

例如

JNIEXPORT void JNICALL Java_com_xyz_jsdi_1test_JSDI_func ==> _Java_com_xyz_jsdi_1test_JSDI_func
Run Code Online (Sandbox Code Playgroud)

编辑:MinGW 附带的实用程序--add-stdcall-underscore功能可以透明地为您解决这个问题。dlltool在 Makefile 中进行设置,您无需担心不同编译器的实际源代码版本不同。请参阅此链接