我目前正在开发一个项目,该项目需要开发一个Java应用程序访问的本机DLL(在C ++中)。我选择了JNA作为桥接作业,我遇到了从Java到C ++函数传递正确的int值的问题。
简单地说,我有一个函数在C ++中接受一个int值作为参数: (代码被剥离,方法被重命名以保持机密性)
JAVALINK_EXPORT SomeStructure WINAPI GetSomeStructureFromIndex(int index) {
std::string debugMsg("Received index of ");
debugMsg.append(toString(index));
OutputDebugString(debugMsg.c_str());
SomeStructure result = defaultStructure;
if (index >= 0 && index < structListSize)
result = structList[index];
return result;
}
toString
是一种使用std::string
将任意数据类型的值转换为std::stringstream
的简单方法。实施如下:
template <class T>
inline std::string toString (const T& t) {
std::stringstream ss;
ss << t;
return ss.str();
}
SomeStructure
是从我在代码中使用的实际结构重命名的。 structList
是SomeStructure
的数组。 structListSize
和structList
都是共享内存中的全局变量。
这是DLL的Java接口中的方法签名:
SomeStructure.ByValue GetSomeStructureFromIndex(int index);
这就是我在Java中使用Java方法的方法:
SomeStructure.ByValue received = library.GetSomeStructureFromIndex(1);
library
是使用StdCallLibrary
生成的DLL文件(Native.loadLibrary
的子类)的接口实例。当上面的代码在Java中执行时,我在Windows调试输出中得到类似以下输出的内容:
Received index of 86701080
(如果我在从数组中获取结构之前省略了index
行if (index >= 0 && index < structListSize)
),程序将继续遇到访问冲突错误。
86701080
可以是任意值。我意识到它会根据导出函数的签名而改变。我在这里错过了什么吗?如果函数签名为1
void PrintIndex(int index)
的预期值
编辑(0):我修改了示例代码以更紧密地匹配实际代码。
编辑(1):根据@ technomage的指示,我开始使用ByValue
来获取所有方法签名和收集返回结构的变量。
编辑(2):与C ++中的SomeStructure
结构相比,Java类SomeStructure
有一个额外的变量和一个Java方法。我目前正在测试是否是造成这种差异的原因。
问题已解决
@technomage解释说,对于C ++函数来解释其参数并按预期返回值,用作返回类型的结构的大小(以及用作函数参数的结构)不应与其Java对应的大小不同。对于SomeStructure
,在C ++中使用sizeof(SomeStructure)
,在Java中使用SomeStructure.size()
,可以检查此内容。
基本上,发生的事情是SomeStructure
结构的大小与其Java表示不同。 SomeStructure
包含一个固定长度的数组,如下面的代码所示:
#define MAX_LIST_SIZE 256
typedef struct {
int list[MAX_LIST_SIZE];
int length;
} SomeStructure;
但是,Java表示没有指定固定长度数组的大小。 list
已初始化为包含单个值0
。
package model;
import com.sun.jna.Structure;
public class SomeStructure extends Structure {
public static class ByValue extends SomeStructure implements Structure.ByValue { }
public int[] list = {0};
public int length = 0;
}
我通过用以下内容替换错误的初始化语句来解决问题:
private static final int MAX_LIST_SIZE = 256;
public int[] list = new int[MAX_LIST_SIZE];
注意:整数常量MAX_LIST_SIZE
被声明为private
以保持仅Java。
完成所有这些修改后,我的代码工作正常,不再遇到访问冲突。
答案 0 :(得分:1)
'WINAPI'意味着stdcall调用约定,但是您必须查看宏的本地定义才能确定。如果是,则需要StdCallLibrary
而不是Library
。这可能会影响您的传入index
参数。
您也在复制您的结构(通过值语义)而不是指向它的指针。按值传递结构或按值返回结构时,需要tell JNA that you're doing so。
修改强>
确保SomeStructure.size()
与原生sizeof(SomeStructure)
匹配。通常,通过值返回的结构被实现,使得调用者在堆栈上分配内存并将隐式指针传递给被调用者,然后被调用者写入该内存。如果调用者和被调用者不同意该内存的大小,它可能会影响堆栈中的其他内容(例如参数和返回值)。您还可以向函数添加多个参数并打印它们的值(作为十六进制)以获得堆栈上的内容的近似值。如果传入可识别的参数(例如0x12345678),通常会清楚是什么在错误的方向上推或拉堆栈。