structs using FieldOffset unexpected behaviour
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.
精彩评论