How to convert a structure to a byte array in C#?
Asked Answered
S

15

97

How do I convert a structure to a byte array in C#?

I have defined a structure like this:

public struct CIFSPacket
{
    public uint protocolIdentifier; //The value must be "0xFF+'SMB'".
    public byte command;

    public byte errorClass;
    public byte reserved;
    public ushort error;

    public byte flags;

    //Here there are 14 bytes of data which is used differently among different dialects.
    //I do want the flags2. However, so I'll try parsing them.
    public ushort flags2;

    public ushort treeId;
    public ushort processId;
    public ushort userId;
    public ushort multiplexId;

    //Trans request
    public byte wordCount;//Count of parameter words defining the data portion of the packet.
    //From here it might be undefined...

    public int parametersStartIndex;

    public ushort byteCount; //Buffer length
    public int bufferStartIndex;

    public string Buffer;
}

In my main method, I create an instance of it and assign values to it:

CIFSPacket packet = new CIFSPacket();
packet.protocolIdentifier = 0xff;
packet.command = (byte)CommandTypes.SMB_COM_NEGOTIATE;
packet.errorClass = 0xff;
packet.error = 0;
packet.flags = 0x00;
packet.flags2 = 0x0001;
packet.multiplexId = 22;
packet.wordCount = 0;
packet.byteCount = 119;

packet.Buffer = "NT LM 0.12";

Now I want to send this Packet by socket. For that, I need to convert the structure to a byte array. How can I do it?

My full code is as follows.

static void Main(string[] args)
{

  Socket MyPing = new Socket(AddressFamily.InterNetwork,
  SocketType.Stream , ProtocolType.Unspecified ) ;


  MyPing.Connect("172.24.18.240", 139);

    //Fake an IP Address so I can send with SendTo
    IPAddress IP = new IPAddress(new byte[] { 172,24,18,240 });
    IPEndPoint IPEP = new IPEndPoint(IP, 139);

    //Local IP for Receiving
    IPEndPoint Local = new IPEndPoint(IPAddress.Any, 0);
    EndPoint EP = (EndPoint)Local;

    CIFSPacket packet = new CIFSPacket();
    packet.protocolIdentifier = 0xff;
    packet.command = (byte)CommandTypes.SMB_COM_NEGOTIATE;
    packet.errorClass = 0xff;
    packet.error = 0;
    packet.flags = 0x00;
    packet.flags2 = 0x0001;
    packet.multiplexId = 22;
    packet.wordCount = 0;
    packet.byteCount = 119;

    packet.Buffer = "NT LM 0.12";

    MyPing.SendTo(It takes byte array as parameter);
}

What would a code snippet be?

Solarize answered 19/7, 2010 at 6:18 Comment(7)
One correction at last line MyPing.Send(It takes byte array as parameter); It is Send not SendTo......Solarize
Hi Petar, I didn't get you ...Solarize
It might be good to accept some answers to your previous questions.Vellavelleity
I suspect it would help to be a bit more specific about the output you expect; there are lots of ways of turning that into a byte[]... We can probably make some assumptions about most of it, that you want the field-order network-byte-order fixed-size representations of the fields - but what about the string?Bargello
Take care about Grand Endian and Little endian and about 32 Bits / 64 bits if you select Marshall option.Panacea
Re: serialization - My understanding is that the OP wants just the data, i.e., an array of bytes representing the ints and bytes and other data fields of his struct. But serialization would also add extra bytes (metadata) to facilitate future deserialization.Contessacontest
For a case that does not contain string or other non-blittable types, and if you're willing to use unsafe code, I've posted a technique that involves mapping a struct on top of the fields in a byte array, on this thread: #7030650Gargoyle
R
154

This is fairly easy, using marshalling.

Top of file

using System.Runtime.InteropServices

Function

byte[] getBytes(CIFSPacket str) {
    int size = Marshal.SizeOf(str);
    byte[] arr = new byte[size];

    IntPtr ptr = IntPtr.Zero;
    try
    {
        ptr = Marshal.AllocHGlobal(size);
        Marshal.StructureToPtr(str, ptr, true);
        Marshal.Copy(ptr, arr, 0, size);
    }
    finally
    {
        Marshal.FreeHGlobal(ptr);
    }
    return arr;
}

And to convert it back:

CIFSPacket fromBytes(byte[] arr)
{
    CIFSPacket str = new CIFSPacket();

    int size = Marshal.SizeOf(str);
    IntPtr ptr = IntPtr.Zero;
    try
    {
        ptr = Marshal.AllocHGlobal(size);

        Marshal.Copy(arr, 0, ptr, size);

        str = (CIFSPacket)Marshal.PtrToStructure(ptr, str.GetType());
    }
    finally
    {
        Marshal.FreeHGlobal(ptr);
    }
    return str;
}

In your structure, you will need to put this before a string

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string Buffer;

And make sure SizeConst is as big as your biggest possible string.

And you should probably read this: http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

Report answered 19/7, 2010 at 6:47 Comment(15)
Thanks Vincet. GetBytes() should be called after sending the byte[]?? and frombytes() method is sending the bytes ? I am little confused buddy ?Solarize
GetBytes converts from your structure to an array. FromBytes converts from the Bytes back to your structure. This is evident from the function signatures.Report
@Vincet : That's fine Vincet.Now i want to get the response back .How i can do that ? How can check my response ?Solarize
@Vincet : Now i have to get response from Socket that what ever CIFS packet i have send it has got it ? How to check that ?Solarize
@Swapnil That is another question, which you should ask separately. You should consider completing a couple of CE tutorials on sockets. Just search Google.Report
I love this! Thanks! Also according to the link you yourself provided at the end, the struct should be decorated by [StructLayout(LayoutKind.Sequential)]. Please add that to your answer too. And thanks again for this!Festoon
i have done this but i got exception in runtime at Marshal.StructureToPtr(str, ptr, true);Brantbrantford
In your fromBytes method there is no need to allocate the CIFSPacket twice. Marshal.SizeOf will happily take a Type as a parameter and Marshal.PtrToStructure allocates a new managed object.Fuzee
Note that in some circumstances the function «StructureToPtr» throws an exception. This could be fixed with passing «false» instead of «true» to Marshal.StructureToPtr(str, ptr, false);. But need to mention that I am using the functions wrapped to a generic, though…Fussy
Consider checking out my newly submitted answer below without the use of Marshal and without the fixing the size of the fields. https://mcmap.net/q/197179/-how-to-convert-a-structure-to-a-byte-array-in-cArticle
What about system endianness? A big-endian system will reverse the bytes in all of those variables, which is less than ideal if the structs in question are file headers. Is there any way to avoid that?Sapajou
This method actually copies twice (Marshal.StructureToPtr and Marshal.Copy). If it is heavily called, GCHandleType.Pinned is a better solution.Gutter
According to docs, you need to call DestroyStructure before release.Gutter
Keep in mind that SizeOf() functions returns size of struct aligned to 4 bytes, therefore a struct containing for example two bytes and a float will be 8 bytes long. In some cases you can avoid this by changing the order of struct elements.Ravishment
PtrToStructure with GetType as shown here solved my problem dealing with F# struct types. One of the other overloads started to throw an exception saying something about not wanting a class type. Happened in .NET 8 or 7, not sure which, probably 8.Gerigerianna
S
37

If you really want it to be FAST on Windows, you can do it using unsafe code with CopyMemory. CopyMemory is about 5x faster (e.g. 800MB of data takes 3s to copy via marshalling, while only taking .6s to copy via CopyMemory). This method does limit you to using only data which is actually stored in the struct blob itself, e.g. numbers, or fixed length byte arrays.

    [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
    private static unsafe extern void CopyMemory(void *dest, void *src, int count);

    private static unsafe byte[] Serialize(TestStruct[] index)
    {
        var buffer = new byte[Marshal.SizeOf(typeof(TestStruct)) * index.Length];
        fixed (void* d = &buffer[0])
        {
            fixed (void* s = &index[0])
            {
                CopyMemory(d, s, buffer.Length);
            }
        }

        return buffer;
    }
Shabby answered 4/6, 2014 at 2:17 Comment(4)
As a heads up to those who are reading this answer.. This isn't Cross Platform friendly (it uses Windows only kernel32.dll). But then again, it was written in 2014. :)Brownout
Plus require the structure to be sequential.Halda
However if on Windows is this actually stil faster?Montreal
yes it is still the fastest way copy the memory data and get back it's byte arrays in WindowsPram
J
28

Have a look at these methods:

byte [] StructureToByteArray(object obj)
{
    int len = Marshal.SizeOf(obj);

    byte [] arr = new byte[len];

    IntPtr ptr = Marshal.AllocHGlobal(len);

    Marshal.StructureToPtr(obj, ptr, true);

    Marshal.Copy(ptr, arr, 0, len);

    Marshal.FreeHGlobal(ptr);

    return arr;
}

void ByteArrayToStructure(byte [] bytearray, ref object obj)
{
    int len = Marshal.SizeOf(obj);

    IntPtr i = Marshal.AllocHGlobal(len);

    Marshal.Copy(bytearray,0, i,len);

    obj = Marshal.PtrToStructure(i, obj.GetType());

    Marshal.FreeHGlobal(i);
}

This is a shameless copy of another thread which I found upon Googling!

Update : For more details, check the source

Jointless answered 19/7, 2010 at 6:36 Comment(5)
I have converterd Structure to byte array using Marshalling now how i can check whether I am getting the response from socket ? How to check that ?Solarize
@Alastair, I missed that out!! Thanks for pointing it.. I have updated my answer.Jointless
This option is platform dependent - Take care about Grand Endian and Little endian and about 32 Bits / 64 bits.Panacea
@Abdel, and the -1 is gone :)Locklear
Would it make sense to perform the Alloc, wrap the middle bit in a try, and then put Free inside of a finally? It seems unlikely that things would fail, but if they do, does the memory ever get released?Bargain
W
22

