代码之家  ›  专栏  ›  技术社区  ›  bufferz

将可变长度结构转换为byte[]

  •  2
  • bufferz  · 技术社区  · 14 年前

    我正在尝试向硬件设备发送一个网络数据包。我想使用一种灵活的、面向对象的方法,这样我就可以在较高的层次上使用它。数据包有几个可变长度的字段。显然,字节布局非常特殊。

    下面是一个表示我需要发送的数据包的结构:

    [StructLayout(LayoutKind.Sequential)]
    public struct Packet
    {
       public UInt16 Instruction;
       public UInt16 Length; //length of data field
       public UInt32 SessionHandle;
    
       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
       public byte[] SenderContext;
    
       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
       public byte[] MessageData;
    }
    

    MessageData[] 可以是任何长度,作为一个开始,我将它固定为8个字节。

    这是我创建 byte[] 通过插座发送:

    public static byte[] ToBytes(Packet ep)
    {
       Byte[] bytes = new Byte[Marshal.SizeOf(typeof(Packet))];
       GCHandle pinStructure = GCHandle.Alloc(ep, GCHandleType.Pinned);
       try
       {
          Marshal.Copy(pinStructure.AddrOfPinnedObject(), bytes, 0, bytes.Length);
          return bytes;
       }
       finally
       {
          pinStructure.Free();
       }
    }
    

    但我收到:

    ArgumentException : Object contains non-primitive or non-blittable data.
    

    我想设置 SizeConst 在结构中会处理这个?不管怎样,我的损失比这更大,因为硬件设备期望一个可变长度的包,我想利用它。

    我可以一个字节一个字节地手动组合数据包,一切都很好。但我知道必须有更好的方法,我一定走错了路。

    有什么想法吗?

    2 回复  |  直到 14 年前
        1
  •  2
  •   Hans Passant    14 年前

    clr不允许使用具有与引用类型重叠的字段的结构。您的案例中的两个数组。它与垃圾收集器非常不兼容,无法跟踪对象引用。并且会非常不安全,因为它允许后门进入堆,直接操纵对象引用的值。

    在您的特定情况下,这样做没有意义,因为两个数组重叠并且大小完全相同。一个人就能把工作做好。

        2
  •  0
  •   Brian Gideon    14 年前

    问题是数组既不可快速复制,也不可打包到结构中。毕竟它们是引用类型。所以结构实际上只包含对堆中数组对象的引用。据我所知 MarshalAs 属性不影响成员的布局。它只影响在互操作例程期间如何封送成员。

    我的另一个观察是,您实际上钉住了结构的装箱版本,而不是实际结构。当然,值类型存在于堆栈中,因此不需要固定它。也许你已经知道了,这就是你选择提取 IntPtr 就这样。这很好,但是通过使用 unsafe 代码或 Marshal.StructureToPtr 方法。

    我认为你最好的办法是创建一个独立的 byte 数组并使用 Array.Copy 方法和 BitConverter 班级。