【问题标题】:How to Convert structure to Byte array in c#.net but the structure size defined in runtime only如何在 c#.net 中将结构转换为字节数组,但结构大小仅在运行时定义
【发布时间】:2016-03-22 08:26:53
【问题描述】:

我有如下结构

[StructLayout(LayoutKind.Sequential)]
public struct MyStructType
{
[MarshalAs(UnmanagedType.U1)]
public byte stx;

public UInt16 cmdId;

public UInt16 status;

public UInt16 pktNo;

[MarshalAs(UnmanagedType.U1)]
public byte contPkt;

[MarshalAs(UnmanagedType.U1)]
public byte dataoffset;

public UInt16 dataLength;

[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 6)]
public byte[] data;

public UInt16 checkSum;

[MarshalAs(UnmanagedType.U1)]
public byte cr;
}

我尝试使用以下代码将此结构转换为字节数组。

byte[] ConvertStructureToByteArray(MyStructType 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;
    }

但我收到以下错误,因为他们不知道尺寸

类型“MyStructType”不能作为非托管结构封送;无法计算出有意义的大小或偏移量。

问题是因为

public UInt16 dataLength; 
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 6)] 
public byte[] data;

dataLength 在运行时计算。如何将此结构转换为 ByteArray?

【问题讨论】:

  • 我认为这不适用于结构。假设您想这样做以将其发送到 C/C++ 函数,您可能必须改用 IntPtr,并使用 Marshal.AllocHGlobal() 自己显式设置它并使用 Marshal.Copy() 填充它
  • 是的,我做到了。请参阅 ConvertStructureToByteArray 方法。
  • 我的意思是你需要将data声明为IntPtr
  • 我的理解是SizeParamIndex 适用于extern 调用中的不同参数,而不是在现有类型中;例如:msdn.microsoft.com/en-us/library/…

标签: c# arrays byte structure marshalling


【解决方案1】:

编组限制

正如您已经注意到的,Marshal.SizeOf() 无法计算包含 byte 数组和 UnmanagedType.LPArray 的结构的大小。但是,这并不意味着您不能自己计算。

但是,即使您这样做,您也会收到 Marshal.StructureToPtr 抱怨必须使用 SafeArray 或 ByValArray 的数据字段。

您应该检查this on MSDN 以了解如何将数组从托管传递到非托管。然而,对于结构中包含的数组,似乎:

大小只能设置为常量

为什么不使用序列化?

Protocol Buffer 是一种将数据序列化为二进制的简单方法。此外,它还支持模型更改、模型共享和其他一些很酷的功能。

它有多种语言版本:

【讨论】:

    【解决方案2】:

    由于@Fab 提供的答案中指出的编组限制,添加提供解决方案的答案

    public UInt16 dataLength; 
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 6)] 
    public byte[] data;
    

    编组实际上并不能非常干净地处理数组,因为它不会像您期望的那样计算数组中每个元素的偏移量(即使使用提供的属性,请看图!)。由于您的结构很小,您可以执行以下操作来正确编组字节:

    [StructLayout(LayoutKind.Sequential)]
    public struct MyStructType
    {
      [MarshalAs(UnmanagedType.U1)]
      public byte stx;
    
      public UInt16 cmdId;
    
      public UInt16 status;
    
      public UInt16 pktNo;
    
      [MarshalAs(UnmanagedType.U1)]
      public byte contPkt;
    
      [MarshalAs(UnmanagedType.U1)]
      public byte dataoffset;
    
      public UInt16 dataLength;
    
      public MyDataArray data;
    
      public UInt16 checkSum;
    
      [MarshalAs(UnmanagedType.U1)]
      public byte cr;
    }
    
    [StructLayout(LayoutKind.Sequential, Pack = 1, Size = Size)]
    public struct MyDataArray
    {
      public const int Size = 6;
    
      public byte Byte0;
      public byte Byte1;
      public byte Byte2;
      public byte Byte3;
      public byte Byte4;
      public byte Byte5;
    
      public MyDataArray(byte[] bytes) {
        if (bytes == null || bytes.Length != Size)
          throw new ArgumentOutOfRangeException(nameof(bytes));
        Byte0 = bytes[0];
        Byte1 = bytes[1];
        Byte2 = bytes[2];
        Byte3 = bytes[3];
        Byte4 = bytes[4];
        Byte5 = bytes[5];
      }
    
      public byte[] ToArray() {
        return new byte[Size] { Byte0, Byte1, Byte2, Byte3, Byte4, Byte5 };
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多