Variant of the code of Vicent with one less memory allocation:

public static byte[] GetBytes<T>(T str)
{
    int size = Marshal.SizeOf(str);

    byte[] arr = new byte[size];

    GCHandle h = default(GCHandle);

    try
    {
        h = GCHandle.Alloc(arr, GCHandleType.Pinned);

        Marshal.StructureToPtr<T>(str, h.AddrOfPinnedObject(), false);
    }
    finally
    {
        if (h.IsAllocated)
        {
            h.Free();
        }
    }

    return arr;
}

public static T FromBytes<T>(byte[] arr) where T : struct
{
    T str = default(T);

    GCHandle h = default(GCHandle);

    try
    {
        h = GCHandle.Alloc(arr, GCHandleType.Pinned);

        str = Marshal.PtrToStructure<T>(h.AddrOfPinnedObject());

    }
    finally
    {
        if (h.IsAllocated)
        {
            h.Free();
        }
    }

    return str;
}

I use GCHandle to "pin" the memory and then I use directly its address with h.AddrOfPinnedObject().

Whirlwind answered 1/3, 2016 at 7:58 Comment(3)
Should remove where T : struct otherwise it will complaint about T being pass is not a non-nullable type.Article
GCHandle.Alloc will fail if the struct has non-blittable data, e.g. an arrayGutter
@Gutter You are right. The code was written for the given structure, that contained only blittable types and string.Whirlwind
M
7

I know this is really late, but with C# 7.3 you can do this for unmanaged structs or anything else that's unmanged (int, bool etc...):

public static unsafe byte[] ConvertToBytes<T>(T value) where T : unmanaged {
        byte* pointer = (byte*)&value;

        byte[] bytes = new byte[sizeof(T)];
        for (int i = 0; i < sizeof(T); i++) {
            bytes[i] = pointer[i];
        }

        return bytes;
    }

Then use like this:

struct MyStruct {
        public int Value1;
        public int Value2;
        //.. blah blah blah
    }

    byte[] bytes = ConvertToBytes(new MyStruct());
Mischief answered 15/6, 2020 at 0:12 Comment(0)
F
5

As the main answer is using CIFSPacket type, which is not (or no longer) available in C#, I wrote correct methods:

    static byte[] getBytes(object str)
    {
        int size = Marshal.SizeOf(str);
        byte[] arr = new byte[size];
        IntPtr ptr = Marshal.AllocHGlobal(size);

        Marshal.StructureToPtr(str, ptr, true);
        Marshal.Copy(ptr, arr, 0, size);
        Marshal.FreeHGlobal(ptr);

        return arr;
    }

    static T fromBytes<T>(byte[] arr)
    {
        T str = default(T);

        int size = Marshal.SizeOf(str);
        IntPtr ptr = Marshal.AllocHGlobal(size);

        Marshal.Copy(arr, 0, ptr, size);

        str = (T)Marshal.PtrToStructure(ptr, str.GetType());
        Marshal.FreeHGlobal(ptr);

        return str;
    }

Tested, they work.

Fillet answered 15/5, 2015 at 20:38 Comment(0)
P
2

You can use Marshal (StructureToPtr, ptrToStructure), and Marshal.copy but this is plataform dependent.


Serialization includes Functions to Custom Serialization.

public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext) 

SerializationInfo include functions to serialize each member.


BinaryWriter and BinaryReader also contains methods to Save / Load to Byte Array (Stream).

