简体   繁体   English

如何将包含数组的结构转换为字节数组?

[英]How to convert a structure that contains an array to a byte array?

How do I convert a structure that contains an array to a byte array in C#?如何将包含数组的结构转换为 C# 中的字节数组?

There was a question here about a struct without array.有一个问题在这里关于无阵列的结构。

But if the struct contains an array like this:但是如果结构包含这样的数组:

public struct DiObject
{
    public byte Command; 
    public byte ErrorClass; 
    public byte Reserved; 
    public byte Flags; 
}

public struct MyPacket
{
    public uint ProtocolIdentifier; 
    public uint NumDi;    
    public DiObject[] Di; 
}

It results with an access violation exception when converting the struct in a byte:将结构体转换为字节时,会导致访问冲突异常:

private static byte[] GetBytes(MyPacket packet, int packetSize)
{
    var data = new byte[packetSize];
    var ptr = Marshal.AllocHGlobal(packetSize);

    // ==== Access violation exception occurs here ====
    Marshal.StructureToPtr(packet, ptr, true);

    Marshal.Copy(ptr, data, 0, packetSize);
    Marshal.FreeHGlobal(ptr);
    return data;
}

My goal is to send a message in bytes in a message queue with MSMQ.我的目标是使用 MSMQ 在消息队列中以字节为单位发送消息。

Here the complete code that compiles and reproduce the problem.这里是编译和重现问题的完整代码。

using System;
//using System.IO;
//using System.Messaging;
using System.Runtime.InteropServices;

namespace StructToBytes
{
    // 4 bytes
    [Serializable]
    public struct DiObject
    {
        public byte Command; 
        public byte ErrorClass; 
        public byte Reserved; 
        public byte Flags; 
    }

    // 8 + (numDi*4) bytes
    [Serializable]
    public struct MyPacket
    {
        public uint ProtocolIdentifier; 
        public uint NumDi;    
        public DiObject[] Di; 
    }

    internal class Program
    {
        private static byte[] GetBytes(MyPacket packet, int packetSize)
        {
            var data = new byte[packetSize];
            var ptr = Marshal.AllocHGlobal(packetSize);

            // ==== Access violation exception occurs here ====
            Marshal.StructureToPtr(packet, ptr, true);

            Marshal.Copy(ptr, data, 0, packetSize);
            Marshal.FreeHGlobal(ptr);
            return data;
        }

        private static MyPacket FromBytes(byte[] data)
        {
            var packet = new MyPacket();
            var dataSize = Marshal.SizeOf(packet);
            var ptr = Marshal.AllocHGlobal(dataSize);
            Marshal.Copy(data, 0, ptr, dataSize);
            packet = (MyPacket) Marshal.PtrToStructure(ptr, packet.GetType());
            Marshal.FreeHGlobal(ptr);
            return packet;
        }

        private static void Main(string[] args)
        {
            const string queuePath = @".\private$\test_msmq";

            // Create the packet
            var packet = new MyPacket();

            // 8 bytes
            packet.ProtocolIdentifier = 1;
            packet.NumDi = 2;

            // 8 bytes
            packet.Di = new DiObject[packet.NumDi];
            packet.Di[0].Command = 2;
            packet.Di[0].ErrorClass = 3;
            packet.Di[0].Flags = 4;
            packet.Di[0].Reserved = 5;
            packet.Di[1].Command = 6;
            packet.Di[1].ErrorClass = 7;
            packet.Di[1].Flags = 8;
            packet.Di[1].Reserved = 9;

            // Convert the struct in bytes
            const int packetSize = 16;
            var packetBytes = GetBytes(packet, packetSize);

            // Create the message
            /*
            var msg = new Message();
            msg.BodyStream = new MemoryStream(packetBytes);

            // Open or create the message queue
            if (!MessageQueue.Exists(queuePath))
                MessageQueue.Create(queuePath);

            // Open the queue
            var q = new MessageQueue(queuePath); // {Formatter = new BinaryMessageFormatter()};

            // Send the message to the queue
            q.Send(msg);
            */
        }
    }
}

The problem lies with wrong assumption about how structure is represented in C#问题在于关于如何在 C# 中表示结构的错误假设

// 8 + (numDi*4) bytes
[Serializable]
public struct MyPacket
{
    public uint ProtocolIdentifier;
    public uint NumDi;
    public DiObject[] Di;
}

The assumption that size of public DiObject[] Di member is numDi * 4 is not true. public DiObject[] Di成员的大小是numDi * 4的假设是不正确的。 In place of this field there is a pointer to the array of structures.代替该字段的是指向结构数组的指针。 Array is a class in .NET and is not included in place in structure declaration. Array 是 .NET 中的一个类,未包含在结构声明中。

To solve this problem one can use fixed arrays.为了解决这个问题,可以使用固定数组。 I understand that the idea behind the design is to get variable length array and it is presented in next code listing.我知道设计背后的想法是获取可变长度数组,它在下一个代码清单中介绍。

This code does not raise AccessViolationException during executin:此代码在执行期间不会引发 AccessViolationException:

using System;
using System.IO;
using System.Messaging;
using System.Runtime.InteropServices;

namespace StructToBytes
{
    // 4 bytes
    [Serializable]
    [StructLayout(LayoutKind.Explicit)]
    public unsafe struct DiObject
    {
        [FieldOffset(0)]
        public byte Command;

        [FieldOffset(1)]
        public byte ErrorClass;

        [FieldOffset(2)]
        public byte Reserved;

        [FieldOffset(3)]
        public byte Flags;
    }

    // 8 + (numDi*4) bytes
    [Serializable]
    public unsafe struct MyPacket
    {
        public uint ProtocolIdentifier;
        public uint NumDi;
        public fixed byte Di[2 * 4];
    }

