Search code examples
.netinteropbinarylegacy

How to take a collection of bytes and pull typed values out of it?


Say I have a collection of bytes

var bytes = new byte[] {0, 1, 2, 3, 4, 5, 6, 7};

and I want to pull out a defined value from the bytes as a managed type, e.g. a ushort. What is a simple way to define what types reside at what location in the collection and pull out those values?

One (ugly) way is to use System.BitConverter and a Queue or byte[] with an index and simply iterate through, e.g.:

int index = 0;
ushort first = System.BitConverter.ToUint16(bytes, index);
index += 2; // size of a ushort
int second = System.BitConverter.ToInt32(bytes, index);
index += 4;
...

This method gets very, very tedious when you deal with a lot of these structures!

I know that there is the System.Runtime.InteropServices.StructLayoutAttribute which allows me to define the locations of types inside a struct or class, but there doesn't seem to be a way to import the collection of bytes into that struct. If I could somehow overlay the struct on the collection of bytes and pull out the values, that would be ideal. E.g.

Foo foo = (Foo)bytes; // doesn't work because I'd need to implement the implicit operator
ushort first = foo.first;
int second = foo.second;
...
[StructLayout(LayoutKind.Explicit, Size=FOO_SIZE)]
public struct Foo  {
    [FieldOffset(0)] public ushort first;
    [FieldOffset(2)] public int second;
}

Any thoughts on how to achieve this?

[EDIT: See also my question on how to deal with the bytes when they are big endian.]


Solution

  • We have done this quite a bit as we talk directly to hardware via bytes over serial.

    Given the struct definition

    [StructLayout(LayoutKind.Explicit, Size=FOO_SIZE)]
    public struct Foo  {
        [FieldOffset(0)] public ushort first;
        [FieldOffset(2)] public int second;
    }
    

    You can use a class like this to perform the conversion

    public class ByteArrayToStruct<StructType>
    {
        public StructType ConvertToStruct(int size, byte[] thebuffer)
        {
            try
            {
                int theSize = size;
                IntPtr ptr1 = Marshal.AllocHGlobal(theSize);
                Marshal.Copy(thebuffer, 0, ptr1, theSize);
                StructType theStruct = (StructType)Marshal.PtrToStructure(ptr1, typeof(StructType));
                Marshal.FreeHGlobal(ptr1);
                return theStruct;
            }
            catch (Exception)
            {
                return default(StructType);
            }
        }
    }
    

    Conversely, you could also create List from the array and do something like the following:

    ushort first = BitConverter.ToInt16(myList.ToArray(), 0);
    myList.RemoveRange(0, sizeof(ushort));
    [...]
    

    This would essentially be keeping the relevant data at the "head" of the list, so you wont have to keep track of the position in the array.