Marshalling nested structs to C#

时间秒杀一切 提交于 2020-01-15 03:46:07

问题


We have COM Server (as dll, we have only dll file), that implements two COM interfaces. One of interfaces allows us to get messages (as structure) from some device. Depending on message we need to process corresponding structure. Every structure is pointer to a structure VARIANT. This structure is of type byte array (VT_ARRAY | VT_UI1).

All structures contains structure Header and some other information. Structure Header contains field MsgId. Depending on MsgId we need to process other structures.

In order to get structures from dll to c# we use reflection.

Now, it's time for example:

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

Every 100 ms we get data from device via reflection:

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

Then we cast our msg as byte[] and then we need to convert array of bytes into our struct. At this place we have some problem with marshaling.

As i have wrote in order to define which structure we need to process (another words we need to marshaling) first of all we need to marshal Header structure that is contained in all structures (another words in all messages from device).

In order to marshal Header struct we use the method proposed in C# array within a struct (with some editing):

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

How we are using one:

 private void ProcessMessage(byte[] message)
 {
     Header msgHeader = new Header();
     msgHeader = DeserializeMsg<Header>(message);
 }

Everything is OK! Here we get msgHeader.MsgId and then we need to get another structure:

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

Here we get exception: Cannot marshal field 'h' of type 'Main'. Invalid managed/unmanaged type combination (this value type must be paired with Struct)

We have tried to change MarshalAsAttribute declaration of inner struct h to

[MarshalAsAttribute(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_SAFEARRAY)]

and

[MarshalAsAttribute (UnmanagedType.SafeArray, SafeArrayUserDefinedSubType=typeof(Header))]

but it does not work. How can we get data from Main strucure if it is possible?


回答1:


Byte by byte reading via BinaryReading is my solution now. Everything work fine. Thanks to everyone!



来源:https://stackoverflow.com/questions/20246459/marshalling-nested-structs-to-c-sharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!