在 C# 中序列化这些值以在C++中作为已知结构正确读取时遇到问题

Having trouble serializing these values in C# to be properly read in C++ as a known struct

本文关键字:结构 读取 问题 遇到 序列化 C++      更新时间:2023-10-16

我们得到了另一个组织的程序,该程序从多播源收集数据并整理和保存该数据。它需要一个格式如下的C++结构:

#define SP_PACKET_SIZE 200
#define NAME_SIZE 64
struct spPacketStruct
{
int Size;
char Name[SP_PACKET_SIZE][NAME_SIZE];
double Value[SP_PACKET_SIZE];
};

显然我不能在 C# 中使用此结构,因为结构不能具有预初始化的数组,因此我想创建单个位并序列化它们。 所以现在我在 C# 中有这个:

int SpPacketSize;
char[,] SpNames = new char[SP_PACKET_SIZE, NAME_SIZE];
double[] SpValues = new double[SP_PACKET_SIZE];

我以前的经验是使用二进制编写器...我不需要在 C# 中反序列化,我只需要将其放入C++程序。我的测试序列化代码如下:

System.IO.MemoryStream outputstream = new System.IO.MemoryStream();
BinaryFormatter serializer = new BinaryFormatter();
serializer.TypeFormat = System.Runtime.Serialization.Formatters.FormatterTypeStyle.TypesWhenNeeded;
serializer.Serialize(outputstream, SpPacketSize);
serializer.Serialize(outputstream, SpNames);
serializer.Serialize(outputstream, SpValues);
byte[] buffer = outputstream.GetBuffer();
udpclient.Send(buffer, buffer.Length, remoteep);

我得到一个二进制数据包,但长度不正确,因为它仍然包含类型格式。当我在 Wireshark 中查看此数据包时,我看到它的开头有一个 System.Int32 符号。 这使得数据包大于预期,因此无法在C++端正确反序列化。

我添加了 TypesWhenNeed TypeFormat,认为我可以最小化它,但它没有改变......我注意到没有选择不打字格式,除非我在某处错过了它。

有没有人有任何关于如何在没有额外信息的情况下正确序列化此数据的提示?

虽然您不能预先初始化结构字段,也不能直接将大小放在 2D 数组ByValueMarshalAs 属性中,但您可以做一些解决方法。您可以定义两个结构,如下所示:

const short SP_PACKET_SIZE = 200;
const short NAME_SIZE = 64;
struct spPacketStruct
{
public int Size;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = SP_PACKET_SIZE)]
private fixedString[] names;
public fixedString[] Names { get { return names ?? (names = new fixedString[SP_PACKET_SIZE]); } }
[MarshalAs(UnmanagedType.ByValArray, SizeConst = SP_PACKET_SIZE)]
private double[] value;
public double[] Value { get { return value ?? (value = new double[SP_PACKET_SIZE]); } }
}
struct fixedString
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAME_SIZE)]
public string Name;
}

通过将其他结构设置为原始结构的成员,可以通过将原始结构中的SizeConst设置为第一维并将其设置为新结构中的第二维来指定两个维度的长度。将字段设为私有并为它们创建属性只是为了方便起见,因此在创建结构时不必自己分配数组。
然后你可以像这样序列化/反序列化结构(来自这个答案的代码:https://stackoverflow.com/a/35717498/9748260):

public static byte[] GetBytes<T>(T str)
{
int size = Marshal.SizeOf(str);
byte[] arr = new byte[size];
GCHandle h = default;
try
{
h = GCHandle.Alloc(arr, GCHandleType.Pinned);
Marshal.StructureToPtr(str, h.AddrOfPinnedObject(), false);
}
finally
{
if (h.IsAllocated)
{
h.Free();
}
}
return arr;
}
public static T FromBytes<T>(byte[] arr) where T : struct
{
T str = default;
GCHandle h = default;
try
{
h = GCHandle.Alloc(arr, GCHandleType.Pinned);
str = Marshal.PtrToStructure<T>(h.AddrOfPinnedObject());
}
finally
{
if (h.IsAllocated)
{
h.Free();
}
}
return str;
}

最后一件事 当尝试序列化/反序列化这样的结构时,请注意结构对齐方式,因为它可能会弄乱结构大小