использует неожиданное поведение FieldOffset - PullRequest
3 голосов
/ 25 февраля 2011

Я пытаюсь понять явное расположение структуры и наложение структуры, и я не вижу поведения, которое ожидаю.Учитывая код ниже:

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 ToString()
    {
        return string.Format("CharArray1: {0}\nCharArray2: {1}\nCharArray3: {2}",
           new string(CharArray1), new string(CharArray2), new string(CharArray3));
    }
}

Я ожидаю, что результат этого будет примерно таким:

IntField1: 1
IntField2: 2
CharArray: ABCDEF
SomeByte: 1
Подструктура:
{CharArray1: ABCDEF
CharArray2: ABCD
CharArray3: E}

, но результат равен:

IntField1: 1
IntField2: 2
CharArray: ABCD
SomeByte: 1
Подструктура:
{CharArray1: ABCD
CharArray2: ABCD
CharArray3: EF}

Почему CharArray в TestStruct имеет длину 4?Я предположил, что он должен иметь 6 символов ABCDEF, но он содержит только ABCD.То же самое для TestStruct2.CharArray1.

Ответы [ 3 ]

2 голосов
/ 07 марта 2011

char [] является ссылочным типом, его размер равен одному IntPtr, который может быть 4 или 8 байт - в зависимости от платформы (x86 или x64) и его значение не хранится на месте в структуре.

Атрибут MarshalAs не меняет способ хранения информации в структуре, а только способ ее перевода (например, в / из неуправляемого кода).

2 голосов
/ 25 февраля 2011

Поместив TestStruct2 после CharArray, но с тем же смещением, теперь указатель на CharArray2 TestStruct2 перезаписывает то, что раньше было указателем на собственный chararray TestStruct.

Если вы закомментируете или измените длину CharArray2 в TestStruct2, вы увидите ожидаемые результаты.

Точно так же смотрите, что происходит, когда вы сначала ставите struct2, т.е.

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

Теперь эффект полностью изменен, и CharArray2 TestStruct2 имеет длину шесть символов.

0 голосов
/ 07 марта 2011

Следует иметь в виду, что char в C # - это 2-байтовый символ Unicode.

Хотя я так и не смог получить ожидаемый результат, пока держу там подструктуру.Кажется, что перекрывающиеся массивы SizeConst все портят.

...