C# 如何在C中将结构转换为字节数组?

C# 如何在C中将结构转换为字节数组?,c#,struct,C#,Struct,如何在C中将结构转换为字节数组 我定义了这样一个结构: 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 fla

如何在C中将结构转换为字节数组

我定义了这样一个结构:

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;
}
struct MyStruct {
        public int Value1;
        public int Value2;
        //.. blah blah blah
    }

    byte[] bytes = ConvertToBytes(new MyStruct());
在我的main方法中,我创建了它的一个实例并为其赋值:

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";
现在我想用套接字发送这个包。为此,我需要将结构转换为字节数组。我怎么做

我的全部代码如下

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);
}

代码片段是什么?

我将看看BinaryReader和BinaryWriter类。我最近不得不将数据序列化到一个字节数组并返回,只有在我自己基本上重写了这些类之后才能找到它们


该页面上也有一个很好的示例。

看起来像是某个外部库的预定义C级结构。元帅是你的朋友。检查:

对于初学者来说,如何处理这个问题。请注意,您可以使用属性定义字节布局和字符串处理之类的内容。实际上,这是一个非常好的方法


BinaryFormatter和MemoryStream都没有这样做。

看看这些方法:

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);
}
这是一个无耻的副本,另一个线程,我发现谷歌


更新:有关更多详细信息,请查看

这相当简单,使用编组

头条

作用

并将其转换回:

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;
}
在您的结构中,需要将其放在字符串之前

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string Buffer;
并确保SizeConst尽可能大

你可能应该读一下:

您可以使用封送结构toptr、ptrToStructure和Marshal.copy,但这取决于平台形式

序列化包括用于自定义序列化的函数

public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext) 
SerializationInfo包括用于序列化每个成员的函数

BinaryWriter和BinaryReader还包含保存/加载到字节数组流的方法

请注意,您可以从字节数组创建MemoryStream,也可以从MemoryStream创建字节数组

您可以在结构上创建方法Save和新方法:

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

然后选择要保存/加载到流->字节数组的成员。

这可以非常直接地完成

使用[StructLayoutKind.Explicit]显式定义结构

此代码只能在不安全的上下文中编写。完成后,您必须释放addr

Marshal.FreeHGlobal(addr);

如果你真的想让它在Windows上运行得更快,你可以使用带有CopyMemory的不安全代码。CopyMemory大约快5倍,例如800MB的数据通过编组复制需要3秒,而通过CopyMemory复制只需要0.6秒。此方法限制您仅使用实际存储在struct blob本身中的数据,例如数字或固定长度字节数组

    [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;
    }

@Abdel Olakara answer donese不适用于.net 3.5,应修改如下:

    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);
    }

这应该可以很快做到,对吗?

因为主要答案是使用CIFSPacket类型,这在C中已经不可用或不再可用,所以我编写了正确的方法:

    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;
    }

经过测试,它们可以工作。

Vicent代码的变体,只需少分配一次内存:

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;
}

我使用GCHandle固定内存,然后直接将其地址与h.addrofpindedObject一起使用。

这里的示例仅适用于纯blittable类型,例如,可以在C中直接memcpy的类型

示例-众所周知的64位结构

[StructLayout(LayoutKind.Sequential)]  
public struct Voxel
{
    public ushort m_id;
    public byte m_red, m_green, m_blue, m_alpha, m_matid, m_custom;
}
定义与此完全相同,结构将自动打包为64位

现在我们可以创建体素的体积:

Voxel[,,] voxels = new Voxel[16,16,16];
并将它们全部保存到字节数组中:

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.
但是,由于OP想知道如何转换结构本身,我们的体素结构可以有以下方法ToBytes:


我提出了一种不同的方法,可以在不固定长度的情况下转换任何结构,但是生成的字节数组会有更多的开销

下面是一个示例结构:

