¿Cómo convertir una estructura a una matriz de bytes en C #?

¿Cómo convierto una estructura en una matriz de bytes en C #?

Definí una estructura como esta:

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

En mi método principal, creo una instancia y le asigno valores:

 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"; 

Ahora quiero enviar este paquete por socket. Para eso, necesito convertir la estructura a una matriz de bytes. ¿Cómo puedo hacerlo?

Mi código completo es el siguiente.

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

¿Qué sería un fragmento de código?

Esto es bastante fácil, usando Marshalling.

Parte superior del archivo

 using System.Runtime.InteropServices 

Función

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

Y para convertirlo de nuevo:

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

En su estructura, tendrá que poner esto antes de una cadena

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

Y asegúrese de que SizeConst sea tan grande como su cadena más grande posible.

Y probablemente debería leer esto: http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

Si realmente quieres que sea RÁPIDO, puedes hacerlo usando un código inseguro con CopyMemory. CopyMemory es aproximadamente 5 veces más rápido (por ejemplo, 800MB de datos tardan 3s en copiarse mediante clasificación, mientras que solo toman .6s para copiar a través de CopyMemory). Este método lo limita a usar solo datos que en realidad están almacenados en el blob struct mismo, por ejemplo, números o arrays de bytes de longitud fija.

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

Eche un vistazo a estos métodos:

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

¡Esta es una copia desvergonzada de otro hilo que encontré en Google!

Actualización : para más detalles, verifique la fuente

Variante del código de Vicent con una asignación de memoria menor:

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

Uso GCHandle para “fijar” la memoria y luego uso directamente su dirección con h.AddrOfPinnedObject() .

Como la respuesta principal es usar el tipo de CIFSPacket, que no está (o ya no está) disponible en C #, escribí los métodos correctos:

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

Probado, ellos trabajan.

Puede usar Marshal (StructureToPtr, ptrToStructure) y Marshal.copy, pero esto depende de la plataforma.


La serialización incluye funciones para la serialización personalizada.

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

SerializationInfo incluye funciones para serializar cada miembro.


BinaryWriter y BinaryReader también contienen métodos para Guardar / Cargar a Byte Array (Stream).

Tenga en cuenta que puede crear un MemoryStream desde una matriz de bytes o una matriz de bytes desde un MemoryStream.

Puede crear un método Guardar y un método Nuevo en su estructura:

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

Luego, seleccione miembros para Guardar / Cargar en secuencia -> Matriz de bytes.

Esto puede hacerse de manera muy simple.

Defina su estructura explícitamente con [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; } 

Este código solo se puede escribir en un contexto inseguro. Tienes que liberar addr cuando hayas terminado.

 Marshal.FreeHGlobal(addr); 

He llegado a un enfoque diferente que podría convertir cualquier struct sin la molestia de fijar la longitud, sin embargo, la matriz de bytes resultante tendría un poco más de sobrecarga.

Aquí hay una struct muestra:

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

Como puede ver, todas esas estructuras requerirían agregar los atributos de longitud fija. Que a menudo terminaba ocupando más espacio de lo requerido. Tenga en cuenta que se requiere LayoutKind.Sequential , ya que queremos que la reflexión siempre nos dé el mismo orden cuando se tira de FieldInfo . Mi inspiración es de TLV Type-Length-Value. Echemos un vistazo al código:

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

La función anterior simplemente utiliza BinaryFormatter para serializar el object sin formato de tamaño desconocido, y simplemente también hago un seguimiento del tamaño y lo guardo dentro de MemoryStream salida.

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

Cuando queremos convertirlo de nuevo a su struct original, simplemente leemos la longitud hacia atrás y BinaryFormatter directamente en el BinaryFormatter que a su vez lo volvemos a convertir en la struct .

Estas 2 funciones son genéricas y deberían funcionar con cualquier struct , he probado el código anterior en mi proyecto C# donde tengo un servidor y un cliente, conectado y comunicado a través de NamedPipeStream y reenvío mi struct como matriz de bytes de una a otra y lo convirtió de nuevo.

Creo que mi enfoque podría ser mejor, ya que no fija la longitud en la propia struct y la única sobrecarga es solo una int para cada campo que tenga en su estructura. También hay un poco de sobrecarga dentro de la matriz de bytes generada por BinaryFormatter , pero aparte de eso, no es mucho.

Me gustaría echar un vistazo a las clases BinaryReader y BinaryWriter. Recientemente tuve que serializar datos en una matriz de bytes (y volver) y solo encontré estas clases después de que básicamente las reescribí yo mismo.

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

Hay un buen ejemplo en esa página también.

Parece una estructura predefinida (nivel C) para alguna biblioteca externa. Marshal es tu amigo. Comprobar:

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

para empezar, cómo lidiar con esto. Tenga en cuenta que puede, con atributos, definir cosas como el diseño de bytes y el manejo de cadenas. Muy buen enfoque, en realidad.

Ni BinaryFormatter ni MemoryStream están listos para eso.

@Abdel Olakara answer donese no funciona en .net 3.5, se debe modificar de la siguiente manera:

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

Esto debería hacer el truco rápidamente, ¿verdad?

Este ejemplo aquí solo se aplica a los tipos blittables puros, por ejemplo, los tipos que se pueden memcpy’d directamente en C.

Ejemplo: estructura bien conocida de 64 bits

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

Definido exactamente así, la estructura se empaquetará automáticamente como de 64 bits.

Ahora podemos crear el volumen de vóxeles:

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

Y guárdelos todos en una matriz de bytes:

 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. 

Sin embargo, dado que el OP quiere saber cómo convertir la estructura en sí, nuestra estructura Voxel puede tener el siguiente método 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();