95 Stimmen

Lesen einer C/C++-Datenstruktur in C# aus einem Byte-Array

Was wäre der beste Weg, um eine C#-Struktur aus einem Byte[] Array zu füllen, wo die Daten aus einer C/C++-Struktur waren? Die C-Struktur würde etwa so aussehen (mein C ist sehr rostig):

typedef OldStuff {
    CHAR Name[8];
    UInt32 User;
    CHAR Location[8];
    UInt32 TimeStamp;
    UInt32 Sequence;
    CHAR Tracking[16];
    CHAR Filler[12];
}

Und würde sich etwa so füllen:

[StructLayout(LayoutKind.Explicit, Size = 56, Pack = 1)]
public struct NewStuff
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    [FieldOffset(0)]
    public string Name;

    [MarshalAs(UnmanagedType.U4)]
    [FieldOffset(8)]
    public uint User;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    [FieldOffset(12)]
    public string Location;

    [MarshalAs(UnmanagedType.U4)]
    [FieldOffset(20)]
    public uint TimeStamp;

    [MarshalAs(UnmanagedType.U4)]
    [FieldOffset(24)]
    public uint Sequence;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
    [FieldOffset(28)]
    public string Tracking;
}

Wie kann ich am besten kopieren? OldStuff a NewStuff wenn OldStuff als Byte[]-Array übergeben wurde?

Ich mache derzeit etwas Ähnliches wie das Folgende, aber es fühlt sich etwas klobig an.

GCHandle handle;
NewStuff MyStuff;

int BufferSize = Marshal.SizeOf(typeof(NewStuff));
byte[] buff = new byte[BufferSize];

Array.Copy(SomeByteArray, 0, buff, 0, BufferSize);

handle = GCHandle.Alloc(buff, GCHandleType.Pinned);

MyStuff = (NewStuff)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(NewStuff));

handle.Free();

Gibt es eine bessere Möglichkeit, dies zu erreichen?


Würde die Verwendung des BinaryReader Klasse einen Leistungsvorteil gegenüber dem Pinning des Speichers und der Verwendung von Marshal.PtrStructure ?

1 Stimmen

Zu Ihrer Information: Wenn Ihr Programm auf verschiedenen Rechnern läuft, müssen Sie möglicherweise Little Endian und Big Endian behandeln.

1 Stimmen

Wie kann man das auf der Ebene der Struktur handhaben, d.h. ohne die Bytes für jeden Wert in der Struktur einzeln umkehren zu müssen?

128voto

Coincoin Punkte 26516

Soweit ich das in diesem Zusammenhang sehen kann, brauchen Sie nicht zu kopieren SomeByteArray in einen Puffer. Sie müssen lediglich den Handle von SomeByteArray anheften, kopieren Sie die IntPtr Daten mit PtrToStructure und dann loslassen. Eine Kopie ist nicht erforderlich.

Das wäre es:

NewStuff ByteArrayToNewStuff(byte[] bytes)
{
    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try
    {
        NewStuff stuff = (NewStuff)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(NewStuff));
    }
    finally
    {
        handle.Free();
    }
    return stuff;
}

Generische Version:

T ByteArrayToStructure<T>(byte[] bytes) where T: struct 
{
    T stuff;
    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try
    {
        stuff = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    }
    finally
    {
        handle.Free();
    }
    return stuff;
}

Einfachere Version (erfordert unsafe Schalter):

unsafe T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
    fixed (byte* ptr = &bytes[0])
    {
        return (T)Marshal.PtrToStructure((IntPtr)ptr, typeof(T));
    }
}

0 Stimmen

CS0411 Die Typargumente für die Methode 'ByteArrayToStructure<T>(byte[], int)' können nicht aus der Verwendung abgeleitet werden. Versuchen Sie, die Typargumente explizit anzugeben. (Ich habe int index of byte array) hinzugefügt.

0 Stimmen

Wird bei Vorhandensein von Ausnahmen Speicherplatz verlieren. Siehe: stackoverflow.com/a/41836532/184528 für eine sicherere Version.

5 Stimmen

Ab 4.5.1 gibt es eine generische Version von PtrToStructure, so dass die zweite Zeile in der obigen generischen Version zu einer werden kann: var stuff = Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());

16voto

cdiggins Punkte 16400

Hier ist eine ausnahmesichere Version der akzeptierte Antwort :

public static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
    var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try {
        return (T) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    }
    finally {
        handle.Free();
    }
}

4 Stimmen

@Ben-Collins Die akzeptierte Antwort wurde bearbeitet, nachdem ich meine Antwort hinzugefügt hatte.

7voto

Tim Ring Punkte 1764

Achten Sie auf Verpackungsprobleme. In dem von Ihnen angeführten Beispiel befinden sich alle Felder an den offensichtlichen Offsets, da alles an 4-Byte-Grenzen liegt, aber das wird nicht immer der Fall sein. Visual C++ packt standardmäßig an 8-Byte-Grenzen.

1 Stimmen

"Visual C++ packt auf 8 Byte Grenzen standardmäßig". Damit ist mein Problem gelöst, vielen Dank!

4voto

Dushyant Punkte 41
object ByteArrayToStructure(byte[] bytearray, object structureObj, int position)
{
    int length = Marshal.SizeOf(structureObj);
    IntPtr ptr = Marshal.AllocHGlobal(length);
    Marshal.Copy(bytearray, 0, ptr, length);
    structureObj = Marshal.PtrToStructure(Marshal.UnsafeAddrOfPinnedArrayElement(bytearray, position), structureObj.GetType());
    Marshal.FreeHGlobal(ptr);
    return structureObj;
}   

Haben diese

-1voto

Mufaka Punkte 72

Wenn Sie ein Byte[] haben, sollten Sie in der Lage sein, die BinaryReader-Klasse zu verwenden und Werte auf NewStuff mithilfe der verfügbaren ReadX-Methoden zu setzen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X