91 votes

La lecture d'un C/C++ structure de données en C# à partir d'un tableau d'octets

Quel serait le meilleur moyen de remplir un C# struct à partir d'un byte[] tableau où les données ont été à partir d'un C/C++ struct? La structure C ressemblerait à quelque chose comme ceci (mon C est très rouillé):

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

Et comblera quelque chose comme ceci:

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

Quel est le meilleur moyen pour copier Avant de NewStuff, si Avant a été adoptée par le byte[] tableau?

Je suis actuellement en train de faire quelque chose comme ce qui suit, mais il se sent en quelque sorte de maladroit.

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

Est-il meilleure façon d'accomplir cette?


En utilisant l'BinaryReader classe offre aucune des gains de performance sur la base du mémoire et l'utilisation de Maréchal.PtrStructure?

122voto

Coincoin Points 12823

À partir de ce que je vois dans ce contexte, vous n'avez pas besoin de copier SomeByteArray dans une mémoire tampon. Vous avez simplement besoin d'obtenir le handle de SomeByteArray, pin, copiez le IntPtr données à l'aide de PtrToStructure et puis relâchez-le. Pas besoin d'une copie.

Cela pourrait être:

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

Version générique:

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

...

7voto

Tim Ring Points 970

Watch out pour l'emballage de questions. Dans l'exemple que vous avez donné tous les champs sont à l'évidence des décalages, car tout est sur 4 octets limites, mais ce ne sera pas toujours le cas. Visual C++ packs sur 8 octets limites par défaut.

4voto

Dushyant Points 31
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;
}   

Ont ce

-1voto

Mufaka Points 54

Si vous avez un byte[] vous devez être en mesure d'utiliser le BinaryReader de classe et de définir des valeurs sur NewStuff en utilisant le ReadX méthodes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X