Note that you can create a MemoryStream from a Byte Array or a Byte Array from a MemoryStream.

You can create a method Save and a method New on your structure:

   Save(Bw as BinaryWriter)
   New (Br as BinaryReader)

Then you select members to Save / Load to Stream -> Byte Array.

Panacea answered 19/7, 2010 at 7:5 Comment(0)
G
2

Almost all of the answers here use Marshal.StructureToPtr, which might be good for P/Invoke but it is very slow, and doesn't even always represent the actual raw content of the value. @Varscott128's answer is much better but it also contains an explicit byte copying, which is not necessary.

For unmanaged structs (structs without managed references) all you need is to reinterpret the allocated result array so a simple assignment does the trick (works even for huge structs):

.NET (Core) Solution:

If you can use the Unsafe class, then the solution is really easy. The unsafe modifier is required only due to sizeof(T).

public static unsafe byte[] SerializeValueType<T>(in T value) where T : unmanaged
{
    byte[] result = new byte[sizeof(T)];
    Unsafe.As<byte, T>(ref result[0]) = value;
    return result;
}

// Note: Validation is omitted for simplicity
public static T DeserializeValueType<T>(byte[] data) where T : unmanaged
    => Unsafe.As<byte, T>(ref data[0]);

.NET Framework/Standard Solution:

public static unsafe byte[] SerializeValueType<T>(in T value) where T : unmanaged
{
    byte[] result = new byte[sizeof(T)];
    fixed (byte* dst = result)
        *(T*)dst = value;
    return result;
}

// Note: Validation is omitted for simplicity
public static unsafe T DeserializeValueType<T>(byte[] data) where T : unmanaged
{
    fixed (byte* src = data)
        return *(T*)src;
}

See the complete code with validations here.

Remarks:

The OP's example contains a string, which is a reference type so the solution above cannot be used for that. And if you can't use generic methods for some reason things start to get more complicated, especially for .NET Framework (but non-generic size calculation is a pain also on the Core platform). If performance does not matter, then you can revert to Marshal.SizeOf and StructureToPtr as suggested by several other answers, or feel free to use the BinarySerializer.SerializeValueType method from my library that I linked also for the examples above (NuGet).

Gerge answered 26/10, 2021 at 11:10 Comment(0)
G
1

This can be done very straightforwardly.

Define your struct explicitly with [StructLayout(LayoutKind.Explicit)]

int size = list.GetLength(0);
IntPtr addr = Marshal.AllocHGlobal(size * sizeof(DataStruct));
DataStruct *ptrBuffer = (DataStruct*)addr;
foreach (DataStruct ds in list)
{
    *ptrBuffer = ds;
    ptrBuffer += 1;
}

This code can only be written in an unsafe context. You have to free addr when you're done with it.

Marshal.FreeHGlobal(addr);
Galliot answered 17/12, 2013 at 10:26 Comment(1)
When doing explicit ordered operations on a fixed size collection, you should probably use an array and for-loop. The array because it's fixed size, and the for-loop because foreach is not guaranteed to be in the order you expect, unless you know the underlying implementation of your type of list and it's enumerator, and that it will never change. One could define the enumerator to start from the end and go backward, for example.Salenasalene
A
1

I've come up with a different approach that could convert any struct without the hassle of fixing length, however the resulting byte array would have a little bit more overhead.

Here is a sample struct:

[StructLayout(LayoutKind.Sequential)]
public class HelloWorld
{
    public MyEnum enumvalue;
    public string reqtimestamp;
    public string resptimestamp;
    public string message;
    public byte[] rawresp;
}

As you can see, all those structures would require adding the fixed length attributes. Which could often ended up taking up more space than required. Note that the LayoutKind.Sequential is required, as we want reflection to always gives us the same order when pulling for FieldInfo. My inspiration is from TLV Type-Length-Value. Let's have a look at the code:

public static byte[] StructToByteArray<T>(T obj)
{
    using (MemoryStream ms = new MemoryStream())
    {
        FieldInfo[] infos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance);
        foreach (FieldInfo info in infos)
        {
            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream inms = new MemoryStream()) {

                bf.Serialize(inms, info.GetValue(obj));
                byte[] ba = inms.ToArray();
                // for length
                ms.Write(BitConverter.GetBytes(ba.Length), 0, sizeof(int));

                // for value
                ms.Write(ba, 0, ba.Length);
            }
        }

        return ms.ToArray();
    }
}

