Преобразовать структуру переменной длины в byte [] - PullRequest
2 голосов
/ 17 сентября 2010

Я пытаюсь отправить сетевой пакет данных на аппаратное устройство. Я хотел бы использовать гладкий, объектно-ориентированный подход, чтобы я мог работать с ним на высоком уровне. Пакет имеет несколько полей переменной длины. Очевидно, что структура байтов очень специфична.

Вот структура, представляющая пакет, который мне нужно отправить:

[StructLayout(LayoutKind.Sequential)]
public struct Packet
{
   public UInt16 Instruction;
   public UInt16 Length; //length of data field
   public UInt32 SessionHandle;

   [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
   public byte[] SenderContext;

   [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
   public byte[] MessageData;
}

MessageData[] может быть любой длины, я фиксирую это как 8 байт в качестве стартера.

Вот моя попытка создать byte[] для отправки через Socket:

public static byte[] ToBytes(Packet ep)
{
   Byte[] bytes = new Byte[Marshal.SizeOf(typeof(Packet))];
   GCHandle pinStructure = GCHandle.Alloc(ep, GCHandleType.Pinned);
   try
   {
      Marshal.Copy(pinStructure.AddrOfPinnedObject(), bytes, 0, bytes.Length);
      return bytes;
   }
   finally
   {
      pinStructure.Free();
   }
}

Но я получаю:

ArgumentException : Object contains non-primitive or non-blittable data.

Я думал, что установка SizeConst в структуре позаботится об этом? В любом случае, я более потерян, чем это, потому что аппаратное устройство ожидает пакет переменной длины, и я хотел бы воспользоваться этим.

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

Есть идеи?

Ответы [ 2 ]

2 голосов
/ 17 сентября 2010

CLR не позволит вам использовать структуру, имеющую поле, которое перекрывает ссылочный тип.Два массива в вашем случае.Он совершенно несовместим с сборщиком мусора, он не может отслеживать ссылку на объект.И это было бы довольно небезопасно, так как он допускает бэкдор в кучу, напрямую манипулируя значением ссылки на объект.

Нет смысла делать это в вашем конкретном случае, поскольку оба массива перекрываются и имеют одинаковый размер.Один справится с работой просто отлично.

0 голосов
/ 17 сентября 2010

Проблема в том, что массивы не являются близаемыми и не упаковываются в структуру.Они являются ссылочными типами в конце концов.Таким образом, структура на самом деле просто содержит ссылку на объект массива, который находится в куче.Насколько мне известно, атрибут MarshalAs не влияет на расположение элементов.Это влияет только на то, как элементы маршалируются во время процедур взаимодействия.

Другое замечание, которое у меня есть, заключается в том, что вы фактически закрепляете коробочную версию структуры, а не фактическую структуру.Тип значения, конечно, живет в стеке, поэтому нет необходимости его закреплять.Возможно, вы уже знали это, и именно этот метод вы выбрали для извлечения IntPtr из него.Это хорошо, но вы могли бы более эффективно и целесообразно выполнить то же самое, используя код unsafe или метод Marshal.StructureToPtr.

Я думаю, что вам лучше всего создать отдельный массив byte и использоватьArray.Copy метод и BitConverter класс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...