如何在 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 中将结构转换为字节数组,但结构大小仅在运行时定义的主要内容,如果未能解决你的问题,请参考以下文章