如您所见,所有这些结构都需要添加固定长度属性。这通常会占用比所需更多的空间。请注意,LayoutKind.Sequential是必需的,因为我们希望反射在提取FieldInfo时始终为我们提供相同的顺序。我的灵感来自TLV类型的长度值。让我们看看代码:

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();
    }
}
当我们想将其转换回原始结构时,我们只需读回长度并直接将其转储回BinaryFormatter,BinaryFormatter反过来将其转储回结构

这两个函数是通用的,应该可以用于任何结构,我在我的C项目中测试了上述代码,其中我有一个服务器和一个客户端,通过NamedPipeStream连接和通信,我将结构作为字节数组从一个转发到另一个,并将其转换回来


我相信我的方法可能会更好,因为它不固定结构本身的长度,唯一的开销就是结构中每个字段的int。BinaryFormatter生成的字节数组中也有一些微小的开销,但除此之外,开销并不多。

我知道这已经很晚了,但使用C 7.3,您可以对非托管结构执行此操作 或任何其他不受管理的int、bool等…:

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;
    }


最后一行MyPing.SendIt进行了一次更正,它将字节数组作为参数;这是发送而不是发送给……嗨,佩塔,我没听懂你的意思……接受你之前问题的一些答案可能会更好。我想,对你期望的输出更具体一点会有帮助;有很多方法可以将其转换为字节[]。。。我们可能会对其中的大部分做出一些假设,您想要字段的字段顺序网络字节顺序固定大小表示-但是字符串呢?如果选择Marshall选项,请注意Grand-Endian和Little-Endian以及大约32位/64位。我现在使用编组将结构转换为字节数组如何检查是否从套接字获得响应? 怎么查呢?@Alastair,我漏掉了!!谢谢你指点。。我已经更新了我的答案。这个选项取决于平台-注意Grand-Endian和Little-Endian以及大约32位/64位。@Abdel,并且-1不见了:执行Alloc,尝试包装中间位,然后在finally中释放是否有意义?事情似乎不太可能失败,但如果失败了,记忆会被释放吗?谢谢Vincet。应在发送字节[]?后调用GetBytes??frombytes方法正在发送字节吗?我有点困惑,buddy?GetBytes将从结构转换为数组。FromBytes将字节转换回您的结构。这从函数签名中可以明显看出。@Swapnil这是另一个问题,您应该单独提问。您应该考虑在套接字上完成两个CE教程。只需搜索Google。在fromBytes方法中,不需要分配两次CIFSPacket。Marshal.SizeOf将愉快地将类型作为参数,Marshal.PtrToStructure分配一个新的托管对象。请注意,在某些情况下,函数«StructureToPtr»会引发异常。这可以通过将«false»而不是«true»传递给Marshal.StructureToPtrstr,ptr,false;来解决;。但需要指出的是,我使用的是封装到泛型的函数,不过……在对固定大小的集合执行显式有序操作时,可能应该使用数组和for循环。数组的大小是固定的,for循环的大小是固定的,foreach不能保证按照您期望的顺序排列,除非您知道列表类型及其枚举数的底层实现,并且它永远不会改变。例如,可以将枚举数定义为从末尾开始向后。GCHandle版本要好得多。应该删除where T:struct,否则它会抱怨T不是不可为null的类型。如果该结构具有不可blittable的数据,例如array@joe你是对的。代码是为给定的结构编写的,该结构只包含可blittable类型和字符串。通常,当人们试图处理此类内容时,他们还关心序列化性能。理论上,任何结构数组都可以重新解释为字节数组,而不需要昂贵的序列化和复制。。这不是跨平台友好的,它只使用Windows内核32.dll。但话说回来,它写于2014年加上要求结构是连续的。但是如果在Windows上,这实际上还是更快吗?
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.
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();
[StructLayout(LayoutKind.Sequential)]
public class HelloWorld
{
    public MyEnum enumvalue;
    public string reqtimestamp;
    public string resptimestamp;
    public string message;
    public byte[] rawresp;
}
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();
    }
}
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));
            }
        }
    }
}
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;
    }
struct MyStruct {
        public int Value1;
        public int Value2;
        //.. blah blah blah
    }

    byte[] bytes = ConvertToBytes(new MyStruct());