Marshal.SizeOf 给出错误的大小
Posted
技术标签:
【中文标题】Marshal.SizeOf 给出错误的大小【英文标题】:Marshal.SizeOf Giving The Wrong Size 【发布时间】:2018-08-02 21:02:46 【问题描述】:下面的代码在使用 Marshal.SizeOf 时给出了错误的尺寸,但我不知道为什么。
这是我试图获取大小的结构:
//[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
//[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct BLEGenericMsg
public BLEMessageHdr msg_hdr;
public byte[] msg_body;
public BLEGenericMsg(int messageSize)
msg_hdr = new BLEMessageHdr();
msg_body = new byte[messageSize];
;
这是填充结构并调用序列化函数的代码:
BLEGenericMsg hostKeyMsg = new BLEGenericMsg(serializedPublicBytes.Length);
hostKeyMsg.msg_hdr.msg_id = MESSAGE_BASE_EVENT + EVENT_HOST_PUBLIC_KEY;
hostKeyMsg.msg_body = serializedPublicBytes;
//Only get the size of the body for the entire message, not counter or header
hostKeyMsg.msg_hdr.msg_body_sz = (uint)hostKeyMsg.msg_body.Length;
BluetoothLEHardwareInterface.Log("public Key Size: " + hostKeyMsg.msg_hdr.msg_body_sz + "\n");
byte[] temp = Serialize(hostKeyMsg);
BluetoothLEHardwareInterface.Log("temp Size: " + (uint)temp.Length + "\n");
这是获取结构体大小的序列化函数:
public static byte[] Serialize<T>(T s)
where T : struct
var size = Marshal.SizeOf(typeof(T));
BluetoothLEHardwareInterface.Log("BLEGenericMsg Size: " + size + "\n");
var array = new byte[size];
var ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(s, ptr, true);
Marshal.Copy(ptr, array, 0, size);
Marshal.FreeHGlobal(ptr);
return array;
serializedPublicBytes 的大小为 91 字节, 结构的其余部分是 6 个字节。 所以我期望 Marshal.SizeOf 是 97 字节, 但它只显示大约 14 或 16 个字节。 我尝试在实例化时给出 msg_body 的大小,但这并没有什么不同。 我错过了什么?
**edit 这里是 BLEMessageHdr 结构:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct BLEMessageHdr
public ushort msg_id;
public uint msg_body_sz;
;
【问题讨论】:
我假设 BLEMessageHdr 是一个类。它告诉您指向 msg_hdr 的指针的大小 + 指向数组的指针的大小是多少。 @pm100: 14 听起来像“msg_hdr
的大小”(不涉及指针)+“指向字节数组的指针大小”
也许吧。但要点是 sizeof 不计算字节数组的大小 - 它不能计算,因为 sizeof 对类型而不是实例进行操作而序列化操作序列化特定实例
结构的非托管定义是什么样的?
是的,它是 14 个字节。如果您不打包 = 1,则为 16 个字节。麻烦制造者是字节[],它被编组为指针。但是您打算创建一个可变大小的结构,换句话说 byte[] 应该具有 [MarshalAs(UnmanagedType.ByValArray, SizeConst=???)] 属性。这 ???是个大问题,您希望不同的消息具有不同的大小。从技术上讲,您可以为每种类型的消息声明一个专用结构,现在您知道 ???。但请考虑以不同的方式执行此操作,使用 BinaryWriter 生成普通的 byte[] 而不是结构。
【参考方案1】:
Marshal.SizeOf()
方法没有返回错误的大小。在您定义的结构中:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct BLEGenericMsg
public BLEMessageHdr msg_hdr;
public byte[] msg_body;
public BLEGenericMsg(int messageSize)
msg_hdr = new BLEMessageHdr();
msg_body = new byte[messageSize];
;
msg_body
成员在 C 中被称为“灵活数组成员”(FAM)。它在 C++ 中是非法构造。因为它在 C++ 中是非法的,并且由于 C 标准(第 6.7.2.1 节)中关于包含 FAM 的结构的实例化的固有不确定性,所以 Marshal 类根本不接受它们与非托管代码的互操作。
通常编组数组成员的方式是使用MarshalAsAttribute
,如下所示:
[MarshalAs(UnmanagedType.ByValArray, SizeConst=N)]
public byte[] msg_body;
其中“N”表示显式声明的数组大小。如果没有此属性,msg_body
成员将被 Marshal 类视为指针。所以,Marshal.SizeOf()
返回的大小是正确的。您的通用 Serialize()
方法不适用于具有 FAM 的结构。
您可以修改它以在 Marshal 类复制其余内容后手动复制 FAM 的内容,但这对于托管结构的二进制序列化似乎是一种相当尴尬的方法。
// specify the name of the FAM and use reflection to get the value
// THIS ASSUMES that the FAM is always a byte[]
public static byte[] Serialize<T>(T s, string fam) where T : struct
Type tt = typeof(T);
// Reflection will get you the bytes in the FAM
FieldInfo fi = tt.GetField(fam);
byte[] famBytes = (byte[])fi.GetValue(s);
// Get the field offset that corresponds to the unmanaged layout for
// the FAM, according to the marshaller
int offset = (int)Marshal.OffsetOf(tt, fam);
var size = Marshal.SizeOf(tt) + famBytes.Length;
BluetoothLEHardwareInterface.Log("BLEGenericMsg Size: " + size + "\n");
var array = new byte[size];
var ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(s, ptr, true);
Marshal.Copy(ptr, array, 0, size);
Marshal.FreeHGlobal(ptr);
// Now you're done with the marshalling, just copy over the contents of the
// byte[] to your resulting array, starting at the correct offset
Array.Copy(famBytes, 0, array, offset, famBytes.Length);
return array;
当然,您还必须修改 Deserialize()
方法以处理具有 FAM 的结构。
再次,这似乎是解决这个问题的尴尬方法。您可能需要重新考虑这种方法。
【讨论】:
感谢代码帮助。我正在尝试关注您发布的内容。我是否想将 msg_body 作为字符串 fam 传递给 serialize 函数?我愿意寻找任何替代想法来实现这一目标,但我还没有看到任何想法。 是的,将"msg_body"
作为 FAM 成员的名称。
我认为这让我接近了我需要的地方。现在数组末尾多了 8 个字节,我不确定它为什么在那里或从哪里来。
没问题。这是一个声明,而不是一个问题以上是关于Marshal.SizeOf 给出错误的大小的主要内容,如果未能解决你的问题,请参考以下文章
Marshal.AllocHGlobal VS Marshal.AllocCoTaskMem,Marshal.SizeOf VS sizeof()