Как преобразовать структуру в массив байтов в C #?

Как преобразовать структуру в массив байтов в C #?

Я определил такую ​​структуру:

public struct CIFSPacket { public uint protocolIdentifier; //The value must be "0xFF+'SMB'". public byte command; public byte errorClass; public byte reserved; public ushort error; public byte flags; //Here there are 14 bytes of data which is used differently among different dialects. //I do want the flags2. However, so I'll try parsing them. public ushort flags2; public ushort treeId; public ushort processId; public ushort userId; public ushort multiplexId; //Trans request public byte wordCount;//Count of parameter words defining the data portion of the packet. //From here it might be undefined... public int parametersStartIndex; public ushort byteCount; //Buffer length public int bufferStartIndex; public string Buffer; } 

В моем основном методе я создаю его экземпляр и присваиваю ему значения:

 CIFSPacket packet = new CIFSPacket(); packet.protocolIdentifier = 0xff; packet.command = (byte)CommandTypes.SMB_COM_NEGOTIATE; packet.errorClass = 0xff; packet.error = 0; packet.flags = 0x00; packet.flags2 = 0x0001; packet.multiplexId = 22; packet.wordCount = 0; packet.byteCount = 119; packet.Buffer = "NT LM 0.12"; 

Теперь я хочу отправить этот пакет через сокет. Для этого мне нужно преобразовать структуру в массив байтов. Как мне это сделать?

Мой полный код выглядит следующим образом.

 static void Main(string[] args) { Socket MyPing = new Socket(AddressFamily.InterNetwork, SocketType.Stream , ProtocolType.Unspecified ) ; MyPing.Connect("172.24.18.240", 139); //Fake an IP Address so I can send with SendTo IPAddress IP = new IPAddress(new byte[] { 172,24,18,240 }); IPEndPoint IPEP = new IPEndPoint(IP, 139); //Local IP for Receiving IPEndPoint Local = new IPEndPoint(IPAddress.Any, 0); EndPoint EP = (EndPoint)Local; CIFSPacket packet = new CIFSPacket(); packet.protocolIdentifier = 0xff; packet.command = (byte)CommandTypes.SMB_COM_NEGOTIATE; packet.errorClass = 0xff; packet.error = 0; packet.flags = 0x00; packet.flags2 = 0x0001; packet.multiplexId = 22; packet.wordCount = 0; packet.byteCount = 119; packet.Buffer = "NT LM 0.12"; MyPing.SendTo(It takes byte array as parameter); } 

Каким будет fragment кода?

Это довольно просто, используя сортировку.

Верх файла

 using System.Runtime.InteropServices 

функция

 byte[] getBytes(CIFSPacket str) { int size = Marshal.SizeOf(str); byte[] arr = new byte[size]; IntPtr ptr = Marshal.AllocHGlobal(size); Marshal.StructureToPtr(str, ptr, true); Marshal.Copy(ptr, arr, 0, size); Marshal.FreeHGlobal(ptr); return arr; } 

И чтобы преобразовать его обратно:

 CIFSPacket fromBytes(byte[] arr) { CIFSPacket str = new CIFSPacket(); int size = Marshal.SizeOf(str); IntPtr ptr = Marshal.AllocHGlobal(size); Marshal.Copy(arr, 0, ptr, size); str = (CIFSPacket)Marshal.PtrToStructure(ptr, str.GetType()); Marshal.FreeHGlobal(ptr); return str; } 

В вашей структуре вам нужно будет поставить это перед строкой

 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)] public string Buffer; 

И убедитесь, что SizeConst такой же большой, как ваша самая большая возможная строка.

И вы должны, вероятно, прочитать это: http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

Если вы действительно хотите, чтобы это было FAST, вы можете сделать это, используя небезопасный код с CopyMemory. CopyMemory примерно в 5 раз быстрее (например, 800 Мбайт данных занимает 3 с для копирования через маршаллинг, а только для копирования с помощью CopyMemory .6s). Этот метод ограничивает использование только данных, которые фактически хранятся в самой структуре struct blob, например, числах или массивах байт с фиксированной длиной.

  [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)] private static unsafe extern void CopyMemory(void *dest, void *src, int count); private static unsafe byte[] Serialize(TestStruct[] index) { var buffer = new byte[Marshal.SizeOf(typeof(TestStruct)) * index.Length]; fixed (void* d = &buffer[0]) { fixed (void* s = &index[0]) { CopyMemory(d, s, buffer.Length); } } return buffer; } 

Посмотрите на эти методы:

 byte [] StructureToByteArray(object obj) { int len = Marshal.SizeOf(obj); byte [] arr = new byte[len]; IntPtr ptr = Marshal.AllocHGlobal(len); Marshal.StructureToPtr(obj, ptr, true); Marshal.Copy(ptr, arr, 0, len); Marshal.FreeHGlobal(ptr); return arr; } void ByteArrayToStructure(byte [] bytearray, ref object obj) { int len = Marshal.SizeOf(obj); IntPtr i = Marshal.AllocHGlobal(len); Marshal.Copy(bytearray,0, i,len); obj = Marshal.PtrToStructure(i, obj.GetType()); Marshal.FreeHGlobal(i); } 