The above function simply uses the BinaryFormatter to serialize the unknown size raw object, and I simply keep track of the size as well and store it inside the output MemoryStream too.

public static void ByteArrayToStruct<T>(byte[] data, out T output)
{
    output = (T) Activator.CreateInstance(typeof(T), null);
    using (MemoryStream ms = new MemoryStream(data))
    {
        byte[] ba = null;
        FieldInfo[] infos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance);
        foreach (FieldInfo info in infos)
        {
            // for length
            ba = new byte[sizeof(int)];
            ms.Read(ba, 0, sizeof(int));

            // for value
            int sz = BitConverter.ToInt32(ba, 0);
            ba = new byte[sz];
            ms.Read(ba, 0, sz);

            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream inms = new MemoryStream(ba))
            {
                info.SetValue(output, bf.Deserialize(inms));
            }
        }
    }
}

When we want to convert it back to its original struct we simply read the length back and directly dump it back into the BinaryFormatter which in turn dump it back into the struct.

These 2 functions are generic and should work with any struct, I've tested the above code in my C# project where I have a server and a client, connected and communicate via NamedPipeStream and I forward my struct as byte array from one and to another and converted it back.

I believe my approach might be better, since it doesn't fix length on the struct itself and the only overhead is just an int for every fields you have in your struct. There are also some tiny bit overhead inside the byte array generated by BinaryFormatter, but other than that, is not much.

Article answered 17/10, 2017 at 21:47 Comment(1)
Generally, when people are trying to deal with such stuff they are also concerned about serialization performance. In theory, any array of structs can be reinterpreted as a byte array without involving expensive serialization and copying.Virology
E
0

I would take a look at the BinaryReader and BinaryWriter classes. I recently had to serialize data to a byte array (and back) and only found these classes after I'd basically rewritten them myself.

http://msdn.microsoft.com/en-us/library/system.io.binarywriter.aspx

There is a good example on that page too.

Endora answered 19/7, 2010 at 6:25 Comment(0)
V
0

Looks like a predefined (C level) structure for some external library. Marshal is your friend. Check:

Link

for a starter how to deal with this. Note that you can - with attributes - define things like byte layout and string handling. VERY nice approach, actually.

Neither BinaryFormatter Nor MemoryStream are done for that.

Veridical answered 19/7, 2010 at 6:35 Comment(0)
V
0

@Abdel Olakara answer donese not work in .net 3.5, should be modified as below:

    public static void ByteArrayToStructure<T>(byte[] bytearray, ref T obj)
    {
        int len = Marshal.SizeOf(obj);
        IntPtr i = Marshal.AllocHGlobal(len);
        Marshal.Copy(bytearray, 0, i, len);
        obj = (T)Marshal.PtrToStructure(i, typeof(T));
        Marshal.FreeHGlobal(i);
    }
Volturno answered 11/6, 2014 at 7:1 Comment(0)
G
0
        Header header = new Header();
        Byte[] headerBytes = new Byte[Marshal.SizeOf(header)];
        Marshal.Copy((IntPtr)(&header), headerBytes, 0, headerBytes.Length);

This should do the trick quickly, right?

Goliard answered 8/3, 2015 at 4:34 Comment(1)
The GCHandle version is far better.Chitterlings
G
0

This example here is only applicable to pure blittable types, e.g., types that can be memcpy'd directly in C.

Example - well known 64-bit struct

[StructLayout(LayoutKind.Sequential)]  
public struct Voxel
{
    public ushort m_id;
    public byte m_red, m_green, m_blue, m_alpha, m_matid, m_custom;
}

Defined exactly like this, the struct will be automatically packed as 64-bit.

Now we can create volume of voxels:

Voxel[,,] voxels = new Voxel[16,16,16];

And save them all to a byte array:

int size = voxels.Length * 8; // Well known size: 64 bits
byte[] saved = new byte[size];
GCHandle h = GCHandle.Alloc(voxels, GCHandleType.Pinned);
Marshal.Copy(h.AddrOfPinnedObject(), saved, 0, size);
h.Free();
// now feel free to save 'saved' to a File / memory stream.

However, since the OP wants to know how to convert the struct itself, our Voxel struct can have following method ToBytes:

byte[] bytes = new byte[8]; // Well known size: 64 bits
GCHandle h = GCHandle.Alloc(this, GCHandleType.Pinned);
Marshal.Copy(hh.AddrOfPinnedObject(), bytes, 0, 8);
h.Free();
Gradygrae answered 27/2, 2017 at 0:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.