我正在Windows上使用Eclipse做一个简单的JNItesting应用程序。 我的C ++编译器是MinGW 4.6.2。 当我尝试在我的testingDLL中调用一个函数时,Java抛出UnsatisfiedLinkError
(DLL自身加载没有问题)。 我已经validation我的DLL导出一个“C”函数与javah
实用程序生成的函数具有相同的名称。
如何试图调用该函数可能会产生链接错误? (另外,还有什么办法可以得到更多的细节,找不到什么符号吗?一个秃头的声明,有一个UnsatisfiedLinkError
是无用的。)
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)
解决它 – WOOO!
事实证明,MSVC预先为__stdcall
函数的名称加下划线。 MinGW没有。 Windows JVM显然需要“_”前缀。 只要我在函数名前添加了“_”,并用MinGW重新编译,一切工作都变得简单了。
例如 :
JNIEXPORT void JNICALL Java_com_xyz_jsdi_1test_JSDI_func ==> _Java_com_xyz_jsdi_1test_JSDI_func
编辑 :MinGW中包含的dlltool
实用工具的--add-stdcall-underscore
dlltool
--add-stdcall-underscore
功能可以透明地为您解决这个问题。 在你的Makefile中设置它,你不用担心不同版本的实际源代码适用于不同的编译器。 请参阅此链接 。
发布一个工作示例,将同一目录下的三个文件中的内容复制(修改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
异常中的签名不具有“int”参数。 所以你的Java代码不同意你的本地代码。