另一个与JNI相关的java.lang.UnsisfiedLinkError

Yet another JNI related java.lang.UnsatisfiedLinkError

本文关键字:java lang UnsisfiedLinkError JNI 另一个      更新时间:2023-10-16

我看过很多关于类似问题的论坛帖子,但我觉得我都看到了。简言之,我通过Java访问了JNI的奇妙之处,获得了本机代码。在我开始使用JAVA软件包之前,代码运行良好。我关注了所有关于确保Javah cmd在正确文件夹中运行的论坛帖子,并成功构建了我的DLL。我已经确保包装器所在的包存在于C++函数的名称中。由于项目的规模,我将简单地展示这段代码的一个示例。注意,所有代码(DLL、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 Worldn");
}

使用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,发现了一些主要的差异。这是未打包的工作dll 的函数名

_Java_COMControl_destroy@8

这是新打包的、无法运行的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文件中唯一的区别是在Java文件的顶部包含了package com.optin.executableContainer.client;

这是怎么回事?

您正在将本机例程导出为名称损坏的C++函数。我们之所以能说出这一点,是因为函数的参数列在库的导出信息中。只要您能够告知以函数名称传递的参数的类型,就意味着该函数将作为装饰的C++方法导出。如果您查看库的原始导出信息而不进行解映射,则它看起来像:

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

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

_Java_Package_ComControl_destroy@8

其是未修饰的CCD_ 6例程。@8是stdcall的简写,表示在堆栈中传递给例程的8个字节

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

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

如果您想使用未修饰的C调用约定手动强制导出例程,在.cpp文件中的函数定义中,您可以放入:

extern "C"
JNIEXPORT void JNICALL
Java_com_optin_executableContainer_client_COMControl_setControlBackColor
(JNIEnv *env, jobject obj, jlong color)
{
    printf("nHello Worldn");
}

通过以这种方式使用extern "C",函数将被定义为C例程而不是C++例程进行导出;但这是不可取的,因为您实际上应该包括来自javah.h

我不知道visual studio是否有类似于gcc-Wmissing-declarations,它会注意到这样的不一致性,这对.dll s有点重要。

相关文章: