Wat zou de beste manier zijn om een C#-struct uit een byte[]-array te vullen waar de gegevens uit een C/C++-struct kwamen? De C-structuur ziet er ongeveer zo uit (mijn C is erg roestig):
typedef OldStuff {
CHAR Name[8];
UInt32 User;
CHAR Location[8];
UInt32 TimeStamp;
UInt32 Sequence;
CHAR Tracking[16];
CHAR Filler[12];
}
En zou zoiets als dit vullen:
[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;
}
Wat is de beste manier om OldStuff
naar NewStuff
te kopiëren, als OldStuff
is doorgegeven als byte[]-array?
Ik doe momenteel zoiets als het volgende, maar het voelt een beetje onhandig aan.
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();
Is er een betere manier om dit te bereiken?
Zou het gebruik van de klasse BinaryReader
enige prestatiewinst opleveren ten opzichte van het vastzetten van het geheugen en het gebruik van Marshal.PtrStructure
?
Antwoord 1, autoriteit 100%
Voor zover ik in die context kan zien, hoef je SomeByteArray
niet naar een buffer te kopiëren. Je hoeft alleen maar het handvat van SomeByteArray
te halen, het vast te pinnen, de IntPtr
-gegevens te kopiëren met behulp van PtrToStructure
en vervolgens los te laten. Geen kopie nodig.
Dat zou zijn:
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;
}
Algemene versie:
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;
}
Eenvoudigere versie (vereist unsafe
schakelaar):
unsafe T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
fixed (byte* ptr = &bytes[0])
{
return (T)Marshal.PtrToStructure((IntPtr)ptr, typeof(T));
}
}
Antwoord 2, autoriteit 10%
Hier is een uitzonderingsveilige versie van het geaccepteerde antwoord:
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();
}
}
Antwoord 3, autoriteit 6%
Kijk uit voor verpakkingsproblemen. In het voorbeeld gaf je alle velden op de voor de hand liggende offsets omdat alles op 4 byte grenzen is, maar dit zal niet altijd het geval zijn. Visual C++ Packs op 8 byte-grenzen Standaard.