另一个JNI、C++、DLL、UnsatisfiedLinkError <Native Method>

3

我已经寻找了两天,但没有一个解决方案可以帮助我,所以我们再试一次:

如何在JNI中修复UnsatisfiedLinkError?

这是我的Java代码:

package org.lingenio.util;

import java.util.*;

public class PTAPIWrapperForOmegaT {

    private native String translateWithPTAPI(String sentence);

    private native void test();

    public PTAPIWrapperForOmegaT(String sentence) throws Exception{
        System.out.println(sentence);
        test();     
    }

    static {
        System.load("C:/Users/michael/Desktop/OmegaT/OmegaT2.3_src/native/PTAPIWrapperForOmegaT.dll");
    }
}

以下是我的 C++ 代码:

    #include <iostream>
    #include <windows.h>
    #include <jni.h>
    #include "PTAPIWrapperForOmegaT.h"

    using namespace std;

    JNIEXPORT jstring JNICALL Java_PTAPIWrapperForOmegaT_translateWithPTAPI(JNIEnv *env, jobject obj, jstring sentence)
    {
/* stuff */
    }

    JNIEXPORT void JNICALL Java_PTAPIWrapperForOmegaT_test(JNIEnv *, jobject)
    {
        cout << "This comes from PTAPIWrapperForOmegaT.cpp test();" << endl;
    }


    int main(){
        return 0;
    }

还有头文件:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class PTAPIWrapperForOmegaT */

#ifndef _Included_PTAPIWrapperForOmegaT
#define _Included_PTAPIWrapperForOmegaT
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     PTAPIWrapperForOmegaT
 * Method:    translateWithPTAPI
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_PTAPIWrapperForOmegaT_translateWithPTAPI
  (JNIEnv *, jobject, jstring);

/*
 * Class:     PTAPIWrapperForOmegaT
 * Method:    test
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_PTAPIWrapperForOmegaT_test
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

以及我如何构建它:

call g++ -Wl,--add-stdcall-alias -c -DBUILDING_EXAMPLE_DLL -I G:/Software/Java/jdk1.7.0_01/include -I G:/Software/Java/jdk1.7.0_01/include/win32 PTAPIWrapperForOmegaT.cpp
call g++ -shared -Wl,-kill-at -o PTAPIWrapperForOmegaT.dll -I G:/Software/Java/jdk1.7.0_01/include -I G:/Software/Java/jdk1.7.0_01/include/win32 PTAPIWrapperForOmegaT.cpp

最后,出现了错误:

10211: Error: Uncatched exception in thread [Thread-14] 
10211: Error: java.lang.UnsatisfiedLinkError: org.lingenio.util.PTAPIWrapperForOmegaT.test()V 
10211: Error:   at org.lingenio.util.PTAPIWrapperForOmegaT.test(Native Method) 
10211: Error:   at org.lingenio.util.PTAPIWrapperForOmegaT.<init>(PTAPIWrapperForOmegaT.java:13) 
10211: Error:   at org.omegat.core.machinetranslators.LingenioTranslate.translate(LingenioTranslate.java:32) 
10211: Error:   at org.omegat.core.machinetranslators.BaseTranslate.getTranslation(BaseTranslate.java:64) 
10211: Error:   at org.omegat.gui.exttrans.MachineTranslateTextArea$FindThread.search(MachineTranslateTextArea.java:122) 
10211: Error:   at org.omegat.gui.exttrans.MachineTranslateTextArea$FindThread.search(MachineTranslateTextArea.java:102) 
10211: Error:   at org.omegat.gui.common.EntryInfoSearchThread.run(EntryInfoSearchThread.java:85) 

我不确定这两行g++的代码具体含义,我认为第二行足够了,但是某些教程可能提供了另一行代码并且我也保留了它。

我在Windows 7上使用MingW和最新版本的Java(我认为是1.7xxx)。

任何帮助都会受到赞赏,我怀疑错误出现在编译中,但我不知道该如何继续。

编辑

通过DependencyWalker查看dll文件,我可以看到函数的命名与我在.cpp文件中命名的函数名称相同。 当然,我正在使用它们各自的名称,即test(),从Java包装器中调用它们。 这可能是个问题吗? 能否告诉我以前经常使用JNI的人这是否是正确的方式?


这可能看起来有些奇怪,但尝试删除加载库时文件名的 .dll 后缀。我目前正在开发的项目不接受文件名末尾的 .dll。 - Hunter McMillen
这只会导致UnsatisfiedLinkError从(Native Method)更改为(Unknown Source)。System.load()实际上是加载不在标准System.loadLibrary()路径上的dll的正确方法。 - mknaf
尝试将dll移动到源目录中,并使用loadLibrary()函数。 - Hunter McMillen
没有帮助。我把dll文件放在C:\Users\michael\mypath下,并使用-Djava.library.path=C:\Users\michael\mypath运行jar文件。问题不是找不到dll文件,它已经加载了dll文件,但是却无法找到其中的函数。 - mknaf
既然您似乎已经找到了解决方案,请将其作为答案发布在您的问题中。 - Roger Lindsjö
1个回答

4
原来所有的代码都没问题。其实是我在编译头文件时犯了错误。如果你查看头文件的函数名称,就会发现:
JNIEXPORT jstring JNICALL Java_PTAPIWrapperForOmegaT_translateWithPTAPI
  (JNIEnv *, jobject, jstring);

现在,请查看您的Java文件的包成员身份,以我的情况为例:
package org.lingenio.util;

由于我错误地编译了头文件,JNI后来无法找到它正在寻找的符号,因为实际上它正在寻找这个:

JNIEXPORT jstring JNICALL Java_org_lingenio_util_PTAPIWrapperForOmegaT_translateWithPTAPI(JNIEnv *env, jobject obj, jstring sentence)

所以,祝愿那些遇到相同问题的人好运。我显然不是最优秀的Java程序员,这就是我必须花费如此长时间担心这个问题的原因。我应该在第一次编译头文件时正确地编译它们。 检查你的包和类路径!

我也遇到了同样的问题...基本上,我的函数名称与X包匹配,但我正在尝试从Y包调用该方法。您能解释一下编译头文件的“正确”方式是什么吗? - bugfixr
我想我刚刚搞错了函数名称。正如您所看到的,该包被称为org.lingenio.util,因此编译后的头文件也应该携带该信息 - 您可以在答案的第三个代码面板中看到:“Java_org_lingenio_util_yadda”。我犯了一个错误,寻找“yadda”而不是“Java_org_lingenio_util_yadda”。 - mknaf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接