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

前端 未结 14 821
难免孤独
难免孤独 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:22

    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

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

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

    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.

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

    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.

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

    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 = Marshal.AllocHGlobal(size);
        Marshal.StructureToPtr(str, ptr, true);
        Marshal.Copy(ptr, arr, 0, size);
        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 = Marshal.AllocHGlobal(size);
    
        Marshal.Copy(arr, 0, ptr, size);
    
        str = (CIFSPacket)Marshal.PtrToStructure(ptr, str.GetType());
        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

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

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

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