开发者

structs using FieldOffset unexpected behaviour

开发者 https://www.devze.com 2023-02-13 03:05 出处:网络
I am trying to understand explicit struct layout and struct overlaying and i am not seeing behaviour i expect.Given the code below:

I am trying to understand explicit struct layout and struct overlaying and i am not seeing behaviour i expect. Given the code below:

class Program
{

    static void Main(string[] args)
    {
        byte[] bytes = new byte[17];
        bytes[0] = 0x01; // Age is 1    //IntField1
        bytes[1] = 0x00;                //IntField1
        bytes[2] = 0x00;                //IntField1
        bytes[3] = 0x00;                //IntField1
        bytes[4] = 0x02;                //IntField2
        bytes[5] = 0x00;                //IntField2
        bytes[6] = 0x00;                //IntField2
        bytes[7] = 0x00;                //IntField2

        bytes[8] = 0x41;                //CharArray A
        bytes[9] = 0x42;                //CharArray B
        bytes[10] = 0x43;               //CharArray C
        bytes[11] = 0x44;               //CharArray D

        bytes[12] = 0x45;               //CharArray E

        bytes[13] = 0x46;               //CharArray F
        bytes[14] = 0x00; // \0 decimal 0
        bytes[15] = 0x00; // \0 decimal 0
        bytes[16] = 0x01; // 1 decimal 1
        Console.WriteLine(Marshal.SizeOf(typeof(TestStruct)));

        TestStruct testStruct2 = (TestStruct) RawDeserialize(bytes, 0, typeof (TestStruct));

        Console.WriteLine(testStruct2);
        Console.ReadLine();
    }
    public static object RawDeserialize( byte[] rawData, int position, Type anyType )
    {
        int rawsize = Marshal.SizeOf( anyType );
        if( rawsize > rawData.Length )
            return null;

        IntPtr buffer = Marshal.AllocHGlobal( rawsize );
        Marshal.Copy( rawData, position, buffer, rawsize );
        object retobj = Marshal.PtrToStructure( buffer, anyType );
        Marshal.FreeHGlobal( buffer );
        return retobj;
    }
}

[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct TestStruct
{
    [FieldOffset(0)]
    public int IntField1;
    [FieldOffset(4)]
    public int IntField2;
    [FieldOffset(8)]
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public char[] CharArray;
    [FieldOffset(16)]
    public byte SomeByte;        

    [FieldOffset(8)]
    public TestStruct2 SubStruct;

    public override string ToString()
    {
        return string.Format("IntField1: {0}\nIntField2: {1}\nCharArray: {2}\nSomeByte: {3}\nSubStruct:\n{{{4}}}", 
            IntField1, IntField2,  new string(CharArray), SomeByte, SubStruct);
    }
}

[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct TestStruct2
{
    [FieldOffset(0)]
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public char[] CharArray1;
    [FieldOffset(0)]
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public char[] CharArray2;
    [FieldOffset(4)]
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
    public char[] CharArray3;

    public override string ToStri开发者_C百科ng()
    {
        return string.Format("CharArray1: {0}\nCharArray2: {1}\nCharArray3: {2}",
           new string(CharArray1), new string(CharArray2), new string(CharArray3));
    }
}

I would expect the result from this to be something like:

IntField1: 1

IntField2: 2

CharArray: ABCDEF

SomeByte: 1

SubStruct:

{CharArray1: ABCDEF

CharArray2: ABCD

CharArray3: E }

but the result is:

IntField1: 1

IntField2: 2

CharArray: ABCD

SomeByte: 1

SubStruct:

{CharArray1: ABCD

CharArray2: ABCD

CharArray3: EF}

Why does the CharArray in the TestStruct have a length of 4? I epxected it to have 6 characters ABCDEF but it only contains ABCD. Same for the TestStruct2.CharArray1.


By putting TestStruct2 after CharArray but at the same offset, now the pointer to TestStruct2's CharArray2 is overwriting what used to be the pointer to TestStruct's own chararray.

If you comment out or change the length of TestStruct2's CharArray2 you'll see the expected results.

Similarly watch what happens when you put struct2 first, i.e.:

[StructLayout(LayoutKind.Explicit, Pack = 1)]
    public struct TestStruct
    {
        [FieldOffset(8)]
        public TestStruct2 SubStruct;

        [FieldOffset(0)]
        public int IntField1;
        [FieldOffset(4)]
        public int IntField2;
        [FieldOffset(8)]
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
        public char[] CharArray;
        [FieldOffset(16)]
        public byte SomeByte;        


        public override string ToString()
        {
            return string.Format("IntField1: {0}\nIntField2: {1}\nCharArray: {2}\nSomeByte: {3}\nSubStruct:\n{{{4}}}", 
                IntField1, IntField2,  new string(CharArray), SomeByte, SubStruct);
        }
    }

Now the effect is reversed and TestStruct2's CharArray2 is six characters long.


char[] is a reference type, its size is one IntPtr, which can be 4 or 8 Bytes - depending on platform (x86 or x64) and its value in not stored inplace within the structure.

The MarshalAs attribute does not change how the information is stored within the struct, only how it is translated (e.g. to/from unmanaged code).


One thing to keep in mind is that char in C# is 2 a byte unicode character.

Although I still couldn't get your expected result while keeping the substruct in there. The overlapping SizeConst arrays seems to mess things up.

0

精彩评论

暂无评论...
验证码 换一张
取 消