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

时间:2013-02-20 15:25:38

标签: java c++ jna

我目前正在开发一个项目,该项目需要开发一个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是从我在代码中使用的实际结构重命名的。 structListSomeStructure的数组。 structListSizestructList都是共享内存中的全局变量。

这是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

(如果我在从数组中获取结构之前省略了indexif (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。

完成所有这些修改后,我的代码工作正常,不再遇到访问冲突。

1 个答案:

答案 0 :(得分:1)

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

您也在复制您的结构(通过值语义)而不是指向它的指针。按值传递结构或按值返回结构时,需要tell JNA that you're doing so

修改

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