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

votos
65

¿Cuál sería la mejor manera de llenar una estructura C # de una matriz de bytes [] donde los datos eran de una estructura C / C ++? La estructura C se vería más o menos 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 OldStuffa NewStuff, si OldStuffse pasa 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();

¿Hay una mejor manera de lograr esto?


¿El uso de la BinaryReaderclase ofrecería alguna mejora en el rendimiento sobre la fijación de la memoria y el uso Marshal.PtrStructure?

Publicado el 05/08/2008 a las 22:19
fuente por usuario
En otros idiomas...                            


5 respuestas

votos
0

Si tiene un byte [], debería poder utilizar la clase BinaryReader y establecer valores en NewStuff utilizando los métodos ReadX disponibles.

Respondida el 05/08/2008 a las 22:24
fuente por usuario

votos
88

Por lo que puedo ver en ese contexto, no es necesario copiar SomeByteArrayen un búfer. Simplemente necesita obtener el identificador SomeByteArray, anclarlo, copiar los IntPtrdatos usando PtrToStructurey luego soltar. No hay necesidad de 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 
{
    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try
    {
        T stuff = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    }
    finally
    {
        handle.Free();
    }
    return stuff;
}

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

unsafe T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
    fixed (byte* ptr = &bytes[0])
    {
        return (T)Marshal.PtrToStructure((IntPtr)ptr, typeof(T));
    }
}
Respondida el 05/08/2008 a las 22:29
fuente por usuario

votos
4

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

Respondida el 31/08/2008 a las 11:03
fuente por usuario

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

Tengo esto

Respondida el 11/12/2011 a las 12:46
fuente por usuario

votos
4

Aquí es una versión segura excepción de la respuesta aceptada :

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();
    }
}
Respondida el 24/01/2017 a las 15:40
fuente por usuario

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