我在回调方法中使用自定义类型,并且始终获得空值。有什么问题吗?
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不知道哪种联合类型适合读取。
FromUnion
javadoc:
在从本机内存中读取时,结构、字符串或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
标记。