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

标签 c# c++ serialization struct

我们从另一个组织获得了一个程序,该程序从多播源收集数据并整理和保存该数据。它需要一个格式如下的 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];

我以前的经验是使用 BinaryWriter...我不需要在 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++ 端正确反序列化。

我添加了 TypesWhenNeeded TypeFormat,以为我可以将它最小化,但它没有改变......我注意到没有不使用 TypeFormat 的选项,除非我在某个地方错过了它。

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

最佳答案

虽然您不能预先初始化结构字段,也不能直接将大小放入 ByValue 二维数组的 MarshalAs 属性中,但您可以采取一些变通方法。您可以像这样定义两个结构:

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 设置为第一维并将其设置为第二维来指定两个维度的长度新结构中的维度。将字段设为私有(private)并为其创建属性只是为了方便,因此您不必在创建结构时自己分配数组。
然后你可以像这样序列化/反序列化结构(来自这个答案的代码: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;
}

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

关于c# - 在 C# 中序列化这些值以在 C++ 中作为已知结构正确读取时遇到问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57100398/

相关文章:

c++ - 用Qt显示大量图像

c++ - 在两个分母的 LCM 都很大的 chrono::duration 类型之间转换

serialization - Google Protocol Buffers 中 bool 占用的空间是多少?

serialization - belongsTo promise 在序列化修改后的记录时未解决

python - 将参数传递给序列化器 DRF

c# - .NET 核心 Web API key

c# - Unity Web 请求包装器

c++ - 映射和 For 循环

c# - 将语言字符转换为拉丁字母

c# - 从 ASP.NET 到 .NET Core 的 DelegateHandler