Возможно ли согласование 1: 1 структур C # с потоковыми записями Delphi?

Итак, у меня есть приложение Delphi, которое принимает записи различных типов, бросает их в поток памяти через stream.Write(record, sizeof(record)) и отправляет их по именованному каналу.

Возьмите эту запись Delphi:

Type TAboutData = record
  Version : array[0..4] of Byte;
  Build : Word;
  BuildDate : TDateTime;
  SVNChangeset : Word;
end;

Когда это отправляется по именованному каналу, это получается в массиве byte [] следующим образом:

Длина: 22 байта

0x06, 0x00, 0x00, 0x00, 4 байта для массива

0x00, 0x00, 0x00, 0x00, 2 байта для сборки, 2 байта для выравнивания?

0x15, 0xA3, 0x86, 0x3F, 8 байтов для двойного

0xBC, 0x44, 0xE4, 0x40,

0xA3, 0x02, 0x00, 0x00, 2 байта для SVNChangeSet, 2 байта выравнивания?

0x00, 0x00, 2 байта на что-то еще?

Вопросы по согласованию

  1. Я считаю, что это называется выравниванием в 4-байтовых границах, верно?
  2. Для чего нужны последние два байта?

Теперь я пытаюсь (безуспешно) упорядочить это в структуру C #.

    [StructLayout(LayoutKind.Sequential)]
    struct TAboutInfo
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public byte[] Version;
        public ushort Build;
        public double BuildDate;
        public ushort SVNChangeSet;
    }    

    IntPtr ptr = Marshal.AllocHGlobal(bytebuffer.Length);
    Marshal.Copy(ptr, bytebuffer, 0, bytebuffer.Length);
    TAboutInfo ta = (TAboutInfo)Marshal.PtrToStructure(ptr, typeof(TAboutInfo));
    Marshal.FreeHGlobal(ptr);

Вопросы по C #

  1. Это просто не работает, и я не могу понять, как учесть выравнивание. Я пробовал явные смещения, но не могу.
  2. У меня много типов записей, некоторые с членами, которые являются динамическими массивами других записей. Я бы предпочел найти надежное решение для преобразования этих байтовых массивов в структуры или объекты.

person Danny Rodriguez    schedule 31.08.2013    source источник
comment
Кстати, array[0..4] of Byte содержит 5 байтов, а не 4. Что такое SizeOf (запись) в Delphi? Я думаю, это 20, а не 22.   -  person nullptr    schedule 01.09.2013
comment
SizeOf (TAboutData) в Delphi 2007 и XE4 оба дают 24, что имеет смысл с заполнением.   -  person Ken White    schedule 01.09.2013


Ответы (2)


Выравнивание обычно используется компилятором в качестве оптимизации. На самом деле каждая структура дополняется до кратного 4 (или 8, я точно не помню).

Обновление. То, что я сказал выше о выравнивании, неточно. Прочтите ответ Дэвида, чтобы узнать, как компилятор обрабатывает выровненные записи. Статья Википедии содержит разумный обзор: http://en.wikipedia.org/wiki/Data_structure_alignment

В любом случае вы можете использовать параметр Pack, чтобы указать выравнивание. Значение Pack, равное 1, возвращает точный размер структуры.

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]

По поводу массивов правильно использовать:

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] someCharArray;

Просто позаботьтесь о том, чтобы при преобразовании из структуры в массив байтов массив должен соответствовать заявленному размеру. Перед преобразованием следует использовать Array.Resize, если содержимое короче.

struct.someCharArray = "Hello".ToCharArray();
Array.Resize<char>(ref struct.someCharArray, 20);

О маршалинге я использую этот метод из байта [] в структуру:

    public static T ParseStructure<T>(byte[] array, int offset) where T : struct
    {
        if (array == null) throw new ArgumentNullException("array", "Input parameter cannot be null");

        if (array.Length - offset < Marshal.SizeOf(typeof(T)))
            Array.Resize<byte>(ref array, Marshal.SizeOf(typeof(T)) + offset);

        int arraySize = array.Length - offset;
        T returnItem;

        // Allocate some unmanaged memory.
        IntPtr buffer = Marshal.AllocHGlobal(arraySize);

        // Copy the read byte array (byte[]) into the unmanaged memory block.
        Marshal.Copy(array, offset, buffer, arraySize);

        // Marshal the unmanaged memory block to a structure.
        returnItem = (T)Marshal.PtrToStructure(buffer, typeof(T));

        // Free the unmanaged memory block.
        Marshal.FreeHGlobal(buffer);

        return returnItem;
 }

А этот метод делает наоборот:

    public static byte[] StructureToArray<T>(T structure) where T : struct
    {
        int objectSize = Marshal.SizeOf(structure);
        byte[] result = new byte[objectSize];
        IntPtr buffer = Marshal.AllocHGlobal(objectSize);

        object dataStructure = (object)structure;

        Marshal.StructureToPtr(dataStructure, buffer, true);

        Marshal.Copy(buffer, result, 0, objectSize);
        Marshal.FreeHGlobal(buffer);
        return result;
    }

Кроме того, используйте следующий код, чтобы проверить размер, рассчитанный платформой:

int objectSize = Marshal.SizeOf(structure);

Наконец, я нашел эту прекрасную статью о маршалинге.

