当前位置: 首页 > 知识库问答 >
问题:

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

关浩壤
2023-03-14

我目前正在做一个项目,需要开发一个原生DLL(用C++)来访问Java应用程序。我选择了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::StringStream将任何数据类型的值转换为std::String。执行情况如下:

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中的方法的方式:

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

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

Received index of 86701080

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

86701080可以是任意值。我意识到它会根据导出函数的签名而改变。我是不是漏了什么?如果函数签名为void PrintIndex(int index),则函数正确地接收到1的期望值

EDIT(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表示没有指定定长数组的大小。列表已初始化为包含一个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个答案

孟承嗣
2023-03-14

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

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

编辑

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

 类似资料:
  • 问题内容: 在Java中,如何将一个函数作为另一个函数的参数传递? 问题答案: Java 8及以上 如果你的类或接口只有一个抽象方法(有时称为SAM type),则使用Java 8+ lambda表达式,例如: 然后可以在使用MyInterface的任何地方替换lambda表达式: 例如,你可以非常快速地创建一个新线程: 并使用方法引用语法使其更加清晰: 如果没有 lambda表达式,则最后两个示

  • 我有一个Kotlin代码: 现在我想从Java类调用这个方法。我不知道该怎么称呼这个。这是我试过的 但它显示了以下错误。

  • 问题内容: 我已经熟悉Android框架和Java,并希望创建一个通用的“ NetworkHelper”类,该类可以处理大多数联网代码,使我能够从中调用网页。 我遵循了来自developer.android.com的这篇文章来创建我的网络类:http : //developer.android.com/training/basics/network- ops/connecting.html 码:

  • 假设我们有python中的函数: 虽然我可能会将传递给A,但我正在寻找一种优雅的方法来传递类似于的东西来执行。 我现在看到的唯一方法是发送一个函数列表并按顺序应用它们。有没有更好的办法?

  • 最近我在使用c语言时遇到了一些问题,基本上是这样的: 在一个函数(比如intmain)中,我声明了一个变量Y=5,我有一个lambda函数,它接收一个值并将Y相加; 我的问题是:我需要将这个lambda函数传递给一个已经存在的函数,这样它就可以在另一个函数内部调用。 我尝试了几件事,但没有一个像我预期的那样工作(有些甚至不起作用): 另一个问题是我不能改变我的receives函数签名,因为代码的剩

  • 在Python中,我可以很容易地将函数作为参数传递,并在另一个函数内部执行。