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

voix
65

Quelle serait la meilleure façon de remplir une struct C # à partir d'un octet tableau [] où les données ont été d'une struct C ++ / C? La struct C ressemblerait à quelque chose comme ça (mon C est très rouillée):

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 copier OldStuffà NewStuff, si OldStuffa été passé en octets tableau []?

Je fais actuellement quelque chose comme ce qui suit, mais il se sent 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();

mieux est-il moyen d'y arriver?


Est-ce que l' aide de la BinaryReaderclasse offre des gains de performance sur épinglant la mémoire et à l' aide Marshal.PtrStructure?

Créé 05/08/2008 à 22:19
source utilisateur
Dans d'autres langues...                            


5 réponses

voix
88

D'après ce que je peux voir dans ce contexte, vous n'avez pas besoin de copier SomeByteArraydans un tampon. Il vous suffit d'obtenir la poignée de SomeByteArray, épingler, copier les IntPtrdonnées à l' aide PtrToStructurepuis relâchez. Pas besoin d'une 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 
{
    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try
    {
        T stuff = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    }
    finally
    {
        handle.Free();
    }
    return stuff;
}

Version simplifiée (nécessite unsafecommutateur):

unsafe T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
    fixed (byte* ptr = &bytes[0])
    {
        return (T)Marshal.PtrToStructure((IntPtr)ptr, typeof(T));
    }
}
Créé 05/08/2008 à 22:29
source utilisateur

voix
4

Voici une exception version sécurisée de la ré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();
    }
}
Créé 24/01/2017 à 18:40
source utilisateur

voix
4

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

Créé 31/08/2008 à 11:03
source utilisateur

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

cette

Créé 11/12/2011 à 12:46
source utilisateur

voix
0

Si vous avez un octet [], vous devriez être en mesure d'utiliser la classe BinaryReader et définir des valeurs sur Newstuff en utilisant les méthodes readx disponibles.

Créé 05/08/2008 à 22:24
source utilisateur

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more