Sni*_*000 5 c# arrays struct sizeof marshalling
下面的代码在使用Marshal.SizeOf时给出了错误的大小,但是我不确定为什么。
这是我要获取的大小的Struct:
//[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];
}
};
Run Code Online (Sandbox Code Playgroud)
以下是填充该结构并调用serialize函数的代码:
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");
Run Code Online (Sandbox Code Playgroud)
这是获取结构大小的序列化函数:
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;
}
Run Code Online (Sandbox Code Playgroud)
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;
};
Run Code Online (Sandbox Code Playgroud)
该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];
}
};
Run Code Online (Sandbox Code Playgroud)
该msg_body成员在C中被称为“灵活数组成员”(FAM)。在C ++中,它是非法构造。因为在C ++中是非法的,并且由于C标准(第6.7.2.1节)在包含FAM的结构的实例化方面存在固有的不确定性,所以Marshal类根本不接受它们与非托管代码进行互操作。
通常将数组成员编组的方式是使用MarshalAsAttribute,例如:
[MarshalAs(UnmanagedType.ByValArray, SizeConst=N)]
public byte[] msg_body;
Run Code Online (Sandbox Code Playgroud)
其中“ N”代表显式声明的数组大小。如果没有此属性,msg_body则元帅类将成员视为指针。因此,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;
}
Run Code Online (Sandbox Code Playgroud)
自然地,您将同样必须修改该Deserialize()方法以处理具有FAM的结构。
再次,这似乎是解决此问题的尴尬方法。您可能需要重新考虑这种方法。