2010-07-19 14 views
63

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

he definido 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, se crea una instancia del mismo y asignar valores a la misma:

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 desea enviar este paquete por zócalo. 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?

+2

Palabra clave - 'Serialización' –

+0

Una corrección en la última línea MyPing.Send (Toma byte array como parámetro); Enviar Enviar enviar ... –

+0

Hola Petar, No te entendí ... –

Respuesta

97

Esto es bastante fácil, usando de clasificación.

Tapa de 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, que tendrá que poner esto antes una cadena

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

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

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

+0

Gracias Vincet. Se debe llamar a GetBytes() después de enviar el byte [] ?? y el método frombytes() está enviando los bytes? Soy un pequeño amigo confundido? –

+1

GetBytes se convierte de su estructura a una matriz. FromBytes se convierte de Bytes a su estructura. Esto es evidente a partir de las firmas de función. –

+0

@Vincet: Está bien, Vincet. Ahora quiero recuperar la respuesta. ¿Cómo puedo hacer eso? ¿Cómo puedo verificar mi respuesta? –

0

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

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

por un primer plato a 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 hechos para eso.

18

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

Ésta es una copia descarada de otro hilo que he encontrado al buscar en Google!

actualización: Para más detalles, revisar el source

+0

@Abdel: ¿Por qué Shamless Copy? –

+4

-1, debe proporcionar un enlace al hilo original. –

+0

He convertido estructura a matriz de bytes usando Marshalling ahora, ¿cómo puedo verificar si recibo la respuesta del socket? ¿Cómo verificar eso? –

2

Puede utilizar Mariscal (StructureToPtr, ptrToStructure), y Marshal.copy pero esto es plataforma dependiente.


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 incluyen funciones para serializar cada miembro.


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

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

Se puede crear un método Save y un método nuevo en su estructura:

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

A continuación, seleccione los miembros para guardar/cargar a Stream -> conjunto de bytes.

1

Esto se puede hacer de manera muy sencilla.

Definir su estructura de forma explícita 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 sólo se puede escribir en un contexto inseguro. Tienes que liberar addr cuando hayas terminado.

Marshal.FreeHGlobal(addr); 
+0

Al realizar operaciones explícitas ordenadas en una colección de tamaño fijo, probablemente debería utilizar una matriz y for-loop. El conjunto porque es de tamaño fijo y el bucle for porque no se garantiza que foreach esté en el orden esperado, a menos que conozca la implementación subyacente de su tipo de lista y su enumerador, y que nunca cambie. Uno podría definir el enumerador para comenzar desde el final y retroceder, por ejemplo. –

19

Si realmente quiere que sea RÁPIDO, puede hacerlo utilizando un código inseguro con CopyMemory. CopyMemory es aproximadamente 5 veces más rápido (por ejemplo, 800 MB de datos tardan 3 segundos 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 se almacenan en la propia estructura de la estructura, p. números o matrices 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; 
    } 
0

@Abdel Olakara respuesta donese no funciona en .NET 3.5, debe modificarse de la siguiente manera:

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); 
    } 
0
 Header header = new Header(); 
     Byte[] headerBytes = new Byte[Marshal.SizeOf(header)]; 
     Marshal.Copy((IntPtr)(&header), headerBytes, 0, headerBytes.Length); 

Esto debe hacer el truco rápido, ¿verdad?

+0

La versión de GCHandle es mucho mejor. –

3

Como la respuesta principal está utilizando el tipo CIFSPacket, que no es (o no) disponible en C#, escribí 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<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; 
    } 

Probado, funcionan.

6

variante del código de Vicent con la asignación de uno menos memoria:

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

lo uso GCHandle en el "contacto" de la memoria y luego usar directamente con su dirección h.AddrOfPinnedObject().

+0

Esta debería ser la respuesta aceptada – Julien

+0

Debería eliminar 'where T: struct' de lo contrario, se quejará de que 'T' se pase no es un 'tipo no nulo'. – codenamezero

0

Este ejemplo aquí sólo es aplicable a tipos puros blittable, por ejemplo, los tipos que se pueden memcpy'd directamente en C.

Ejemplo - bien conocido struct 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; 
} 

exactamente definida como esto, la estructura se empaquetará automáticamente como de 64 bits.

Ahora podemos crear volumen de voxels:

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

y guardarlos todos a 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, puesto que el PO quiere saber cómo convertir la estructura en sí, nuestra estructura Voxel puede tener ToBytes siguiente método:

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

yo he llegado con un enfoque diferente que podría convertir cualquierstruct sin la molestia de la longitud de fijación, sin embargo, la matriz de bytes resultante tendría un poco más de sobrecarga.

Aquí es una muestra struct:

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

Como se puede ver, todas aquellas estructuras requeriría la adición de 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 para FieldInfo. Mi inspiración es TLV Tipo-Longitud-Valor. Vamos a echar un vistazo al código:

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

La función anterior simplemente utiliza el BinaryFormatter para serializar el tamaño desconocido prima object, y yo simplemente no perder de vista el tamaño, así y guardarlo dentro de la salida MemoryStream también.

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

Cuando queremos convertir de nuevo a su original struct le vemos el largo de espalda y volcar directamente de nuevo en el BinaryFormatter que a su vez volcado de nuevo en el struct.

Estas 2 funciones son genéricos y debería funcionar con cualquier struct, He probado el código anterior en mi proyecto C# donde tengo un servidor y un cliente, conectado y comunicarse a través de NamedPipeStream y me devuelve mi struct como matriz de bytes de uno y a otro y lo convirtió de nuevo.

Creo que mi enfoque podría ser mejor, ya que no fija la longitud en el struct y la única sobrecarga es solo int por 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.

Cuestiones relacionadas