我正在读取几年前预定义的二进制数据(通过网络流或文件)。我目前通过将其读入字节数组来读取此数据,然后将数组转换为System.BitConverter
所需的字段。毋庸置疑,这是耗费时间和容易出错的。
我希望我可以使用ISerializable
,但我不知道如何为预定义的结构实现这一点。
我希望能够指出如何改进我目前的策略......
答案 0 :(得分:5)
首先,您必须能够创建结构的托管实例。这不应该太难,它只是意味着你的结构有很多额外的属性。
例如,这是一个包含有关服务器的一些信息的数据结构。当然,字符串大小不准确,所以请确保您的字符串大小为!
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=1)]
struct TServerInformation {
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=255)]
public string ComputerName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=255)]
public string HostName;
[MarshalAs(UnmanagedType.U4)]
public Int32 IPAddress;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=64)]
public string AdminName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)]
public string WindowsDir;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=255)]
public string Domain;
};
另外,如果您正在使用正确的Charset,如果它被编译为使用Unicode,则需要确保Charset能够反映出来。
现在,这里很酷......
要获取结构的SIZE,(以及需要保存缓冲区的大小),只需使用:
byte[] buff = new byte[Marshal.SizeOf(pStruct)];
而且,有了这个,你现在可以做任何你做的事情来获取传递给你的这些数据(我假设你已经找到了所有这些......)然后魔术就可以开始了。我们所做的是分配一块非托管内存,然后将缓冲区复制到内存块中,然后将其复制回我们的结构中,并释放非托管内存块。这可以很容易地放入一个可以在任何byte []上运行的扩展函数,使你能够调用类似的东西:
pStruct = (TServerInformation)buff.FromMemory();
以下代码对我有用。哦,不要忘记在生产代码中放置try / catch / finally块...
// Create our Managed Object...
TServerInformation pStruct = new TServerInformation();
// Allocate our block of unmanaged memory...
IntPtr pMem = Marshal.AllocHGlobal(Marshal.SizeOf(pStruct));
// Copy our buff buffer into that new block of memory...
Marshal.Copy(buff, 0, pMem, buff.Length);
// Type-cast that block of memory with the results of the Marshal object's help...
pStruct = (TServerInformation)Marshal.PtrToStructure(pMem, pStruct.GetType());
// Free that block of memory, that is no longer needed now that the data exists in managed form.
Marshal.FreeHGlobal(pMem);
你有它!这样可以避免使用序列化时包含的所有额外标题信息,而且,这是我发现的唯一可以获得准确" sizeof()"来自托管结构的数据。我确定可能有其他方法可以做到这一点,但到目前为止,这是我发现的最好的,经过大约5天的搜索,黑客攻击,搜索,调试,黑客攻击和搜索更多...: )
如果这对你有用,请告诉我......我很想知道它是否能解决你的问题。
答案 1 :(得分:4)
一种方法是在字节数组周围创建MemoryStream
,然后使用BinaryReader
。这使您可以非常轻松地解析原始值等。
但是,它取决于数据的endianness是否合适。我在MiscUtil中有一个EndianBinaryReader
课程,如果内置的课程不合适,可以帮助您。
答案 2 :(得分:2)
Marshal互操作类和方法(即PtrToStruct和StructToPtr)也可以在这里提供帮助。请参阅:http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx
答案 3 :(得分:0)
//set your byte data instead of null
byte[] data = null;
MemoryStream stream = new MemoryStream();
stream.Write(data,0,data.Length);
BinaryFormatter formatter = new BinaryFormatter();
Type s1 = (Type)formatter.Deserialize(stream);