使用固定缓冲区将字节数组重新解释为托管结构

我正在寻找将字节数组reinterpret_cast转换为C#结构。我已经阅读了该问题的其他几个答案,其中大多数是关于如何实现重新解释演员表的。我已经确定了一种重新解释演员表的方法,但是在我的演员表转换过程中,我得到的是单个字符而不是字符数组。

例如,我有以下对象:

    public unsafe struct Establish503
    {
        public static Establish503 ReinterpretCast(byte[] message)
        {
            GCHandle handle = GCHandle.Alloc(message,GCHandleType.Pinned);
            Establish503 theStruct = (Establish503)Marshal.PtrToStructure(handle.AddrOfPinnedObject(),typeof(Establish503));
            handle.Free();
            return theStruct;
        }

        public fixed char HMACSignature[32];
        public fixed char accessKey[20];
        public fixed char TradingSystemName[30];
        public fixed char TradingSystemVersion[10];
        public fixed char TradingSystemVendor[10];
    }

由于某种原因,我没有一个字节数组,而是将单个字符放在一个数组中。为什么会这样呢?这是我的本地调试窗口:

使用固定缓冲区将字节数组重新解释为托管结构

如您所见,由于某种原因,它将所有字段都视为char而不是char[]

如果这不是正确的方法,那么我还有什么需要看的吗?我一直在研究Span<T>

编辑:与所选答案的作者Oguz Ozgul进行了进一步讨论后,确定编组将是最好的方法。后续问题是,我将如何处理嵌套结构?以下是我目前的做法。正如Oguz所提到的,对于在类外部定义并包含原始类型的结构,可以排除Marshal属性。然后可以将这些结构用作另一个结构中的字段。我已经解决了定义嵌套结构的方法,类似于定义非嵌套结构的方法。

    [StructLayout(LayoutKind.Sequential,CharSet = CharSet.Ansi,Pack = 1)]
    public struct OrderMassactionReport558
    {
        public const int templateId_ = 558;
        public const int blockSize_ = 103;

        public static OrderMassactionReport558 ReinterpretCast(byte[] message)
        {
            GCHandle handle = GCHandle.Alloc(message,GCHandleType.Pinned);
            OrderMassactionReport558 theStruct = (OrderMassactionReport558)
                Marshal.PtrToStructure(handle.AddrOfPinnedObject(),typeof(OrderMassactionReport558));
            handle.Free();
            return theStruct;
        }

        [MarshalAs(UnmanagedType.U4)]
        public uInt32 seqNum;
        [MarshalAs(UnmanagedType.U8)]
        public uInt64 uUID;
        [MarshalAs(UnmanagedType.ByValArray,SizeConst = 20)]
        private byte[] _senderID;
        public string senderID => System.Text.Encoding.ASCII.GetString(this._senderID);
        [MarshalAs(UnmanagedType.U8)]
        public uInt64 partyDetailsListReqID;
        [MarshalAs(UnmanagedType.U8)]
        public uInt64 transactTime;
        [MarshalAs(UnmanagedType.U8)]
        public uInt64 sendingTimeEpoch;
        [MarshalAs(UnmanagedType.U8)]
        public uInt64 orderRequestID;
        [MarshalAs(UnmanagedType.U8)]
        public uInt64 massactionReportID;
        [MarshalAs(UnmanagedType.ByValArray,SizeConst = 6)]
        private byte[] _securityGroup;
        public string securityGroup => System.Text.Encoding.ASCII.GetString(this._securityGroup);
        [MarshalAs(UnmanagedType.ByValArray,SizeConst = 5)]
        private byte[] _location;
        public string location => System.Text.Encoding.ASCII.GetString(this._location);
        [MarshalAs(UnmanagedType.I4)]
        public Int32NULL securityID;
        [MarshalAs(UnmanagedType.U2)]
        public uInt16NULL delayDuration;
        [MarshalAs(UnmanagedType.U1)]
        public MassactionResponse massactionResponse;
        [MarshalAs(UnmanagedType.U1)]
        public ManualOrdIndReq manualOrderIndicator;
        [MarshalAs(UnmanagedType.U1)]
        public MassactionScope massactionScope;
        [MarshalAs(UnmanagedType.U1)]
        public uInt8 totalAffectedOrders;
        [MarshalAs(UnmanagedType.U1)]
        public Booleanflag lastFragment;
        [MarshalAs(UnmanagedType.U1)]
        public uInt8NULL massactionRejectReason;
        [MarshalAs(UnmanagedType.U1)]
        public uInt8NULL marketSegmentID;
        [MarshalAs(UnmanagedType.U1)]
        public MassCxlReqTyp massCancelRequestType;
        [MarshalAs(UnmanagedType.U1)]
        public SideNULL side;
        [MarshalAs(UnmanagedType.U1)]
        public MassactionOrdTyp ordType;
        [MarshalAs(UnmanagedType.U1)]
        public MassCancelTIF timeInForce;
        [MarshalAs(UnmanagedType.U1)]
        public SplitMsg splitMsg;
        [MarshalAs(UnmanagedType.U1)]
        public BooleanNULL liquidityflag;
        [MarshalAs(UnmanagedType.U1)]
        public Booleanflag possRetransflag;

        [StructLayout(LayoutKind.Sequential,Pack = 1)]
        public struct NoAffectedOrdersEntry
        {
            public const int blockSize_ = 32;
            [MarshalAs(UnmanagedType.ByValArray,SizeConst = 20)]
            private byte[] _origCIOrdID;
            public string origCIOrdID => System.Text.Encoding.ASCII.GetString(this._origCIOrdID);
            public uInt64 AffectedOrderID;
            public uInt32 CxlQuantity;
        }
    }
