如何在 c#.net 中将结构转换为字节数组,但结构大小仅在运行时定义

Posted

技术标签:

【中文标题】如何在 c#.net 中将结构转换为字节数组,但结构大小仅在运行时定义【英文标题】:How to Convert structure to Byte array in c#.net but the structure size defined in runtime only 【发布时间】: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/… 【参考方案1】:

编组限制

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

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

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

大小只能设置为常量

为什么不使用序列化?

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

它有多种语言版本:

C# API:Protobuf-csharp-port 或 Protobuf-net Google C++ API.

【讨论】:

【参考方案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 ;
  

【讨论】:

以上是关于如何在 c#.net 中将结构转换为字节数组,但结构大小仅在运行时定义的主要内容,如果未能解决你的问题,请参考以下文章

如何在c ++中将int数组转换为字节数组[重复]

如何在c ++中将数组字节转换为字符串?

如何在C ++中将字节数组中的整数转换为数字

如何在C#中将IntPtr转换为字节数组?

如何在 C 中将字节数组转换为十六进制字符串?

如何在php中将字节数组转换为整数?