Pregunta

¿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? OldStuff a NewStuff, si OldStuff ¿Se pasó 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?


¿Utilizaría el BinaryReader La clase ofrece mejoras de rendimiento sobre la fijación de la memoria y el uso. Marshal.PtrStructure?

¿Fue útil?

Solución

Por lo que puedo ver en ese contexto, no es necesario copiar SomeByteArray en un búfer.Simplemente necesita obtener el mango de SomeByteArray, fijarlo, copiar el IntPtr datos usando PtrToStructure y luego soltar.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 unsafe cambiar):

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

Otros consejos

Aquí hay una versión segura de excepción del 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();
    }
}

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.

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

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top