    internal unsafe class Program
    {
        private static byte[] GetBytes(MyPacket packet, int packetSize)
        {
            var data = new byte[packetSize];
            var ptr = Marshal.AllocHGlobal(packetSize);

            // ==== Access violation exception occurs here ====
            Marshal.StructureToPtr(packet, ptr, true);

            Marshal.Copy(ptr, data, 0, packetSize);
            Marshal.FreeHGlobal(ptr);
            return data;
        }

        private static MyPacket FromBytes(byte[] data)
        {
            var packet = new MyPacket();
            var dataSize = Marshal.SizeOf(packet);
            var ptr = Marshal.AllocHGlobal(dataSize);
            Marshal.Copy(data, 0, ptr, dataSize);
            packet = (MyPacket)Marshal.PtrToStructure(ptr, packet.GetType());
            Marshal.FreeHGlobal(ptr);
            return packet;
        }

        private static void Main(string[] args)
        {
            const string queuePath = @".\private$\test_msmq";

            // Create the packet
            var packet = new MyPacket();

            // 8 bytes
            packet.ProtocolIdentifier = 1;
            packet.NumDi = 2;

            // 8 bytes
            // packet.Di = new DiObject[packet.NumDi];
            packet.Di[0] = 2;
            packet.Di[1] = 3;
            packet.Di[2] = 4;
            packet.Di[3] = 5;
            packet.Di[4] = 6;
            packet.Di[5] = 7;
            packet.Di[6] = 8;
            packet.Di[7] = 9;

            // Convert the struct in bytes
            int packetSize = Marshal.SizeOf<MyPacket>();
            var packetBytes = GetBytes(packet, packetSize);

            // Create the message

            var msg = new Message();
            msg.BodyStream = new MemoryStream(packetBytes);

            // Open or create the message queue
            if (!MessageQueue.Exists(queuePath))
                MessageQueue.Create(queuePath);

            // Open the queue
            var q = new MessageQueue(queuePath); // {Formatter = new BinaryMessageFormatter()};

            // Send the message to the queue
            q.Send(msg);

        }
    }
}

Code below provides efficient conversion to byte array and from byte array for MyPacket struct with variable internal array size.下面的代码为具有可变内部数组大小的 MyPacket 结构提供了到字节数组和从字节数组的有效转换。 Implementation avoids casts and bounds checks by using unsafe pointer arithmetic.实现通过使用不安全的指针算法来避免强制转换和边界检查。

using System;
using System.IO;
using System.Messaging;
using System.Runtime.InteropServices;

namespace StructToBytes
{
    // 4 bytes
    [Serializable]
    [StructLayout(LayoutKind.Explicit)]
    public unsafe struct DiObject
    {
        [FieldOffset(0)]
        public byte Command;

        [FieldOffset(1)]
        public byte ErrorClass;

        [FieldOffset(2)]
        public byte Reserved;

        [FieldOffset(3)]
        public byte Flags;
    }

    [Serializable]
    public unsafe struct MyPacket
    {
        public uint ProtocolIdentifier;
        public uint NumDi;
        public DiObject[] Di;

        public byte[] ToBytes()
        {
            byte[] buffer = new byte[NumDi];

            fixed(DiObject* pDi = Di)
            fixed(byte* pBuff = buffer)
            {
                var pBuffDi = (DiObject*)pBuff;
                var pDiPtr = pDi;
                for (int i = 0; i < NumDi; i++)
                    *pBuffDi++ = *pDiPtr++;
            }
            return buffer;
        }

        public static MyPacket Create(byte[] buffer)
        {
            // argument checking code here

            var packet = new MyPacket();
            packet.ProtocolIdentifier = buffer[0];
            packet.NumDi = buffer[1];
            packet.Di = new DiObject[packet.NumDi];

            fixed (byte* pBuf = buffer)
            fixed (DiObject* pDi = packet.Di)
            {
                byte* pBufPtr = pBuf;
                pBufPtr += 2;
                var pBufDi = (DiObject*)pBufPtr;
                var pDiPtr = pDi;

                for (int i = 0; i < packet.NumDi; i++)
                    *pDiPtr++ = *pBufDi++;
            }

            return packet;
        }
    }

    internal unsafe class Program
    {

        private static void Main(string[] args)
        {
            const string queuePath = @".\private$\test_msmq";

            // Create the packet
            var packet = new MyPacket();

            // 8 bytes
            packet.ProtocolIdentifier = 1;
            packet.NumDi = 5;

            // 8 bytes
            packet.Di = new DiObject[packet.NumDi];
            packet.Di[0].Command = 2;
            packet.Di[0].ErrorClass = 3;
            packet.Di[0].Flags = 4;
            packet.Di[0].Reserved = 5;
            packet.Di[1].Command = 6;
            packet.Di[1].ErrorClass = 7;
            packet.Di[1].Flags = 8;
            packet.Di[1].Reserved = 9;
            packet.Di[2].Command = 6;
            packet.Di[2].ErrorClass = 7;
            packet.Di[2].Flags = 8;
            packet.Di[2].Reserved = 9;
            packet.Di[3].Command = 6;
            packet.Di[3].ErrorClass = 7;
            packet.Di[3].Flags = 8;
            packet.Di[3].Reserved = 9;

            // Create the message

            var msg = new Message();
            msg.BodyStream = new MemoryStream(packet.ToBytes());

            // Open or create the message queue
            if (!MessageQueue.Exists(queuePath))
                MessageQueue.Create(queuePath);

            // Open the queue
            var q = new MessageQueue(queuePath);

            // Send the message to the queue
            q.Send(msg);

        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM