Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to parse a byte array into a object?

Tags:

c#

.net

I'm reading binary data (either by network stream or from files) that is predefined several years ago. 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. 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.

I hope for pointers on how to improve my current strategy...

like image 488
rozon Avatar asked Jun 08 '11 06:06

rozon


People also ask

How do you convert bytes to objects?

Converting byte array into Object and Object into a byte array process is known as deserializing and serializing. The class object which gets serialized/deserialized must implement the interface Serializable. Serializable is a marker interface that comes under package 'java.

Can you convert byte into string?

One method is to create a string variable and then append the byte value to the string variable with the help of + operator. This will directly convert the byte value to a string and add it in the string variable. The simplest way to do so is using valueOf() method of String class in java.

What is an byte array?

ByteArray is an extremely powerful Class that can be used for many things related to data manipulation, including (but not limited to) saving game data online, encrypting data, compressing data, and converting a BitmapData object to a PNG or JPG file.

How do I convert an image to a Bytearray?

Read the image using the read() method of the ImageIO class. Create a ByteArrayOutputStream object. Write the image to the ByteArrayOutputStream object created above using the write() method of the ImageIO class. Finally convert the contents of the ByteArrayOutputStream to a byte array using the toByteArray() method.


2 Answers

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. 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!)...

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.

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:

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. This can easily be put into an extension function that can operate on any byte[], giving you the ability to call something like:

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...

// 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, 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, and searching some more... :)

If this works for you, let me know... I'm curious to see if it fixes your problem as well.

like image 168
LarryF Avatar answered Nov 15 '22 09:11

LarryF


One way is to create a MemoryStream around the byte array, then use a 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.

like image 35
Jon Skeet Avatar answered Nov 15 '22 09:11

Jon Skeet