person Community    schedule 31.08.2013
comment
Клянусь, я как раз был на грани понимания этого, когда ваш Оазис ответа закончил за меня работу. СПАСИБО МНОГО! Ты мужчина! - person Danny Rodriguez; 01.09.2013
comment
Нет необходимости в Pack = 1. Вы не должны делать это без причины. Итак, почему? А у вас первый абзац просто неверный. Проверьте размер структуры, содержащей один байт. - person David Heffernan; 01.09.2013
comment
Хорошо иметь полную картину, согласен. Но ваш ответ не так. Начните с первого абзаца, который совершенно неверен. - person David Heffernan; 02.09.2013
comment
Я прекрасно понимаю, а то, что вы говорите, совершенно неверно. Я объяснил, как и в своем ответе описал выравнивание. Вы сказали: На самом деле каждая структура дополняется до кратного 4 (или 8, я точно не помню), что абсолютно неверно. - person David Heffernan; 02.09.2013

Я собираюсь предположить, что ваш компилятор Delphi работает в режиме по умолчанию и выравнивает записи. Я также предполагаю, что ваш код на Паскале содержит простую опечатку, когда в вашем массиве 5 элементов вместо 4.

Выравнивание выровненной записи определяется членом с наибольшим выравниванием. Самый большой член - это 8-байтовый двойной. Таким образом, запись имеет выравнивание 8. Ее размер является точным кратным ее выравниванию.

Каждое отдельное поле выравнивается, чтобы соответствовать выравниванию поля. Массив байтов имеет выравнивание 1 и может располагаться где угодно. 2-байтовые целые числа должны располагаться на 2-байтовых границах и т. Д. Записи могут иметь заполнение в конце, чтобы гарантировать, что массивы записи также будут выровнены. Итак, запись имеет размер, кратный 8. Ваша запись имеет размер 24.

В вашем случае заполнение перед 8-байтовым двойником и в конце записи. Если бы это дополнение в конце не было включено, 8-байтовый двойник будет неправильно выровнен в любых массивах вашей записи.

Нет необходимости вносить какие-либо изменения в ваши записи Delphi и объявления структур C #. Они уже заявлены правильно и идеально подходят друг другу.

Вы можете использовать SizeOf в Delphi и Marshal.SizeOf в C #, чтобы проверить размер ваших структур. Вы обнаружите, что они совпадают с кодом в вопросе.

Я не могу комментировать, как ваш код не удался, потому что вы не описали этот сбой. Моя основная мысль заключается в том, что любой сбой происходит не из-за несоответствия между вашими структурами. Нет никакого рационального объяснения источника числа 22. Я бы посмотрел, откуда это число.

Наконец, ответ, который вы приняли, предполагает использование упакованных структур. В этом нет необходимости, я не вижу причин для этого, и это не объясняет ваших проблем.

person David Heffernan    schedule 01.09.2013
comment
Размер 22 - это длина пакета, который я получаю от Delphi. TAboutData заполняется, затем отправляется через TPipeServer.SendStream () [он не сериализуется, он просто считывается в поток байтов]. Похоже, что по какой-то причине не хватает последних двух байтов? Приложение Delphi - это приложение с закрытым исходным кодом, к которому у меня нет доступа. Я должен был прокомментировать, что параметр Pack не нужен. Я был просто рад, что у меня появилась альтернатива StructureToPtr (), которая действительно работала. - person Danny Rodriguez; 01.09.2013
comment
Я не вижу альтернативы. PtrToStructure - это все. - person David Heffernan; 01.09.2013
comment
Извините, реализация не альтернативы - person Danny Rodriguez; 01.09.2013
comment
Настоящая проблема, с которой я столкнулся с принятым вами ответом, - это грубая фактическая ошибка в первом абзаце. Вы спросили о согласованности и согласились, и ответили, что это совершенно неверно по этому поводу. - person David Heffernan; 01.09.2013
comment
@ dna2 проблема в том, что вы, кажется, не понимаете выравнивания - person David Heffernan; 02.09.2013
comment
Дэвид, я здесь не для того, чтобы учиться, вы можете просто отметить мой ответ (который я редактировал уже 10 раз), а это все, что требуется здесь, на SO. С Уважением - person Dan; 02.09.2013
comment
@ dna2 Не для того, чтобы учиться? Отлично. Я мог бы научить вас без проблем, но если вы не хотите учиться. - person David Heffernan; 02.09.2013
comment
Как насчет того, чтобы научиться сетевому этикету? Я принимаю критику, но потребовалось 3/4 сообщения, чтобы получить от вас то, что вы сочли неправильным. Легко сказать «это неправильно, это неправильно» или «проблема в том, что вы, кажется, не понимаете выравнивания» ... это не помогает мне улучшить ответ ... и, кстати, ваше поведение действительно агрессивен. Нет новостей. Я занял оборонительную позицию. Я с удовольствием уберу свой ответ, но просто не могу. Вы хотите, чтобы я сам отметил это? Успокоит ли это твой гнев на меня? - person Dan; 02.09.2013
comment
Также добавлена ​​вики Сообщества. Если я понимаю, для чего она нужна, теперь вы сможете свободно ее редактировать. - person Dan; 02.09.2013
comment
@ dna2 Мне не нужна вики сообщества, чтобы редактировать ваш ответ. У меня достаточно репутации, чтобы ее отредактировать. Но я не особо хочу полностью переписывать ответ. Потому что тогда это было бы похоже на мое. И этикет здесь требует, чтобы вы не переписывали принятые ответы. Сразу объяснил, что не так. Не в комментарии, а в моем ответе. Я добавил свой ответ только для того, чтобы исправить ваши недостатки. Мне не нравится, если принятый ответ в корне неверен. Я уверен, тебе это тоже не нравится. Вы кажетесь мне очень благородным, и я действительно не пытаюсь быть агрессивным. - person David Heffernan; 02.09.2013