2

我目前正在开发一个项目,该项目需要开发一个 Java 应用程序可以访问的本机 DLL(在 C++ 中)。我已选择 JNA 进行桥接工作,但在将正确的 int 值从 Java 传递到 C++ 函数时遇到了问题。

简单地说,我有一个在 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是一个数组SomeStructurestructListSize并且structList都是共享内存中的全局变量。

这是 DLL 的 Java 接口中的方法签名:

SomeStructure.ByValue GetSomeStructureFromIndex(int index);

这就是我在 Java 中将方法用于测试用例的方式:

SomeStructure.ByValue received = library.GetSomeStructureFromIndex(1);

libraryStdCallLibrary是使用生成的 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 类有一个额外的变量和一个 Java 方法。SomeStructure我目前正在测试这是否是导致差异的原因。

问题解决了

@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。

进行所有这些修改后,我的代码运行良好,不再遇到访问冲突。

4

1 回答 1

1

'WINAPI' 暗示 stdcall 调用约定,但您必须查看宏的本地定义才能确定。如果是这样,您需要StdCallLibrary代替Library. 这可能会影响您传入的index论点。

您还复制了您的结构(按值语义),而不是指向它的指针。当您按值传递结构或按值返回结构时,您需要告诉 JNA 您正在这样做

编辑

确保SomeStructure.size()与本机匹配sizeof(SomeStructure)。通常按值返回的结构是这样实现的,即调用者在堆栈上分配内存并将隐式指针传递给被调用者,然后被调用者写入该内存。如果调用者和被调用者对该内存的大小存在分歧,它可能会影响堆栈上的其他内容(例如参数和返回值)。您还可以向函数添加更多参数并打印它们的值(作为十六进制)以获得堆栈上的近似值。如果您传入可识别的参数(例如 0x12345678),通常会清楚是什么在错误的方向推动或拉动堆栈。

于 2013-02-20T20:01:58.357 回答