2009-12-20 13 views
7

¿Alguien puede decirme cómo obtener una matriz de bytes en una estructura de forma directa en C# .NET versión 2? Como el familiar fread que se encuentra en C, hasta ahora no he tenido mucho éxito leyendo un flujo de bytes y completando automáticamente una estructura. He visto algunas implementaciones donde hay puntero hocus-pocus en el código administrado usando la palabra clave unsafe.Un equivalente de C# del archivo fread de C

Tenga una mirada en esta muestra:

public unsafe struct foobarStruct{ 

    /* fields here... */ 

    public foobarStruct(int nFakeArgs){ 
     /* Initialize the fields... */ 
    } 

    public foobarStruct(byte[] data) : this(0) { 
     unsafe { 
     GCHandle hByteData = GCHandle.Alloc(data, GCHandleType.Pinned); 
     IntPtr pByteData = hByteData.AddrOfPinnedObject(); 
     this = (foobarStruct)Marshal.PtrToStructure(pByteData, this.GetType()); 
     hByteData.Free(); 
     } 
    } 
} 

La razón por la que tengo dos constructores en foobarStruct

  • es que no puede haber un constructor vacío.
  • Pase un bloque de memoria (como una matriz de bytes) al constructor al crear una instancia de la estructura.

¿Es esa implementación lo suficientemente buena o hay una forma mucho más limpia de lograr esto?

Editar: No quiero utilizar la interfaz ISerializable o su implementación. Estoy tratando de leer una imagen binaria para calcular los campos utilizados y determinar sus datos utilizando las estructuras PE.

+3

incluso en C, que es una muy mala idea directamente '' fread' en un struct' debido al relleno y alineación consideraciones –

+0

Ha considerado el uso de serialización aquí en su lugar? –

+0

Esta operación debe ocurrir dentro de un bloque inseguro porque es, bueno, inseguro. Una estructura podría contener miembros que apuntan a tipos de referencia, etc. Usted está pidiendo tomar bytes de disco desconocidos y lanzarlos a una estructura que pueda contener punteros a cualquier cosa. Es demasiado pedirle al marco que valide lo que está tratando de hacer, de ahí el bloque inseguro. Aún puede hacerlo, pero el marco debe adoptar un enfoque de "usted es independiente".La serialización maneja las preocupaciones subyacentes por usted, pero no se ajusta a todos los escenarios. No creo que va a hacer mucho mejor que el código que se muestra. –

Respuesta

10

No hay nada de malo con el uso del marcador de referencias P/Invocar, no es inseguro y no tiene que utilizar la palabra clave insegura. Hacerlo mal solo producirá datos incorrectos. Puede ser mucho más fácil de usar que escribir explícitamente el código de deserialización, especialmente cuando el archivo contiene cadenas. No puede usar BinaryReader.ReadString(), supone que la cadena fue escrita por BinaryWriter. Sin embargo, asegúrese de declarar la estructura de los datos con una declaración de estructura, esto.GetType() no funcionará bien.

Aquí es una clase genérica que hará que funcione para cualquier declaración de la estructura:

class StructureReader<T> where T : struct { 
    private byte[] mBuffer; 
    public StructureReader() { 
     mBuffer = new byte[Marshal.SizeOf(typeof(T))]; 
    } 
    public T Read(System.IO.FileStream fs) { 
     int bytes = fs.Read(mBuffer, 0, mBuffer.Length); 
     if (bytes == 0) throw new InvalidOperationException("End-of-file reached"); 
     if (bytes != mBuffer.Length) throw new ArgumentException("File contains bad data"); 
     T retval; 
     GCHandle hdl = GCHandle.Alloc(mBuffer, GCHandleType.Pinned); 
     try { 
     retval = (T)Marshal.PtrToStructure(hdl.AddrOfPinnedObject(), typeof(T)); 
     } 
     finally { 
     hdl.Free(); 
     } 
     return retval; 
    } 

Un ejemplo de declaración de la estructura de los datos en el archivo:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] 
struct Sample { 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 42)] 
    public string someString; 
} 

que necesitará para modificar la declaración de estructura y los atributos para obtener una coincidencia con los datos en el archivo. código de ejemplo que lee un archivo:

var data = new List<Sample>(); 
    var reader = new StructureReader<Sample>(); 
    using (var stream = new FileStream(@"c:\temp\test.bin", FileMode.Open, FileAccess.Read)) { 
    while(stream.Position < stream.Length) { 
     data.Add(reader.Read(stream)); 
    } 
    } 
2

Probablemente desee utilizar un BinaryReader que le permita leer en tipos primitivos en forma binaria.

Crea un MemoryStream desde el byte[] y luego usa el BinaryReader desde ese. Debería poder leer la estructura y completar su objeto en consecuencia.