Это бесстыдная копия другого streamа, который я нашел в Google!

Обновление : для получения дополнительной информации проверьте источник

Вариант кода Vicent с одним меньшим распределением памяти:

 public static byte[] GetBytes(T str) { int size = Marshal.SizeOf(str); byte[] arr = new byte[size]; GCHandle h = default(GCHandle); try { h = GCHandle.Alloc(arr, GCHandleType.Pinned); Marshal.StructureToPtr(str, h.AddrOfPinnedObject(), false); } finally { if (h.IsAllocated) { h.Free(); } } return arr; } public static T FromBytes(byte[] arr) where T : struct { T str = default(T); GCHandle h = default(GCHandle); try { h = GCHandle.Alloc(arr, GCHandleType.Pinned); str = Marshal.PtrToStructure(h.AddrOfPinnedObject()); } finally { if (h.IsAllocated) { h.Free(); } } return str; } 

Я использую GCHandle для « GCHandle » памяти, а затем я использую непосредственно ее адрес с h.AddrOfPinnedObject() .

В качестве основного ответа используется тип CIFSPacket, который не является (или больше не доступен) на C #, я написал правильные методы:

  static byte[] getBytes(object str) { int size = Marshal.SizeOf(str); byte[] arr = new byte[size]; IntPtr ptr = Marshal.AllocHGlobal(size); Marshal.StructureToPtr(str, ptr, true); Marshal.Copy(ptr, arr, 0, size); Marshal.FreeHGlobal(ptr); return arr; } static T fromBytes(byte[] arr) { T str = default(T); int size = Marshal.SizeOf(str); IntPtr ptr = Marshal.AllocHGlobal(size); Marshal.Copy(arr, 0, ptr, size); str = (T)Marshal.PtrToStructure(ptr, str.GetType()); Marshal.FreeHGlobal(ptr); return str; } 

Протестированные, они работают.

Вы можете использовать маршал (StructureToPtr, ptrToStructure) и Marshal.copy, но это зависит от формы.


Сериализация включает функции для пользовательской сериализации.

 public virtual void GetObjectData(SerializationInfo info, StreamingContext context) Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext) 

SerializationInfo включает функции для сериализации каждого члена.


BinaryWriter и BinaryReader также содержат методы Save / Load to Byte Array (Stream).

Обратите внимание, что вы можете создать MemoryStream из байт-массива или байт-массива из MemoryStream.

Вы можете создать метод Сохранить и метод Новый в вашей структуре:

  Save(Bw as BinaryWriter) New (Br as BinaryReader) 

Затем вы выбираете элементы для сохранения / загрузки в stream -> Байт-массив.

Это можно сделать очень просто.

Определите свою структуру явно с помощью [StructLayout(LayoutKind.Explicit)]

 int size = list.GetLength(0); IntPtr addr = Marshal.AllocHGlobal(size * sizeof(DataStruct)); DataStruct *ptrBuffer = (DataStruct*)addr; foreach (DataStruct ds in list) { *ptrBuffer = ds; ptrBuffer += 1; } 

Этот код может быть написан только в небезопасном контексте. Вы должны освободить addr когда закончите с ним.

 Marshal.FreeHGlobal(addr); 

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

Вот пример struct :

 [StructLayout(LayoutKind.Sequential)] public class HelloWorld { public MyEnum enumvalue; public string reqtimestamp; public string resptimestamp; public string message; public byte[] rawresp; } 

