如何使用 JNA 从 Java 传递 int 作为C++函数参数

How to pass int as a C++ function parameter from Java using JNA

本文关键字:作为 C++ 函数 参数 int 传递 何使用 JNA Java      更新时间:2023-10-16

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

简而言之,我有一个函数,它接受 int 值作为 C++ 中的参数:(剥离代码并重命名方法以保持机密性(

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::stringstream 将任何数据类型的值转换为std::string。实现如下:

template <class T>
inline std::string toString (const T& t) {
    std::stringstream ss;
    ss << t;
    return ss.str();
}

SomeStructure是从我在代码中使用的实际结构重命名的。 structList是一组SomeStructurestructListSizestructList 都是共享内存中的全局变量。

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

SomeStructure.ByValue GetSomeStructureFromIndex(int index);

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

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

library 是使用 Native.loadLibrary 生成的 DLL 文件(StdCallLibrary 的子类(接口的实例。当上面的代码在 Java 中执行时,我在 Windows 调试输出中得到类似于以下输出的内容:

Received index of 86701080

(如果我在从数组中获取结构之前省略了对行if (index >= 0 && index < structListSize) index的检查,程序将继续遇到访问冲突错误(

86701080可以是任意值。我意识到它会根据导出函数的签名而变化。我在这里错过了什么吗?如果函数签名已void PrintIndex(int index),函数将正确接收预期值 1

编辑(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 使用。

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

">

WINAPI"意味着stdcall调用约定,但你必须查看宏的本地定义才能确定。 如果是这样,您需要StdCallLibrary而不是Library。 这可能会影响您传入的index论点。

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

编辑

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