简体   繁体   English

如何将字节数组解析为 object?

[英]How to parse a byte array into a object?

I'm reading binary data (either by network stream or from files) that is predefined several years ago.我正在读取几年前预定义的二进制数据(通过网络 stream 或文件)。 I currently read this data by reading it into a byte array and then convert the array into the fields I need with System.BitConverter .我目前通过将其读入字节数组来读取这些数据,然后使用System.BitConverter将该数组转换为我需要的字段。 Needless to say this is timeconsuming and error prone.不用说,这是耗时且容易出错的。

I wish I could use ISerializable , but I don't see how this can be implemented for a predefined structure.我希望我可以使用ISerializable ,但我不知道如何为预定义的结构实现它。

I hope for pointers on how to improve my current strategy...我希望有关如何改进当前策略的指示...

Rozon, I had this exact same problem, and I was about to give up on the solution until I found this question here, and read Joe's response to it. Rozon,我遇到了同样的问题,我正要放弃解决方案,直到我在这里找到了这个问题,并阅读了 Joe 对此的回复。 Joe mentions the Marshal object to move data between regular MEMORY and a managed object... Here is what I did, and what worked like magic for me... If you haven't already figured this out, here is a whole break-down of what I did, and how I got it to work.. (Thank you Joe.)... Joe 提到了 Marshal object 在常规MEMORY和托管 object 之间移动数据......这就是我所做的,对我来说就像魔法一样......如果你还没有弄清楚这一点,我做了什么,以及我是如何让它发挥作用的......(谢谢乔。)......

First, you must be able to create a managed instance of your structure.首先,您必须能够创建结构的托管实例。 This shouldn't be too hard, it just means a lot of extra attributes on your structure.这不应该太难,它只是意味着你的结构上有很多额外的属性。

For example, here's a data structure that holds some information about a server.例如,这是一个包含有关服务器的一些信息的数据结构。 Of course, the string sizes are not accurate, so make SURE your string sizes ARE!当然,字符串大小不准确,因此请确保您的字符串大小是正确的!

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

Also, make CERTAIN you are using the correct Charset, in the case that it was compiled to use Unicode, you need to make sure the Charset reflects that.此外,确保您使用正确的字符集,如果它被编译为使用 Unicode,您需要确保字符集反映了这一点。

Now, here's the cool part...现在,这是很酷的部分......

To get the SIZE of the struct, (and the size of the buffer you need to hold it), you need only to use:要获得结构的 SIZE,(以及你需要保存它的缓冲区的大小),你只需要使用:

byte[] buff = new byte[Marshal.SizeOf(pStruct)];

And, with that, you can now do whatever it is you do to GET this data delivered to you, (which I assume you already have all that figured out...) and then the magic can begin.而且,有了这个,您现在可以做任何事情来获取这些数据,(我假设您已经知道了所有这些......)然后魔术就可以开始了。 What we do, is allocate a block of unmanaged memory, then copy the buffer into the block of memory, then copy it back out into our structure, and free the unmanaged memory block.我们所做的是分配一块非托管 memory,然后将缓冲区复制到 memory 块中,然后将其复制回我们的结构中,并释放非托管 ZCD69B4957F06CD818D7BF3D61980E2 块。 This can easily be put into an extension function that can operate on any byte[], giving you the ability to call something like:这可以很容易地放入可以对任何字节 [] 进行操作的扩展 function 中,使您能够调用如下内容:

pStruct = (TServerInformation)buff.FromMemory();

The following code is what worked wonders for me.以下代码为我创造了奇迹。 Oh, and do not forget to put try/catch/finally blocks around this in production code...哦,不要忘记在生产代码中放置 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);

And there you have it, This avoids all the extra header info that is included when you use Serialization, AND.有了它,这避免了使用序列化时包含的所有额外 header 信息,并且。 it's about the only way I've found to get accurate "sizeof()" data from a managed structure, I'm sure there may be other ways to do this, but so far, this is the best I have found, after about 5 days of searching, hacking, searching, debugging, hacking.这是我发现从托管结构中获取准确的“sizeof()”数据的唯一方法,我确信可能还有其他方法可以做到这一点,但到目前为止,这是我发现的最好的方法,大约5天的搜索,黑客,搜索,调试,黑客。 and searching some more..: :)并搜索更多..:)

If this works for you, let me know... I'm curious to see if it fixes your problem as well.如果这对您有用,请告诉我...我很想知道它是否也能解决您的问题。

One way is to create a MemoryStream around the byte array, then use a BinaryReader .一种方法是围绕字节数组创建MemoryStream ,然后使用BinaryReader That allows you to parse primitive values etc very easily.这使您可以非常轻松地解析原始值等。

However, it does depend on whether the endianness of the data is appropriate.但是,它确实取决于数据的字节顺序是否合适。 I have an EndianBinaryReader class in MiscUtil which can help you if the built-in one isn't appropriate.我在MiscUtil中有一个EndianBinaryReader class 如果内置的不合适,它可以为您提供帮助。

The Marshal interop classes and methods (namely, PtrToStruct and StructToPtr) can help here as well. Marshal 互操作类和方法(即 PtrToStruct 和 StructToPtr)也可以在这里提供帮助。 See: http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx请参阅: http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

hope this help:希望这有帮助:

  //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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM