¿Cuál sería la mejor manera de llenar una estructura C # desde una matriz de bytes [] donde los datos provienen de una estructura C / C ++? La estructura de 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 OldStuffa NewStuff, si OldStuffse pasa 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();
¿Existe una mejor manera de lograr esto?
¿Usar la BinaryReaderclase ofrecería alguna mejora de rendimiento en comparación con anclar la memoria y usarla Marshal.PtrStructure?
c#
.net
data-structures
marshalling
Chris Miller
fuente
fuente

Respuestas:
Por lo que puedo ver en ese contexto, no es necesario copiar
SomeByteArrayen un búfer. Simplemente necesita obtener el identificador deSomeByteArray, fijarlo, copiar losIntPtrdatos usandoPtrToStructurey luego liberar. No necesita 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)); } }fuente
var stuff = Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());Aquí hay una versión segura de 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(); } }fuente
Tenga cuidado con los problemas de embalaje. En el ejemplo que proporcionó, 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 ++ se empaqueta en límites de 8 bytes de forma predeterminada.
fuente
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
fuente
Si tiene un byte [], debería poder usar la clase BinaryReader y establecer valores en NewStuff usando los métodos ReadX disponibles.
fuente