从字节数组中读取C#中的C / C ++数据结构
从数据来自C / C ++结构的byte []数组填充C#结构的最佳方法是什么? C结构看起来像这样(我的C是非常生锈的):
typedef OldStuff { CHAR Name[8]; UInt32 User; CHAR Location[8]; UInt32 TimeStamp; UInt32 Sequence; CHAR Tracking[16]; CHAR Filler[12]; }
并会填写这样的东西:
[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; }
如果OldStuff
作为byte []数组传递,那么将OldStuff
复制到NewStuff
最佳方法是什么?
我目前正在做类似下面的事情,但感觉有点笨拙。
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();
有没有更好的方法来完成这个?
会使用BinaryReader
类提供任何性能增加超过固定内存和使用Marshal.PtrStructure
?
从我可以看到在这方面,你不需要复制SomeByteArray
到缓冲区。 您只需要从SomeByteArray
获取句柄,固定它,使用PtrToStructure
复制IntPtr
数据,然后释放。 不需要复制。
那将是:
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; }
通用版本:
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; }
更简单的版本(需要unsafe
开关):
unsafe T ByteArrayToStructure<T>(byte[] bytes) where T : struct { fixed (byte* ptr = &bytes[0]) { return (T)Marshal.PtrToStructure((IntPtr)ptr, typeof(T)); } }
注意包装问题。 在你给的例子中,所有的字段都是明显的偏移量,因为一切都在4个字节的边界上,但并不总是这样。 Visual C ++在默认情况下封装在8个字节的边界上。
以下是接受的答案的exception安全版本:
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(); } }
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; }
拥有这个
如果你有一个byte [],你应该可以使用BinaryReader类,并使用可用的ReadX方法在NewStuff上设置值。