【发布时间】:2013-11-30 14:23:55
【问题描述】:
我们有 COM 服务器(作为 dll,我们只有 dll 文件),它实现了两个 COM 接口。其中一个接口允许我们从某个设备获取消息(作为结构)。根据消息,我们需要处理相应的结构。每个结构都是指向结构 VARIANT 的指针。这个结构是字节数组类型(VT_ARRAY | VT_UI1)。
所有结构都包含结构Header 和一些其他信息。结构 Header 包含字段 MsgId。根据MsgId,我们需要处理其他结构。
为了将结构从 dll 获取到 c#,我们使用反射。
现在,是时候举例了:
// Header (sub-struct of main struct) -
[StructLayout(LayoutKind.Sequential)]
public struct Header
{
public int MsgId;
}
// main struct
[StructLayout(LayoutKind.Sequential)]
public struct Main
{
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct)]
public Header h;
public int count;
}
每 100 毫秒我们通过反射从设备获取数据:
private bool ReadMessage(ref object msg)
{
object _msg = null;
object[] args = new Object[] { _msg };
ParameterModifier byRefParamMod = new ParameterModifier(1);
byRefParamMod[0] = true;
ParameterModifier[] pmArray = { byRefParamMod };
var value = SomeWrapper.CallMethod(ClientInstance, "ReadMessage", args, pmArray);
msg = args[0];
return ((int)value == S_OK) ? true : false;
}
然后我们将msg 转换为byte[],然后我们需要将字节数组转换为我们的结构。在这个地方,我们在编组方面遇到了一些问题。
正如我所写的,为了定义我们需要处理的结构(换句话说我们需要编组)首先我们需要编组所有结构中包含的Header 结构( 来自设备的所有消息中的其他词)。
为了编组Header struct,我们使用C# array within a struct 中提出的方法(有一些编辑):
public static T DeserializeMsg<T>(byte[] msg) where T : struct
{
// Pin the managed memory while, copy it out the data, then unpin it
GCHandle handle = GCHandle.Alloc(msg, GCHandleType.Pinned);
T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return theStructure;
}
我们如何使用一个:
private void ProcessMessage(byte[] message)
{
Header msgHeader = new Header();
msgHeader = DeserializeMsg<Header>(message);
}
一切正常!这里我们得到msgHeader.MsgId,然后我们需要得到另一个结构:
private void ProcessMessage(byte[] message)
{
Header msgHeader = new Header();
msgHeader = DeserializeMsg<Header>(message);
switch (msgHeader.MsgId)
{
case START:
Main msgMain = new Main();
// Here we get exception (see below)
msgMain = DeserializeMsg<Main>(message);
break;
default:
break;
}
}
这里出现异常:无法封送“主”类型的字段“h”。无效的托管/非托管类型组合(此值类型必须与 Struct 配对)
我们尝试将内部结构h 的MarshalAsAttribute 声明更改为
[MarshalAsAttribute(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_SAFEARRAY)]
和
[MarshalAsAttribute (UnmanagedType.SafeArray, SafeArrayUserDefinedSubType=typeof(Header))]
但它不起作用。如果可能,我们如何从Main structure 获取数据?
【问题讨论】:
-
这不可能,pinvoke marshaller 无法将结构映射到结构数组。声明它 Header[] h 是必需的。毫无疑问,您现在会遇到该数组不是可预测的大小的问题,该 count 字段会带来麻烦。这也是不可能的,只有当您知道该字段的位置时,才能可靠地读取该字段。这取决于数组大小,主要的先有鸡还是先有蛋的问题。只有将 count 放在数组之前,它才能工作。
-
感谢您的回答。如果我将
count放在h之前,我会得到同样的异常。汉斯,因此,如果我理解正确的话,我们只能通过一些不受管理的包装器来完成这项工作? -
很抱歉,这里social.msdn.microsoft.com/Forums/vstudio/en-US/…问题已经解决了……
-
如果我将
count放在h之前的Main结构中并声明Header[] h而不是Header h我得到Access Violation Exception Attempted to read or write protected memory. This is often an indication that other memory is corrupt。也许我应该提供SizeConst属性。
标签: c# structure marshalling