wqqweqe 回答:使用固定缓冲区将字节数组重新解释为托管结构

这是因为只设置了char数组的第一个元素,而其余的为零(您可以在“内存”窗口中看到它)。

首先,除非我们可以指定确切的字符集,否则尝试用原始二进制数据填充char数组可能会导致意外和不可预测的结果。您可以在结构顶部看到CharSet设置为Ansi,每个字符1个字节。

然后,您可以使用字符串,而不必使用固定的指针char数组,而是按值和确切的大小对它们进行编组。

请告诉我这是否有帮助:

    [StructLayout(LayoutKind.Sequential,CharSet = CharSet.Ansi,Pack = 1)]
    public unsafe struct Establish503
    {
        public static Establish503 ReinterpretCast(byte[] message)
        {
            GCHandle handle = GCHandle.Alloc(message,GCHandleType.Pinned);
            Establish503 theStruct = (Establish503)Marshal.PtrToStructure(handle.AddrOfPinnedObject(),typeof(Establish503));
            handle.Free();
            return theStruct;
        }

        [MarshalAs(UnmanagedType.ByValTStr,SizeConst = 32)]
        public string HMACSignature;
        [MarshalAs(UnmanagedType.ByValTStr,SizeConst = 20)]
        public string AccessKey;
        [MarshalAs(UnmanagedType.ByValTStr,SizeConst = 30)]
        public string TradingSystemName;
        [MarshalAs(UnmanagedType.ByValTStr,SizeConst = 10)]
        public string TradingSystemVersion;
        [MarshalAs(UnmanagedType.ByValTStr,SizeConst = 10)]
        public string TradingSystemVendor;
    }  

更新1:OP在下面的评论中还问了一个附加问题,因此答案得到了更新。

OP想要知道是否将另一个具有Int64字段的结构嵌入到当前结构中。

首先,阅读:https://docs.microsoft.com/en-us/dotnet/framework/interop/default-marshaling-behavior#default-marshaling-for-value-types

因此,我将此新结构添加到了源代码中:

    [StructLayout(LayoutKind.Sequential,Pack = 1)]
    public struct Data
    {
        [MarshalAs(UnmanagedType.I8)]
        public long LongField;
    }

然后,将其嵌入到当前版本中:

    [StructLayout(LayoutKind.Sequential,typeof(Establish503));
            handle.Free();
            return theStruct;
        }

        public Data DataStruct;
        [MarshalAs(UnmanagedType.ByValTStr,SizeConst = 10)]
        public string TradingSystemVendor;
    }

最后,将其从非托管内存中整理回去:

        Establish503 establish503 = Establish503.ReinterpretCast(new byte[] { 
            1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,20
        });
本文链接:https://www.f2er.com/2635961.html

大家都在问