简体   繁体   English

JNA:回调返回空值

[英]JNA: Callback returns null value

I am using custom types in the callback method and always get an empty value.我在回调方法中使用自定义类型并且总是得到一个空值。 What could be the problem?可能是什么问题呢?

C code in dll(i have .h file for this dll): dll中的C代码(我有这个dll的.h文件):

typedef union
{
char    caStruct[16384];

struct
    {
    // Header
    int iCode;
    int iID;
    int iResult;
    int iInfo;                  // Bits 0-3 = 1: data structure TVehicleData, 2: TVehicleDataXL, 8: tCalResults
    int iNum;                       // number of data sets
    int iMask;                  // mask with participating sensors, if available
    int iaReserve[10];

    // "Payload"
    char caData[];
    };
} tResponse;

//callback function
DLL_PROC int    __stdcall vwacom_ResultCallback( void (__stdcall *Results)( tResponse Response ) ); // 0

My java code:我的java代码:

Structure transformation:结构改造:

@Structure.FieldOrder({"uResp"})
public class TResponse extends Structure {
    public static class ByReference extends TResponse implements Structure.ByReference { }
    public static class ByValue extends TResponse implements Structure.ByValue { }
    
    public UnionResp uResp;
    
    public static class UnionResp extends Union{
        public static class ByReference extends TResponseU implements Union.ByReference { }
        public static class ByValue extends TResponseU implements Union.ByValue { }
    
        public String caStruct; //char[0x4000]
        public StructResp sResp;
    }

    @Structure.FieldOrder({"iID","iCode","iResult","iInfo","iNum","iMask","iaReserve","caData"})
    public static class StructResp extends Structure{
        public static class ByReference extends TResponseS implements Structure.ByReference { }
        public static class ByValue extends TResponseS implements Structure.ByValue { }

        // Header
        public int  iID;
        public int  iCode;
        public int  iResult;
        public int  iInfo;                  // Bits 0-3 = 1: data structure TVehicleData, 2: TVehicleDataXL, 8: tCalResults
        public int  iNum;                       // number of data sets
        public int  iMask;                  // mask with participating sensors, if available
        public int[] iaReserve = new int[10];
        // "Payload"
        public String caData; //char[]
    }
}

dll interface: dll接口:

public interface JNAVIsiWheAi extends Library {
    
    JNAVIsiWheAi INSTANCE = (JNAVIsiWheAi) Native.load("C:\\CWM\\JNAVIsiWheAi.dll", JNAVIsiWheAi.class);
    
    //vwacom_ResultCallback( void (__stdcall *Results)( tResponse Response ) );
    interface Results extends Callback{
        void invoke(TResponse tRes);
    }

    int vwacom_ResultCallback(Results results);
}

Use callback:使用回调:

public void resultCallback() {
    JNAVIsiWheAi.Results results = new JNAVIsiWheAi.Results() {
        @Override
        public void invoke(TResponse tRes) {
            /*just many sout imformation */
        }
    };

    jnavIsiWheAi.vwacom_ResultCallback(results);
}

I don't get errors, but when outputting values, they are always empty.我没有收到错误,但是在输出值时,它们总是为空的。

My example console output looks like this:我的示例控制台输出如下所示:

  • caStruct = null; caStruct = null; iID=0;身份证=0; iCode=0; iCode=0; iResult=0; iResult=0; iInfo=0; iInfo=0; iNum=0; iNum=0; iMask=0 iaReserve =[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; iMask=0 iaReserve =[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; caData=null caData=null

There are a few issues here.这里有几个问题。

First, you're not allocating enough memory to fill the data.首先,您没有分配足够的内存来填充数据。 By mapping the char[16384] to String you're essentially just allocating enough space for a pointer to memory elsewhere.通过将char[16384]映射到String ,您实际上只是为指向其他地方的内存指针分配了足够的空间。 This should be a byte[] to allocate that much space to the Union.这应该是一个byte[]来为 Union 分配那么多空间。 JNA has no size information to allocate more space to read the result. JNA 没有大小信息来分配更多空间来读取结果。

(Related, the String mapping in your structure won't work, and could cause an invalid memory error when you try to read it. The correct thing to do for a byte array of unknown length would be to map a byte array of size 1, and either directly read the values from that field's offset, or resize the byte array using length information and re-read it. But you know a max size already because of the 16384 bytes minus the allocations for the 16 int s, so you could just allocate that size.) (相关,结构中的String映射不起作用,并且在您尝试读取它时可能导致无效的内存错误。对于未知长度的字节数组,正确的做法是映射大小为 1 的字节数组,并直接从该字段的偏移量中读取值,或者使用长度信息调整字节数组的大小并重新读取它。但是您已经知道最大大小是因为 16384 字节减去 16 个int的分配,所以您可以只需分配该大小。)

Secondly, the values in a union do not auto-read because JNA does not know which of the union types is appropriate to read.其次,联合中的值不会自动读取,因为 JNA 不知道哪种联合类型适合读取。

From the Union javadoc:来自Union javadoc:

Upon reading from native memory, Structure, String, or WString fields will not be initialized unless they are the current field as identified by a call to setType(java.lang.Class<?>) .从本机内存读取时,不会初始化结构、字符串或 WString 字段,除非它们是通过调用setType(java.lang.Class<?>)标识的当前字段。 The current field is always unset by default to avoid accidentally attempting to read a field that is not valid.默认情况下,当前字段始终未设置,以避免意外尝试读取无效字段。 In the case of a String, for instance, an invalid pointer may result in a memory fault when attempting to initialize the String.例如,在 String 的情况下,在尝试初始化 String 时,无效的指针可能会导致内存错误。

For most unions, there is an element (often the first one) to read which tells you the type.对于大多数联合,有一个元素(通常是第一个)要读取,它告诉您类型。 In this particular mapping, it appears the union is only there to force a fixed-size structure and you'll always want the StructResp .在这个特定的映射中,似乎联合只是为了强制一个固定大小的结构,你总是需要StructResp

In your TResponse mapping, you should override read() and set the type appropriately.在您的TResponse映射中,您应该覆盖read()并适当地设置类型。 The general pattern is to call super.read() to read the Structure and get type info, set the type based on that, then read() the union.一般模式是调用super.read()来读取结构并获取类型信息,基于此设置类型,然后read()联合。

@Override
public void read() {
    super.read(); // not necessary in this case because you're not reading type 
    uResp.setType(StructResp.class);
    uResp.read();
}

Note, however, that since you're always reading the structure element, you don't actually need to map the union at all.但是请注意,由于您总是在读取结构元素,因此您实际上根本不需要映射联合。

Just simplify things and only map the Structure you want to populate, and allocate enough memory for the caData element based on the fixed size:只需简化事情并仅映射您要填充的结构,并根据固定大小为caData元素分配足够的内存:

public static class StructResp extends Structure{ 
    // ... existing mappings ...
    public byte[] caData = new byte[0x4000 - 16 * Integer.BYTES];
}

Then you can simply extract the String with Native.toString(caData) .然后,您可以简单地使用Native.toString(caData)提取String (This may be Native.toWideString() if the data is in UTF-16.) (如果数据是 UTF-16,这可能是Native.toWideString() 。)

Since you're mapping a __stcall library, your interface should extend StdCallLibrary rather than Library .由于您正在映射__stcall库,因此您的接口应该扩展StdCallLibrary而不是Library

And finally, the argument in the callback is not a pointer;最后,回调中的参数不是指针; Structures are treated ByReference by default as arguments, but you need to use the ByValue tag in the invoke() method.默认情况下,结构被ByReference视为参数,但您需要在invoke()方法中使用ByValue标记。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM