【问题标题】:Marshalling nested structs to C#将嵌套结构编组到 C#
【发布时间】: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 配对)

我们尝试将内部结构hMarshalAsAttribute 声明更改为

[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


【解决方案1】:

逐字节通过BinaryReading 阅读是我现在的解决方案。一切正常。谢谢大家!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-01
    • 2021-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-09
    • 2010-12-17
    相关资源
    最近更新 更多