none
Strange issue with LayoutKind.Explicit RRS feed

  • Question

  • Hello,

    I created an explicit structure as below:

    Code Snippet: Version A

    [StructLayout(LayoutKind.Explicit)]
    public struct ExplicitStruct
    {
        [FieldOffset(0)]
        public short mShort;
        [FieldOffset(2)]
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
        public byte[] mByteArray;
        [FieldOffset(8)]
        public long mLong;
    }


    IMHO, this should work perfectly.

    But when I use the above structure, I get type load exception saying "because it contains an object field at offset 2 that is incorrectly aligned or overlapped by a non-object field."

    I tried many things and finally came up with the below structure:
       
    Code Snippet: Version B

    [StructLayout(LayoutKind.Explicit)]
        public struct ExplicitStruct
        {
            [FieldOffset(0)]
            public short mShort;

    //notice the change in offset

            [FieldOffset(4)]
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
            public byte[] mByteArray;
            [FieldOffset(10)]
            public long mLong;
        }


    and now it works fine.

    My question is: Why do I need to start my byte array from offset '4', when a short type takes only 2 bytes. So what happens to the next two bytes (byte 3 and byte 4) after the short?

    Regards,
    Jayanta

    Monday, February 4, 2008 12:12 PM

Answers

  •  Jayanta Dey wrote:
    Hello,

    I created an explicit structure as below:

    Code Snippet: Version A

    [StructLayout(LayoutKind.Explicit)]
    public struct ExplicitStruct
    {
        [FieldOffset(0)]
        public short mShort;
        [FieldOffset(2)]
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
        public byte[] mByteArray;
        [FieldOffset(8)]
        public long mLong;
    }


    IMHO, this should work perfectly.

    But when I use the above structure, I get type load exception saying "because it contains an object field at offset 2 that is incorrectly aligned or overlapped by a non-object field."

    I tried many things and finally came up with the below structure:
       
    Code Snippet: Version B

    [StructLayout(LayoutKind.Explicit)]
        public struct ExplicitStruct
        {
            [FieldOffset(0)]
            public short mShort;

    //notice the change in offset

            [FieldOffset(4)]
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
            public byte[] mByteArray;
            [FieldOffset(10)]
            public long mLong;
        }


    and now it works fine.

    My question is: Why do I need to start my byte array from offset '4', when a short type takes only 2 bytes. So what happens to the next two bytes (byte 3 and byte 4) after the short?

    Regards,
    Jayanta

     

    OK.  I think I understand what's going on here.  It seems like the problem is related to the fact that the array type (which is an object type) must be stored at a 4-byte boundary in memory.  However, what you're really trying to do is serialize the 6 bytes separately.

     

    I think the problem is the mix between FieldOffset and serialization rules.  I'm thinking that structlayout.sequential may work for you, since it doesn't actually modify the in-memory representation of the structure.  I think FieldOffset is actually modifying the in-memory layout of the type.  This causes problems because the .NET framework requires object references to be aligned on appropriate boundaries (it seems).

     

     

    Wednesday, February 6, 2008 6:23 AM
  • The default behaviour in the framework is for values in the structure to be aligned at 4-byte boundaries, so values start at FieldOffset 0,4,8,12 ...

    Values that are smaller than 4 bytes (your short for example) get padded up to 4 bytes.


    You might have seen people use

    StructLayout(LayoutKind.Explicit, Pack:=1)

    This tells the framework to pack everything in to 1 byte boundaries, so there will be no padding.
    Wednesday, February 6, 2008 6:38 AM

All replies

  •  Jayanta Dey wrote:
    Hello,

    I created an explicit structure as below:

    Code Snippet: Version A

    [StructLayout(LayoutKind.Explicit)]
    public struct ExplicitStruct
    {
        [FieldOffset(0)]
        public short mShort;
        [FieldOffset(2)]
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
        public byte[] mByteArray;
        [FieldOffset(8)]
        public long mLong;
    }


    IMHO, this should work perfectly.

    But when I use the above structure, I get type load exception saying "because it contains an object field at offset 2 that is incorrectly aligned or overlapped by a non-object field."

    I tried many things and finally came up with the below structure:
       
    Code Snippet: Version B

    [StructLayout(LayoutKind.Explicit)]
        public struct ExplicitStruct
        {
            [FieldOffset(0)]
            public short mShort;

    //notice the change in offset

            [FieldOffset(4)]
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
            public byte[] mByteArray;
            [FieldOffset(10)]
            public long mLong;
        }


    and now it works fine.

    My question is: Why do I need to start my byte array from offset '4', when a short type takes only 2 bytes. So what happens to the next two bytes (byte 3 and byte 4) after the short?

    Regards,
    Jayanta

     

    OK.  I think I understand what's going on here.  It seems like the problem is related to the fact that the array type (which is an object type) must be stored at a 4-byte boundary in memory.  However, what you're really trying to do is serialize the 6 bytes separately.

     

    I think the problem is the mix between FieldOffset and serialization rules.  I'm thinking that structlayout.sequential may work for you, since it doesn't actually modify the in-memory representation of the structure.  I think FieldOffset is actually modifying the in-memory layout of the type.  This causes problems because the .NET framework requires object references to be aligned on appropriate boundaries (it seems).

     

     

    Wednesday, February 6, 2008 6:23 AM
  • The default behaviour in the framework is for values in the structure to be aligned at 4-byte boundaries, so values start at FieldOffset 0,4,8,12 ...

    Values that are smaller than 4 bytes (your short for example) get padded up to 4 bytes.


    You might have seen people use

    StructLayout(LayoutKind.Explicit, Pack:=1)

    This tells the framework to pack everything in to 1 byte boundaries, so there will be no padding.
    Wednesday, February 6, 2008 6:38 AM
  • The Pack field should be used when the LayoutKind.Sequential value is specified.
    Friday, February 8, 2008 3:04 AM
  • Thanks people!

     

    Ok, I understand what you guys are saying and you are right the offset has to be a multiple of 4 and I cannot use pack in explicit layout.

     

    Here is the situation, I have a union in C, which I want to map in C# (for marshalling). The C union is actually a union of two C structures with different data types (integers, shorts, char[], wchar_t[] with varying array sizes).

     

    So I am finding it difficult to map such a complex structure in C#. I believe to map a C union, I have to use explicit layout, but that offset padding (multiple of 4) is preventing me from creating an exact mapping.

     

    Any help in this regard will be very helpful.

     

    Regards,

    Jayanta

    Friday, February 8, 2008 11:18 AM
  • Not any kind of C structure can be exactly converted to a C# equivlent class. But there's a tool may be able to help you:
    P/Invoke Interop Assistant

    Thanks!


    Saturday, February 9, 2008 12:51 PM
  • Pack = 1 is not working for me I still get the alignment exception.

    You can get it to work by using other type of data members. Say use five ushort instead of an array of five ushort.

    You can also stick to sequential but that needs an extra struct and member name when dealing with unions.

    I was specifically trying to define HIDP_VALUE_CAPS.


    Sunday, February 15, 2015 12:21 PM