另一个JNI与java.lang.UnsatisfiedLinkError有关

时间:2014-04-30 12:52:16

标签: java c++ java-native-interface

我在类似的问题上经历过这么多论坛帖子,但我觉得我已经看到了这一切。简而言之,我通过JNI的奇迹获得了本机代码,由Java访问。在我开始使用JAVA包之前,代码工作正常。我已经关注了所有论坛帖子,关于确保Javah cmd在正确的文件夹中运行,并且已经成功构建了我的DLL。我已经确保包含包装器的包存在于C ++函数的名称中。由于项目的大小,我将简单地展示此代码的示例。注意所有代码(DLL' s,JVM C ++编译器)以32位运行,目标是运行Windows 8.1的PC。

我的所有Java代码都包含在包中:

package com.optin.executableContainer.client;

因此,在我的Java包装器类中,我明确加载DLL(32位),如下所示

static
{
System.load("C:\\JNITests\\JNIBridge.dll"); 
}

我知道这会加载DLL,因为没有DLL在该文件夹中我得到了"无法加载库"运行时错误。由javah -jni命令生成的标头代码是

JNIEXPORT void JNICALL Java_com_optin_executableContainer_client_COMControl_setControlBackColor
(JNIEnv *, jobject, jlong);

.cpp文件中包含的相关C ++代码是:

JNIEXPORT void JNICALL Java_com_optin_executableContainer_client_COMControl_setControlBackColor
(JNIEnv *env, jobject obj, jlong color)
{
printf("\nHello World\n");
}

使用Visual Studio 2010编译时没有错误。我已经通过使用DLL导出查看器验证了此函数存在于目标DLL中,该导出查看器将函数名称视为:

void __stdcall Java_com_optin_executableContainer_client_COMControl_setControlBackColor(struct JNIEnv_ *,class _jobject *,__int64)

然而,当我运行此代码时,在Eclipse IDE中,我得到了可怕的

Exception in thread "main" java.lang.UnsatisfiedLinkError: 
    com.optin.executableContainer.client.COMControl.setControlBackColor(J)V
at com.optin.executableContainer.client.COMControl.setControlBackColor(Native Method)

如果我错过了一些事情,请指出给我。

此致 Jarren

更新: 我去比较了工作dll(在包装之前完成)和新的dll,并发现了一些主要的差异。这是unpackaged working dll的函数名称

_Java_COMControl_destroy@8

这是新打包的unworking dll的函数名称

void __stdcall Java_com_optin_executableContainer_client_COMControl_destroy(struct JNIEnv_ *,class _jobject *)

Java代码COMControl.java(生成COMControl头)声明此函数如下

private native void destroy();

我已经尝试比较生成工作头文件和生成损坏头文件的头文件之间的差异,除了明显的基于包装的更改(com_optin等)之外没有其他差异。 COMControl.java文件的唯一区别是在package com.optin.executableContainer.client;的顶部包含了{{1}}。

这里发生了什么?

1 个答案:

答案 0 :(得分:1)

您正在将本机例程导出为名称错误的C++函数。我们可以告诉这个的原因是因为函数的参数列在库的导出信息中。每当您可以告诉函数名称中传递的参数类型时,它意味着该函数将作为装饰C++方法导出。如果您在没有解码的情况下查看库的原始导出信息,它将类似于:

?Java_Package_ComControl_destroy@@YGXPAUJNIEnv_@@PAV_jobject@@@Z

当它看起来像这样,java运行时找不到它,它应该看起来更像:

_Java_Package_ComControl_destroy@8

这是一个未修饰的C例程。 @8是8字节的stdcall简写,在堆栈中传递给例程

发生这种情况的最常见原因是声明该函数并由javah 生成的.h文件与定义内容的.cpp文件不匹配函数 - 即.h文件和.cpp文件之间存在一些细微差别。您应该将.h文件中函数的声明复制粘贴到.cpp文件中,确保所有参数都排成一行并且所有类型都排成一行。

此外,对于.cpp文件,您必须确保#include是由javah生成的.h文件,而不是#include <jni.h>文件C 1}}。如果你不这样做,那么编译器将不知道该例程将被导出为C样式例程。这是生成的已编译dll不包含方法的未解码版本的常见原因。

如果要手动强制使用未修饰的.cpp调用约定导出的例程,请在extern "C" JNIEXPORT void JNICALL Java_com_optin_executableContainer_client_COMControl_setControlBackColor (JNIEnv *env, jobject obj, jlong color) { printf("\nHello World\n"); } 文件的函数定义中输入:

extern "C"

通过以这种方式使用C,该函数将被定义为导出为C++例程而不是.h例程;但这是不可取的,因为你应该真的包括来自javah的{​​{1}}。

我不知道visual studio是否有-Wmissing-declarations gcc的{​​{1}},它注意到这样的不一致,这对.dll来说很重要。