.net - Lecture d'une structure de données C / C ++ en C # à partir d'un tableau d'octets

Translate

Quelle serait la meilleure façon de remplir une structure C # à partir d'un tableau byte [] où les données provenaient d'une structure C / C ++? La structure C ressemblerait à 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 remplirait 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;
}

Quelle est la meilleure façon de copierOldStuffàNewStuff, siOldStuffa été passé comme tableau d'octets []?

Je fais actuellement quelque chose comme ce qui suit, mais c'est un peu 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();

Y a-t-il une meilleure façon d'accomplir cela?


Serait en utilisant leBinaryReaderclasse offre des gains de performances par rapport à l'épinglage de la mémoire et à l'utilisationMarshal.PtrStructure?

This question and all comments follow the "Attribution Required."

Toutes les réponses

Translate

D'après ce que je peux voir dans ce contexte, vous n'avez pas besoin de copierSomeByteArraydans un tampon. Vous devez simplement obtenir la poignée deSomeByteArray, épinglez-le, copiez leIntPtrdonnées utilisantPtrToStructurepuis relâchez. Pas besoin de copie.

Ce serait:

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

Version générique:

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

Version plus simple (nécessiteunsafecommutateur):

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

Voici une version sûre des exceptions duréponse acceptée:

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

Faites attention aux problèmes d'emballage. Dans l'exemple que vous avez donné, tous les champs sont aux décalages évidents car tout est sur des limites de 4 octets, mais ce ne sera pas toujours le cas. Visual C ++ emballe sur des limites de 8 octets par défaut.

La source
Translate
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;
}   

Avoir ça

La source
Translate

Si vous avez un octet [], vous devriez pouvoir utiliser la classe BinaryReader et définir des valeurs sur NewStuff à l'aide des méthodes ReadX disponibles.

La source