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

前端 未结 14 819
难免孤独
难免孤独 2020-11-22 09:59

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

I have defined a structure like this:

public struct CIFSPacket
{
    public uint protocolIdentifi         


        
相关标签:
14条回答
  • 2020-11-22 10:13

    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);
    
    0 讨论(0)
  • 2020-11-22 10:15

    @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);
        }
    
    0 讨论(0)
  • 2020-11-22 10:17

    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.

    0 讨论(0)
  • 2020-11-22 10:17

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

    http://geekswithblogs.net/taylorrich/archive/2006/08/21/88665.aspx

    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.

    0 讨论(0)
  • 2020-11-22 10:18

    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());
    
    0 讨论(0)
  • 2020-11-22 10:19

    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.

    0 讨论(0)
提交回复
热议问题