将变长结构转换为byte []

时间:2010-09-17 15:13:20

标签: c# serialization data-structures

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

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

[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[]以通过Socket发送它:

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 个答案:

答案 0 :(得分:2)

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

在您的特定情况下这样做是没有意义的,因为两个阵列重叠并且大小完全相同。一个人可以完成工作。

答案 1 :(得分:0)

问题是数组不是blittable,也不是打包到struct中。它们毕竟是参考类型。所以struct实际上只包含对堆上的数组对象的引用。据我所知,MarshalAs属性不会影响成员的布局。它只会影响成员在互操作例程中的编组方式。

我的另一个观察是你实际上是固定结构的盒装版本,而不是实际的结构。当然,值类型存在于堆栈中,因此无需固定它。也许你已经知道了,这就是你选择从中提取IntPtr的方法。这没关系,但您可以使用unsafe代码或Marshal.StructureToPtr方法更高效,更合适地完成同样的事情。

我认为您最好的办法是创建一个单独的byte数组并使用Array.Copy方法和BitConverter类。

相关问题