JNA:回调返回空值



我在回调方法中使用自定义类型,并且始终获得空值。有什么问题吗?

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

java代码:

结构转换:

@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接口:

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);
}

使用回调:

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

我没有得到错误,但当输出值时,它们总是空的。

我的示例控制台输出如下所示:

  • caStruct = null;iID = 0;iCode = 0;iResult = 0;iInfo = 0;iNum = 0;iMask = 0iaReserve =[0,0,0,0,0,0,0,0,0];零/ul>

这里有几个问题。

首先,您没有分配足够的内存来填充数据。通过将char[16384]映射到String,你实际上只是为指向其他地方的内存指针分配了足够的空间。这应该是一个byte[]来分配这么多空间给Union。JNA没有大小信息来分配更多的空间来读取结果。

(相关的,您的结构中的String映射将无法工作,并且当您尝试读取它时可能会导致无效内存错误。对于长度未知的字节数组,正确的做法是映射大小为1的字节数组,然后直接从该字段的偏移量中读取值,或者使用长度信息调整字节数组的大小并重新读取它。但是你已经知道了一个最大大小,因为16384字节减去16个int的分配,所以你可以分配这个大小。

其次,联合中的值不会自动读取,因为JNA不知道哪种联合类型适合读取。

FromUnionjavadoc:

在从本机内存中读取时,结构、字符串或WString字段将被初始化,除非它们是调用setType(java.lang.Class<?>)标识的当前字段。默认情况下,当前字段始终不设置,以避免意外尝试读取无效字段。以String为例,在尝试初始化String时,无效指针可能导致内存错误。

对于大多数联合,都有一个元素(通常是第一个)要读取,它告诉您类型。在这个特殊的映射中,联合似乎只是用来强制固定大小的结构体,并且您总是需要StructResp

在您的TResponse映射中,您应该覆盖read()并适当地设置类型。一般模式是调用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();
}
但是,请注意,由于总是读取结构元素,因此实际上根本不需要映射联合。

只是简化事情,只映射您想要填充的结构,并根据固定大小为caData元素分配足够的内存:

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

那么您可以简单地用Native.toString(caData)提取String。(如果数据是UTF-16格式,可能是Native.toWideString())

因为你映射的是__stcall库,你的接口应该扩展StdCallLibrary而不是Library

最后,回调函数的实参不是指针;默认情况下,结构体将ByReference视为参数,但是您需要在invoke()方法中使用ByValue标记。