有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java 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运行它时,它从未像C#实现那样返回1

我想知道我是否做错了什么,因为我没有使用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示例无效吗

--编辑25-08-2021 12:04

文件中说,使用了提供给该方法的参数。所以我想,如果它们为null,那么该方法将不会返回任何内容。使用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);

共 (2) 个答案

  1. # 1 楼答案

    具有给定的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);
    }
    

    C上的int在重要的地方应该是32位的,因此它可以清晰地映射到javaint,因为所有参数都是通过引用传递的IntByReference是您的朋友。shortShortByReference也是如此(只是比特数不同)

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

    下面是您应该始终创建最小可运行示例的原因:

    您跳过了绑定的重要部分。C签名有一个u stdcall标记。这改变了呼叫约定。从StdCallLibrary派生应该可以解决这个问题

  2. # 2 楼答案

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