Как вы можете видеть, для всех этих структур потребуется добавить атрибуты фиксированной длины. Который часто мог занять больше места, чем требовалось. Обратите внимание, что LayoutKind.Sequential требуется, так как мы хотим, чтобы reflection всегда FieldInfo нам тот же порядок при FieldInfo . Мое вдохновение – от TLV Type-Length-Value. Давайте посмотрим на код:

 public static byte[] StructToByteArray(T obj) { using (MemoryStream ms = new MemoryStream()) { FieldInfo[] infos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance); foreach (FieldInfo info in infos) { BinaryFormatter bf = new BinaryFormatter(); using (MemoryStream inms = new MemoryStream()) { bf.Serialize(inms, info.GetValue(obj)); byte[] ba = inms.ToArray(); // for length ms.Write(BitConverter.GetBytes(ba.Length), 0, sizeof(int)); // for value ms.Write(ba, 0, ba.Length); } } return ms.ToArray(); } } 

Вышеупомянутая функция просто использует BinaryFormatter для сериализации необработанного необработанного object , и я просто отслеживаю его размер и сохраняю его внутри выходного MemoryStream .

 public static void ByteArrayToStruct(byte[] data, out T output) { output = (T) Activator.CreateInstance(typeof(T), null); using (MemoryStream ms = new MemoryStream(data)) { byte[] ba = null; FieldInfo[] infos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance); foreach (FieldInfo info in infos) { // for length ba = new byte[sizeof(int)]; ms.Read(ba, 0, sizeof(int)); // for value int sz = BitConverter.ToInt32(ba, 0); ba = new byte[sz]; ms.Read(ba, 0, sz); BinaryFormatter bf = new BinaryFormatter(); using (MemoryStream inms = new MemoryStream(ba)) { info.SetValue(output, bf.Deserialize(inms)); } } } } 

Когда мы хотим преобразовать его обратно в свою исходную struct мы просто прочитываем длину назад и непосредственно выгружаем ее обратно в BinaryFormatter который, в свою очередь, возвращает ее обратно в struct .

Эти 2 функции являются общими и должны работать с любой struct , я протестировал вышеуказанный код в моем проекте C# где у меня есть сервер и клиент, подключенный и связанный через NamedPipeStream и я пересылаю свою struct как массив байтов от одного и к другому и преобразовал его обратно.

Я считаю, что мой подход может быть лучше, поскольку он не фиксирует длину самой struct , и только накладные расходы – это всего лишь int для каждого поля, который у вас есть в вашей структуре. Внутри байтового массива, генерируемого BinaryFormatter , есть также немного небольших надбавок, но кроме этого, это немного.

Я бы посмотрел на classы BinaryReader и BinaryWriter. Недавно мне пришлось сериализовать данные в массив байтов (и обратно), и я нашел эти classы только после того, как сам переписал их.

http://msdn.microsoft.com/en-us/library/system.io.binarywriter.aspx

На этой странице также есть хороший пример.

Похож на предопределенную (C-уровень) структуру для некоторой внешней библиотеки. Маршал – ваш друг. Проверьте:

http://geekswithblogs.net/taylorrich/archive/2006/08/21/88665.aspx

для стартера, как с этим бороться. Обратите внимание, что вы можете – с атрибутами – определять такие вещи, как макет макета и обработка строк. ОЧЕНЬ хороший подход, на самом деле.

Для этого не используются BinaryFormatter Nor MemoryStream.

@Abdel Olakara ответить donese не работать в .net 3.5, следует изменить следующим образом:

  public static void ByteArrayToStructure(byte[] bytearray, ref T obj) { int len = Marshal.SizeOf(obj); IntPtr i = Marshal.AllocHGlobal(len); Marshal.Copy(bytearray, 0, i, len); obj = (T)Marshal.PtrToStructure(i, typeof(T)); Marshal.FreeHGlobal(i); } 
  Header header = new Header(); Byte[] headerBytes = new Byte[Marshal.SizeOf(header)]; Marshal.Copy((IntPtr)(&header), headerBytes, 0, headerBytes.Length); 

Это должно сделать трюк быстро, не так ли?

Этот пример применим только к чистым типам blittable, например, типам, которые могут быть memcpy’d непосредственно в C.

Пример – хорошо известная 64-битная структура

 [StructLayout(LayoutKind.Sequential)] public struct Voxel { public ushort m_id; public byte m_red, m_green, m_blue, m_alpha, m_matid, m_custom; } 

Определенный точно так же, структура будет автоматически упакована как 64-разрядная.

Теперь мы можем создать объем вокселей:

 Voxel[,,] voxels = new Voxel[16,16,16]; 

И сохраните их все в массив байтов:

 int size = voxels.Length * 8; // Well known size: 64 bits byte[] saved = new byte[size]; GCHandle h = GCHandle.Alloc(voxels, GCHandleType.Pinned); Marshal.Copy(h.AddrOfPinnedObject(), saved, 0, size); h.Free(); // now feel free to save 'saved' to a File / memory stream. 

Однако, поскольку OP хочет знать, как преобразовать структуру, наша структура Voxel может иметь следующий метод ToBytes :

 byte[] bytes = new byte[8]; // Well known size: 64 bits GCHandle h = GCHandle.Alloc(this, GCHandleType.Pinned); Marshal.Copy(hh.AddrOfPinnedObject(), bytes, 0, 8); h.Free(); 
  • Как определить структуру typedef, содержащую указатели на себя?
  • Обоснование макроса container_of в linux / list.h
  • Как включить динамический массив INSIDE a struct в C?
  • Структура массивов по сравнению с массивом структур в CUDA
  • Как скомпилировать C-код с анонимными структурами / объединениями?
  • Копирование структуры, содержащей указатели на устройство CUDA
  • Какая польза для тегов в Go?
  • Почему я не могу определить конструктор по умолчанию для структуры в .NET?
  • bds 2006 C конфликты с скрытым диспетчером памяти (class new / delete vs. AnsiString)
  • C: указатель на структуру в определении структуры
  • Копирование одной структуры в другую
  • Давайте будем гением компьютера.