使用 Protocol buffers / Protobuf 的 PInvoke 通用字符串格式

Common string format for PInvoke using Protocol buffers / Protobuf

我有一个 C++ 库,可以让我访问大型数据集。我在 C# 应用程序中使用它,通过使用 PInvoke 调用 C++ 函数。

我使用 protobuf 在 C++ 中序列化数据集,将数据作为字符串传递给 C#,然后在 C# 中反序列化。

C++伪代码

ReadData(..., char * &output, ...){
    Dataset data = ReadData(...);
    ProtoBufDataset protobufDataset = SerializeToProtobufStructure(data);
    string serialized = protobufDataset.SeralizeToString();

    // allocate serialized to output string
    ::CoTaskMemAlloc(output, serialized);
    return true;
}

C# 包装函数定义

[DllImport("CPLusPLusdll.dll", CallingConvention = CallingConvention.Cdecl,
            BestFitMapping = false, ThrowOnUnmappableChar = true)]
        [return: MarshalAs(UnmanagedType.I1)]
        internal static extern bool ReadData(
            ...
            [MarshalAs(UnmanagedType.LPStr)] out string output,
            ...);

C#伪代码

string serializedData;
ReadDataFromCplusPlus(...., out serializedData, ...)
ProtobufDataset protobufDataset;
protoBufDataset.Deserialize(serializedData);
...

这行得通,但我在反序列化某些数据集时遇到了问题,我相当确定这与字符串编码或缺乏处理有关。我在两边都添加了 base64 encoding/decoding,这似乎有效。

C++伪代码

ReadData(..., char * &output, ...){
    Dataset data = ReadData(...);
    ProtoBufDataset protobufDataset = SerializeToProtobufStructure(data);
    string serialized = protobufDataset.SeralizeToString();
    string encoded = base64_encode(serialized);

    // allocate serialized to output string
    ::CoTaskMemAlloc(output, encoded);
    return true;
}

C#伪代码

string serializedData;
ReadDataFromCplusPlus(...., out serializedData, ...)
ProtobufDataset protobufDataset;
protoBufDataset.DeserializeInBase64(serializedData);
...

我对 base64 编码的开销不满意。我的问题是我可以使用编组参数 and/or Invoke 函数中正确的数据类型来获得相同的结果吗?

由于您已经将 LPStr 用作编组类型,因此您应该确保使用 unicode 字符串,因为 C# 中的 System.String 是 unicode,而 C++ 中的默认字符集是多字节 (UCS2)。

您可以在 Visual Studio 的项目设置中执行此操作。 确保字符集设置为 "Use Unicode Character Set"。这应该可以解决您的问题。

您还可以在 C# 中将 UCS2 转换为 Unicode(已经在 SO 上,但这也意味着开销,您可以通过在 C++ 中选择 Unicode 来避免这种开销。