none
Marshalling nested structs RRS feed

  • Question

  • 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 fieldMsgId. 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?






    Thursday, November 28, 2013 6:25 AM

Answers

  • I don't understand why you are using this:

    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct)]

    This is intended to be used with array fields, your h field is not an array. Just remove the attribute and it should work fine.

    Friday, November 29, 2013 10:02 AM
    Moderator

All replies