Как преобразовать структуру в массив байтов в 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);
}
Каким должен быть фрагмент кода?
Ответы:
Это довольно просто, используя маршалинг.
Начало файла
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
источник
Marshal.StructureToPtr(str, ptr, false);
. Но нужно упомянуть, что я использую функции, обернутые в универсальный, хотя…Если вы действительно хотите, чтобы это было БЫСТРО в Windows, вы можете сделать это, используя небезопасный код с CopyMemory. CopyMemory примерно в 5 раз быстрее (например, для копирования 800 МБ данных с помощью маршаллинга требуется 3 секунды, а для копирования с помощью CopyMemory требуется всего 0,6 секунды). Этот метод ограничивает использование только данных, которые фактически хранятся в самой структуре 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); }
Это бесстыдная копия другой ветки, которую я нашел в Google!
Обновление : для получения дополнительной информации проверьте источник
источник
Вариант кода Vicent с выделением памяти на единицу меньше:
public static byte[] GetBytes<T>(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<T>(str, h.AddrOfPinnedObject(), false); } finally { if (h.IsAllocated) { h.Free(); } } return arr; } public static T FromBytes<T>(byte[] arr) where T : struct { T str = default(T); GCHandle h = default(GCHandle); try { h = GCHandle.Alloc(arr, GCHandleType.Pinned); str = Marshal.PtrToStructure<T>(h.AddrOfPinnedObject()); } finally { if (h.IsAllocated) { h.Free(); } } return str; }
Я использую
GCHandle
для «закрепления» памяти, а затем использую напрямую ее адресh.AddrOfPinnedObject()
.источник
where T : struct
противном случае будет жалоба на то,T
что он не прошелnon-nullable type
.GCHandle.Alloc
завершится ошибкой, если структура содержит непреобразуемые данные, например массивstring
.Поскольку основной ответ заключается в использовании типа 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<T>(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; }
Проверено, работают.
источник
Я знаю, что это действительно поздно, но с C # 7.3 вы можете сделать это для неуправляемых структур или чего-то еще, что не связано (int, bool и т. Д.):
public static unsafe byte[] ConvertToBytes<T>(T value) where T : unmanaged { byte* pointer = (byte*)&value; byte[] bytes = new byte[sizeof(T)]; for (int i = 0; i < sizeof(T); i++) { bytes[i] = pointer[i]; } return bytes; }
Затем используйте вот так:
struct MyStruct { public int Value1; public int Value2; //.. blah blah blah } byte[] bytes = ConvertToBytes(new MyStruct());
источник
Вы можете использовать Marshal (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 также содержат методы для сохранения / загрузки в массив байтов (поток).
Обратите внимание, что вы можете создать MemoryStream из байтового массива или байтового массива из MemoryStream.
Вы можете создать в своей структуре метод Save и метод New:
Save(Bw as BinaryWriter) New (Br as BinaryReader)
Затем вы выбираете элементы для сохранения / загрузки в поток -> байтовый массив.
источник
Это можно сделать очень просто.
Определите вашу структуру явно с помощью
[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
когда закончите с этим.источник
Я придумал другой подход, который мог бы преобразовать любой
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
требуется, поскольку мы хотим, чтобы отражение всегда давало нам один и тот же порядок при извлеченииFieldInfo
. Мое вдохновение - отTLV
Type-Length-Value. Посмотрим на код:public static byte[] StructToByteArray<T>(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<T>(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
, также есть небольшие накладные расходы , но в остальном их не так много.источник
Я бы посмотрел на классы BinaryReader и BinaryWriter. Недавно мне пришлось сериализовать данные в байтовый массив (и обратно), и я нашел эти классы только после того, как я в основном их переписал сам.
http://msdn.microsoft.com/en-us/library/system.io.binarywriter.aspx
На этой странице тоже есть хороший пример.
источник
Похоже на предопределенную структуру (уровень C) для какой-то внешней библиотеки. Маршал - твой друг. Проверьте:
http://geekswithblogs.net/taylorrich/archive/2006/08/21/88665.aspx
для начала как с этим бороться. Обратите внимание, что с помощью атрибутов вы можете определять такие вещи, как разметка байтов и обработка строк. ОЧЕНЬ хороший подход, на самом деле.
Ни BinaryFormatter, ни MemoryStream для этого не предназначены.
источник
Ответ @Abdel Olakara не работает в .net 3.5, его следует изменить, как показано ниже:
public static void ByteArrayToStructure<T>(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);
Это должно помочь быстро, не так ли?
источник
Этот пример применим только к чистым непреобразуемым типам, например, к типам, которые могут быть запомнены непосредственно в 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();
источник