5

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

这是来自的相应输出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

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

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"

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

...

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);
}

最后,这是输出:

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)
4

3 回答 3

4

解决了——哇!

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

例如

JNIEXPORT void JNICALL Java_com_xyz_jsdi_1test_JSDI_func ==> _Java_com_xyz_jsdi_1test_JSDI_func

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

于 2013-06-19T20:31:14.733 回答
1

发布一个工作示例,将三个文件中的内容复制到同一目录中(修改 JDK 的路径),然后调用 build.cmd

/* File: HelloWorld.java */
public class HelloWorld {
    private static native void writeHelloWorldToStdout();

    public static void main(String[] args) {
        System.loadLibrary("HelloWorld");
        writeHelloWorldToStdout();
    }
}

/* File: HelloWorld.c */
#include <stdio.h>
#include "HelloWorld.h"
JNIEXPORT void JNICALL Java_HelloWorld_writeHelloWorldToStdout(JNIEnv *env, jclass c)
{
    printf("Hello World!");
}

rem File: build.cmd 

%echo off
echo delete generated binaries
del HelloWorld.class
del HelloWorld.dll
del HelloWorld.h
del HelloWorld.def

echo Compile the Java Class
javac HelloWorld.java

echo Generate the Header file
javah -classpath . -o HelloWorld.h HelloWorld

echo Build the DLL 
gcc -I"C:\Program Files (x86)\Java\jdk1.7.0_25\include" -I"C:\Program Files (x86)\Java\jdk1.7.0_25\include\win32" -Wl,--add-stdcall-alias -Wl,--output-def,HelloWorld.def -shared  -o HelloWorld.dll HelloWorld.c

echo run the program 
java HelloWorld
于 2013-08-30T00:22:05.597 回答
0

异常中的签名没有“int”参数。因此,您的 Java 代码与您的本机代码不一致。

于 2013-06-19T19:57:22.603 回答