2008-08-05 12 views
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 algo como esto (mi C es muy oxidado):Lectura de una estructura de datos C/C++ en C# desde una matriz de bytes

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 era pasado como byte [] matriz?

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?


haría con la clase BinaryReader ofrece mejoras de rendimiento más de fijar la memoria y el uso de Marshal.PtrStructure?

+1

FYI, Si el programa se ejecuta en varias máquinas que pueda necesitar para manejar poca vs big endian. – KPexEA

+1

¿Cómo se puede manejar eso en el nivel de la estructura, es decir, sin tener que invertir individualmente los bytes para cada valor en la estructura? – Pat

Respuesta

87

Según lo que puedo ver en ese contexto, no es necesario copiar SomeByteArray en un búfer. Simplemente necesita obtener el identificador del SomeByteArray, fijarlo, copiar los datos IntPtr usando PtrToStructure y luego soltarlo. 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; 
} 

más simple versión (requiere unsafe interruptor):

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

CS0411 \t Los argumentos de tipo para el método 'ByteArrayToStructure (byte [], int)' no se pueden deducir del uso. Intente especificar los argumentos de tipo explícitamente. (Agregué el índice int de la matriz de bytes). – SSpoke

+0

Se perderá memoria en presencia de excepciones. Consulte: http://stackoverflow.com/a/41836532/184528 para obtener una versión más segura. – cdiggins

+0

A partir de 4.5.1, hay una versión genérica de PtrToStructure, por lo que la segunda línea en la versión genérica, anterior, puede convertirse en: '' 'var stuff = Marshal.PtrToStructure (handle.AddrOfPinnedObject())' ' – RobinHood70

0

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

4

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.

+0

" Visual C++ paquetes en ** ** byte ** límites de forma predeterminada. Esto resolvió mi problema, ¡muchas gracias! –

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

tienen esta

4

Aquí es una versión segura excepción de la accepted answer:

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(); 
    } 
} 
Cuestiones relacionadas