Marshal.SizeOf 尺寸错误

Marshal.SizeOf Giving The Wrong Size

下面的代码在使用 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 的大小,但这并没有什么不同。 我错过了什么?

**编辑这是 BLEMessageHdr 结构:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct BLEMessageHdr
{
    public ushort msg_id;
    public uint msg_body_sz;
};

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 中称为 "Flexible Array Member" (FAM)。它是 C++ 中的非法结构。因为它在 C++ 中是非法的,并且由于 C 标准(§ 6.7.2.1)中关于包含 FAM 的结构的实例化的固有不确定性,Marshal class 根本不接受它们进行互操作使用非托管代码。

通常编组数组成员的方式是 MarshalAsAttribute,像这样:

[MarshalAs(UnmanagedType.ByValArray, SizeConst=N)]
public byte[] msg_body;

其中 "N" 表示显式声明的数组大小。如果没有此属性,msg_body 成员将被 Marshal class 视为指针。因此,Marshal.SizeOf() 返回的大小是正确的。您的通用 Serialize() 方法不适用于具有 FAM 的结构。

您可以将其修改为在 Marshal class、 复制 FAM 的其余内容后手动复制 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 的结构。

再一次,这似乎是解决这个问题的尴尬方法。您可能真的想重新考虑这种方法。