JNA的行为不同,然后我的c#对应,为什么?



我正在为一个硬件开发一些东西,我有一个C库来与硬件通信。我有一些方法可以向硬件发送信号(如灯泡打开),这些方法在使用JNA的c#和Java上都可以正常工作。

机器还有一个可按的按钮,当按钮被按下时,它会记录一个信号,该信号可以用a方法检索。

这是打算工作的方式是创建一个新的线程,它一直调用这个方法,直到它返回1,在这种情况下,它将有关于按钮按下的信息。

我已经得到了这个工作在c#与以下代码:

while (true)
{
byte[] ccbdata = new byte[255];
short TagCommand = -1, nMsg_type = -1;
int ccblen = 0, nGwId = 0, nNode = 0;
int ret = CWrapper.Wrapper.A(ref nGwId, ref nNode, ref TagCommand, ref nMsg_type, ref ccbdata[0], ref ccblen);
if (ret > 0)
{
// do stuff
}
Thread.Sleep(100);
}

在c#中导入的方法,如:

[DllImport("Clibrary.dll")]
public static extern int C(ref int Gateway_ID, ref int Node_Addr, ref short Subcmd, ref short msg_type, ref byte data, ref int data_cnt);

我希望这段代码也能在Java上工作。不幸的是,当我用java运行它时,它从不返回1,不像c#实现。

我想知道我是否做错了什么,因为我没有使用JNA的经验。在Java中,我像这样导入方法:

int C(LongByReference gatewayID, LongByReference tag_addr, LongByReference Subcmd, LongByReference msg_type,
ByteByReference data, LongByReference data_cnt);

我试着像这样运行代码:

while(run) {
gatewayID.setValue(0);
tag_addr.setValue(0);
subcmd.setValue(-1);
msg_type.setValue(-1);
byte b = 0;
data.setValue(b);
data_cnt.setValue(0);
int ref = CWrapper.INSTANCE.C(gatewayID, tag_addr, subcmd, msg_type, data, data_cnt);
if (ref > 0) {
System.out.println("hit");
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

在文档中,我有关于这个方法,它在C中定义为:

C(ByRef gatewayID As Integer, ByRef tag_addr As Integer, ByRef subcmd As Integer, ByRef msg_type As Integer, ByRef data As Byte, ByRef data_cnt As Integer)

有谁知道为什么c#的例子工作,而不是Java的一个?

—EDIT 25-08-2021 12:04

文档说明使用提供给方法的参数。所以我想如果它们为空这个方法不会返回任何东西。也许使用ByReference对象的初始化是错误的?

——编辑26-08-2021

我已经得到了c++签名,它是:

typedef int (__stdcall *pC)(int& Gateway_ID, int& Node_Addr, short& Subcmd, 
short& Msg_Type, unsigned char* Data, short& Data_Cnt);

在java中,long是8个字节,你的c#代码使用ref int是4个字节。
您试过使用IntByReference吗?

使用给定的C签名:

typedef int (__stdcall *pC)(int& Gateway_ID, int& Node_Addr, short& Subcmd, short& Msg_Type, unsigned char* Data, short& Data_Cnt);

我将像这样绑定它:

interface CLibrary extends StdCallLibrary {
int C(
IntByReference Gateway_ID,
IntByReference Node_Addr,
ShortByReference Subcmd,
ShortByReference Msg_Type,
byte[] Data,
ShortByReference Data_Cnt);
}

int在C上可以预期是32位的,所以它可以干净地映射到javaint,因为所有参数都是通过引用传递的,IntByReference是你的朋友。shortShortByReference也是一样(只是位元不同)。

这就留下了一个问题:DataData_Cnt会发生什么?我猜,数据是一个缓冲区,其中函数放置数据(调用C是一个拉操作)。该缓冲区长255字节(我假设,这是作为库的要求在某处记录的)。现在,当pull操作成功时,我希望将数据写入支持data的缓冲区,并将接收到的数据量写入Data_Cnt。现在调用方(Java)可以读取传输的数据量(Data_Cnt.getValue())并从byte[]中读取那么多。Java数组通过引用传递——JNI创建数组的副本,将其传递给函数,函数运行,数据被复制回数组。

这就是为什么你应该总是创建一个最小的可运行示例的原因:

您跳过了绑定的关键部分。C签名有一个__stdcall标记。这就改变了调用约定。从StdCallLibrary派生应该可以解决这个问题。

相关内容

  • 没有找到相关文章

最新更新