Leer una estructura de datos C/C++ en C# desde una matriz de bytes

Resuelto Chris Miller asked hace 16 años • 5 respuestas

¿Cuál sería la mejor manera de llenar una estructura C# a partir de una matriz de bytes[] donde los datos provenían de una estructura C/C++? La estructura C se vería así (mi C está muy oxidada):

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

Y llenaría algo como esto:

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

¿Cuál es la mejor manera de copiar OldStuffsi NewStuffse OldStuffpasó como matriz de bytes []?

Actualmente estoy haciendo algo como lo siguiente, pero se siente un poco torpe.

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

¿Existe una mejor manera de lograr esto?


¿El uso de la BinaryReaderclase ofrecería alguna mejora en el rendimiento respecto a fijar la memoria y usar Marshal.PtrStructure?

Chris Miller avatar Aug 06 '08 04:08 Chris Miller
Aceptado

Por lo que puedo ver en ese contexto, no es necesario copiar SomeByteArrayen un búfer. Simplemente necesita obtener el identificador SomeByteArray, fijarlo, copiar los IntPtrdatos PtrToStructurey luego soltarlo. No es necesaria una copia.

Eso sería:

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

Versión genérica:

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

Versión más simple (requiere unsafeinterruptor):

unsafe T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
    fixed (byte* ptr = &bytes[0])
    {
        return (T)Marshal.PtrToStructure((IntPtr)ptr, typeof(T));
    }
}
Coincoin avatar Aug 05 '2008 21:08 Coincoin

Tenga cuidado con los problemas de embalaje. En el ejemplo que dio, todos los campos tienen desplazamientos obvios porque todo está en límites de 4 bytes, pero este no siempre será el caso. Visual C++ empaqueta límites de 8 bytes de forma predeterminada.

Tim Ring avatar Aug 31 '2008 10:08 Tim Ring
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;
}   

Tengo esto

Dushyant avatar Dec 11 '2011 11:12 Dushyant