2009-12-20 4 views
7

Kann mir jemand sagen, wie man ein Array von Bytes in einer Struktur direkt in C# .NET Version 2 bekommt? Wie der bekannte fread, der in C gefunden wurde, hatte ich bisher nicht viel Erfolg beim Lesen eines Bytestroms und beim automatischen Füllen einer Struktur. Ich habe einige Implementierungen gesehen, bei denen es im verwalteten Code einen Zeiger-Hokuspokus gibt, indem das Schlüsselwort unsafe verwendet wird.Ein C# -Äquivalent von C's Freed-Datei i/o

bei dieser Probe Werfen Sie einen Blick:

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

Der Grund Ich habe zwei Konstrukteure in foobarStruct

  • Gibt es keine leeren Konstruktor sein kann.
  • Übergeben Sie einen Speicherblock (als Byte-Array) in den Konstruktor, wenn Sie die Struktur instanziieren.

Ist diese Implementierung gut genug oder gibt es einen viel saubereren Weg, dies zu erreichen?

Bearbeiten: Ich möchte nicht die ISerializable-Schnittstelle oder ihre Implementierung verwenden. Ich versuche ein binäres Bild zu lesen, um die verwendeten Felder auszuarbeiten und seine Daten anhand der PE-Strukturen zu bestimmen.

+3

auch in C, es ist eine sehr schlechte Idee direkt zu 'fread' in eine' struct' aufgrund Polsterung und Ausrichtung Überlegungen –

+0

Sie Serialisierung betrachtet haben mit hier statt? –

+0

Dieser Vorgang muss in einem unsicheren Block ausgeführt werden, da er nicht sicher ist. Eine Struktur könnte Elemente enthalten, die auf Verweistypen usw. zeigen. Sie möchten unbekannte Byte-Bytes aufnehmen und sie in eine Struktur einfügen, die möglicherweise Zeiger auf irgendetwas enthält. Es ist zu viel, um das Framework zu fragen, was Sie versuchen zu tun, daher der unsichere Block. Sie können es immer noch tun, aber das Framework muss einen "Sie sind auf sich selbst" -Ansatz nehmen.Serialisierung behandelt die zugrunde liegenden Probleme für Sie, aber es passt nicht alle Szenarien. Ich denke nicht, dass du viel besser als der gezeigte Code machen wirst. –

Antwort

10

Es ist nichts falsch an der Verwendung des P/Invoke-Marshallers, es ist nicht unsicher und Sie müssen das unsafe-Schlüsselwort nicht verwenden. Wenn Sie falsch liegen, werden nur schlechte Daten produziert. Es kann viel einfacher zu verwenden sein, als den Deserialisierungscode explizit zu schreiben, insbesondere wenn die Datei Strings enthält. Sie können BinaryReader.ReadString() nicht verwenden, es wird davon ausgegangen, dass die Zeichenfolge von BinaryWriter geschrieben wurde. Stellen Sie jedoch sicher, dass Sie die Struktur der Daten mit einer struct-Deklaration deklarieren, dies wird wahrscheinlich nicht gut funktionieren.

Hier ist eine generische Klasse, die es für jede Strukturdeklaration Arbeit machen:

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

Eine Probe Erklärung für die Struktur der Daten in der Datei:

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

Sie benötigen passe die Strukturdeklaration und die Attribute an, um eine Übereinstimmung mit den Daten in der Datei zu erhalten. Beispielcode, der eine Datei liest:

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

Sie möchten wahrscheinlich eine BinaryReader verwenden, die Sie in primitiven Typen in binärer Form lesen können.

Erstellen Sie eine MemoryStream aus der byte[] und verwenden Sie dann die BinaryReader davon. Sie sollten in der Lage sein, die Struktur auszulesen und Ihr Objekt entsprechend zu füllen.