如何通过 COM 变体编组 utf-8 字节串?

How to marshall utf-8 bytestring via COM variant?

我有一个代码可以通过 COM 变体传递一些 utf-8 编码的 JSON 字符串,特别是使用 CComVariant。在日本用户的计算机上安装我的软件之前,一切都运行良好,我想他正在使用日文版的 Windows 7。不知何故 Windows 决定更改 1 个非 ascii 字符的字节序列,并且破坏了 JSON 格式。

组合问题:

"nić" (bytes: 0x22 0x6E 0x69 0xC4 0x87 0x22)

在打包到 CComVariant 然后解压后,上面的字符串已更改为:

"niāE (bytes: 0x22 0x6E 0x69 0xC4 0x81 0x45)

即组合 ć" 变成了 āE.

我的代码如下(简化版):

void get_json(VARIANT *out)
{
    const std::string json = "\"nić\"";
    CComVariant result = json.c_str();
    result.Detach(out);
}

然后在代码的其他部分:

CComVariant varJson;
get_json(&varJson);
const std::string utf8json = std::string(CStringA(varJson));
// At this point utf8json is not the same as original json above
// and cannot be decoded properly by JSON parser.

看来我对 COM Variant 中的 CStringA 有一些误解,在这里传递 UTF-8 字节是不安全的。我用西欧版的Windows无法重现这个问题,这跟日版有点关系。

问题在评论中解释了。至于解决方案,因为您使用的是 std (还有很多其他解决方案),我建议您使用此答案中定义的 widen 函数: Is this code safe using wstring with MultiByteToWideChar? 并将代码更改为:

CComVariant result = widen(json).c_str();

让我们在调试器下检查一下。之前:

之后:

现在,VARIANT(或它包含的 BSTR)没问题了。

请注意,如果您需要来自此 VARIANT 或来自 BSTR 的等效字节字符串(您真的吗?),请不要使用像这样的损坏代码将其转换回来:std::string(CStringA(varJson)),再次使用widen 的反向等效值,这次基于 WideCharToMultiByte

我找到了 2 种方法来解决这个问题:

  1. (按照建议)将我的 json 对象的 utf-8 编码字符串表示形式转换为预期的 Variant 正确的 unicode 字符串
  2. (稍微复杂一点)将我的字符串中的任何非 ascii 字符转换为 unicode 转义序列,例如 \u1234,因此确保我的所有数据都是纯 ascii。

由于向后兼容的限制,我不得不走第二条路。