【问题标题】:How to convert CComVariant bstr to CString如何将 CComVariant bstr 转换为 CString
【发布时间】:2018-01-11 01:17:51
【问题描述】:

我是 C++ 的新手,我已经接管了一个 COM 项目来解决一些问题。 我正在处理的当前问题是处理 UTF8 字符串。 我有这段代码:

// CString strValue;
CStringW strValue; 
CComVariant* val = &(*result)[i].minValue;
switch (val->vt)
{
case VT_BSTR:   
    //strValue = OLE2CA(val->bstrVal);
    strValue = OLE2W(val->bstrVal); // Works
    (*result)[i].name = strValue; // Works
    (*result)[i].expression = "[" + fieldName + "] = \"" + strValue + "\""; // fails
    break;
case VT_R8:     
    //strValue.Format("%g", val->dblVal);
    strValue.Format(L"%g", val->dblVal); // Works
    (*result)[i].name = strValue; // Works
    (*result)[i].expression = "[" + fieldName + "] = " + strValue; // fails
    break;
case VT_I4:     
    //strValue.Format("%i", val->lVal);
    strValue.Format(L"%i", val->lVal); // Works
    (*result)[i].name = strValue; // Works
    (*result)[i].expression = "[" + fieldName + "] = " + strValue; // fails
    break;
}

struct CategoriesData
{
    public:
    CComVariant minValue;
    CComVariant maxValue;
    //CString expression;
    CStringW expression;
    //CString name;
    CStringW name;
    tkCategoryValue valueType;
    int classificationField;
    bool skip;
};

问题在于strValue = OLE2CA(val->bstrVal); 这一行当val->bstrVal 是这样的俄罗斯文本Воздух 的Unicode 字符串时,strValue 被转换为?????

我尝试了几种方法并搜索了互联网,但无法将 strValue 设为ВоздухCString 可以包含这种文本还是应该更改为另一种类型?那么是哪一个?

minValue 可以是 VT_BSTR、VT_R8 或 VT_I4。

这些是我目前尝试过的选项:

strValue = val->bstrVal;
strValue = Utility::ConvertFromUtf8(val->bstrVal);
strValue = Utility::ConvertToUtf8(val->bstrVal);
temp = Utility::ConvertBSTRToLPSTR(val->bstrVal);
strValue = W2BSTR(Utility::ConvertFromUtf8(temp));
strValue = W2BSTR(val->bstrVal);                
strValue = CW2A(val->bstrVal);
strValue = (CString)val->bstrVal;
strValue = Utility::ConvertToUtf8(OLE2W(val->bstrVal));

编辑 辅助函数的代码:

CStringA ConvertToUtf8(CStringW unicode) {
    USES_CONVERSION;
    CStringA utf8 = CW2A(unicode, CP_UTF8);
    return utf8;
}

CStringW ConvertFromUtf8(CStringA utf8) {
    USES_CONVERSION;
    CStringW unicode = CA2W(utf8, CP_UTF8);
    return unicode;
}

char* ConvertBSTRToLPSTR (BSTR bstrIn)
{
  LPSTR pszOut = NULL;
  if (bstrIn != NULL)
  {
    int nInputStrLen = SysStringLen (bstrIn);

    // Double NULL Termination
    int nOutputStrLen = WideCharToMultiByte(CP_ACP, 0, bstrIn, nInputStrLen, NULL, 0, 0, 0) + 2; 
    pszOut = new char [nOutputStrLen];

    if (pszOut)
    {
      memset (pszOut, 0x00, sizeof (char)*nOutputStrLen);
      WideCharToMultiByte (CP_ACP, 0, bstrIn, nInputStrLen, pszOut, nOutputStrLen, 0, 0);
    }
  }
  return pszOut;
}

编辑2 我添加了完整的 switch 语句。 当我将 strValue 从 CString 更改为 CStringW 时,其他情况会出现错误,例如 strValue.Format("%g", val->dblVal); 如何解决?

编辑3 我已经修复了一个类似的问题,但那是转换为 VARIANT 而不是来自:

    val->vt = VT_BSTR;
    const char* v = DBFReadStringAttribute(_dbfHandle, _rows[RowIndex].oldIndex, _fields[i]->oldIndex);
    // Old code, not unicode ready:
    //WCHAR *buffer = Utility::StringToWideChar(v);
    //val->bstrVal = W2BSTR(buffer);
    //delete[] buffer;              
    // New code, unicode friendly:
    val->bstrVal = W2BSTR(Utility::ConvertFromUtf8(v)); 

编辑4 感谢到目前为止的所有帮助,我设法做出了一些改变。我在这篇文章中更新了我的初始代码并添加了该函数的所有代码。我现在坚持这一行:

 (*result)[i].expression = "[" + fieldName + "] = \"" + strValue + "\"";    

我无法连接 CStringW 值。

更多背景信息:该函数是MapWinGIS 的一部分,这是一个开源 GIS 应用程序,您可以在其中显示地图(shapefile)。这些地图具有属性数据。该数据以 DBase IV 格式存储,可以保存 unicode/UTF-8 文本。我已经进行了修复(参见 Edit3)以在网格视图中正确显示此文本。我现在正在努力的功能是将数据分类(分组),例如为相似的值赋予相同的颜色。这个类别有一个名称和一个表达式。稍后会解析此表达式以进行实际分组。例如,我有一张带有州的地图,我想为每个州赋予不同的颜色。 如前所述,我是 C++ 新手,我真的不在我的舒适区。我真的很感谢你给我的所有帮助。我希望你能再帮助我一次。

【问题讨论】:

  • bstrVal 最初包含什么?一个 UTF8 字符串?你一开始是怎么放进去的)。 Windows 没有原生的 UTF8 字符串类型,所以你必须以某种方式进行转换,它才能工作,那么 Utility::ConvertToUtf8 是如何编码的?
  • 我添加了辅助函数的代码
  • BSTR 存储为 UTF-16 ,你不应该尝试任何 UTF8 的东西。 (好吧,我猜可能有人将 UTF-8 字节复制到 BSTR 中,但这会非常糟糕)
  • 答案取决于您是否在 Unicode 项目中。如果是,那么 CString 表示 CStringW 并且您不会使用任何 UTF-8 覆盖;否则它意味着CStringA,你会的。 IMO 最好使用 Unicode 项目,但如果您正在处理最初构建为非 Unicode 的遗留代码,那么您可能会被困住。请澄清哪种情况适合您,因为两者的答案都不同。 (你可以通过重载转换函数同时支持)
  • 如果它是一个 unicode 项目,那么我不清楚您要在 CStringW 中使用 UTF-8 做什么

标签: c++ unicode com type-conversion bstr


【解决方案1】:

BSTRs “自然”存储 Unicode UTF-16 长度前缀字符串,尽管您可以“拉伸” BSTR 并用它存储更多通用的以长度为前缀的字节序列(但我不喜欢这种用法)。

(有关BSTRs的更多详细信息,您会发现this blog post by Eric Lippert非常有趣。)

所以,我正在考虑BSTR 的正常使用,它存储以长度为前缀的UTF-16 字符串。

如果要将存储在 BSTR 中的 UTF-16 字符串转换为 UTF-8 字符串,可以使用带有 CP_UTF8 标志的 WideCharToMultiByte Win32 API(请参阅例如this MSDN Magazine article 了解详情,this reusable code on GitHub)。

您可以将目标 UTF-8 字符串存储在 std::string 类的实例中。

PS 如果您想将 CStringW 用于 UTF-16,将 CStringA 用于 UTF-8 字符串,并使用 ATL CW2A 帮助器进行 UTF-16/8 转换,请注意您的代码中不需要 USES_CONVERSION 宏;您可以将const&(常量参考)的输入字符串作为良好的代码卫生:

CStringA Utf8FromUtf16(const CStringW &utf16) {
    CStringA utf8 = CW2A(utf16, CP_UTF8);
    return utf8;
}

RE 编辑 2

试试strValue.Format(L"%g",...CStringWL 前缀为 CStringW::Format 生成 Unicode UTF-16 字符串文字。

RE 编辑 4

我在 cmets 中对此进行了回复,但为了完整起见,要将字符串文字与 CStringW 实例连接起来,请考虑使用 L"..." 装饰这些文字:这定义了 Unicode UTF- 16 个字符串文字,基于 wchar_t,可与 CStringW 对象配合使用。

(*result)[i].expression = L"[" + fieldName + L"] = \"" + strValue + L"\"";    

【讨论】:

  • 我试过你的Utf8FromUtf16' and it returns "Ð'ода" ATL::CSimpleStringT: "Ð'ода"`
  • 您是如何可视化返回的 CStringA 字符串的内容的?请注意,(字符)字节序列可以根据编码以不同方式显示。您是否检查过 CStringA 中的实际字节是否代表源 UTF-16 字符串的预期 UTF-8 编码序列?
  • 除了我之前的评论,考虑使用s8 标志,例如在 VS 命令窗口中打印 UTF-8 字符串,例如:? str.GetString(), s8。 (您可以阅读this blog post 了解更多详情。)
  • 我使用 VS2013 调试代码并设置断点,然后快速查看我可以看到值。使用 C# 编写的可执行文件调用该方法,该可执行文件仅调用此特定方法。最后,数据在更大的 C# 应用程序中查看。顺便提一句。所有代码都是开源的,它是 MapWindow GIS 项目:github.com/MapWindow
  • 我再次更新了我的帖子(参见 Edit4)并添加了更多代码。我现在正在努力将 CStringW 值连接在一起。我曾希望这将是一个小修复,但结果却是一项巨大的努力。尤其是当你不知道自己在做什么时;)
【解决方案2】:

如果不将您的项目转换为支持 Unicode 的应用程序,您将无法获得始终可用的版本。

换句话说,要支持所有可能包含在 BSTR 中的字符,您需要一个 Unicode CString (CStringW)

您可以继续使用 MBCS 版本,但在这种情况下,您仍然必须处理 Unicode。在这里使用 CStringW 可能是一个选项。

使用 WideCHarToMultiByte 转换为 UTF-8

【讨论】:

  • 我尝试使用CStringW temp = val->bstrVal,它似乎工作。我可以安全地将我的变量从 CString 更改为 CStringW 吗?
  • 是的。为什么不?你能指望什么。将 CSrintgW 转换为 UTF-8 是使用 MultiByteToWideChar 完成的
  • 抱歉有更多新手问题。我通过添加整个 switch 语句来更新我的帖子。当我更改为 CStringW strValue.Format("%g", val->dblVal); 时不再编译。
  • @PaulMeems 使用 CStringW 尝试 strValue.Format(L"%g",...L 前缀生成 Unicode UTF-16 字符串文字。
【解决方案3】:

如何:在各种字符串类型之间进行转换
https://docs.microsoft.com/en-us/cpp/text/how-to-convert-between-various-string-types

本主题演示如何转换各种 Visual C++ 字符串类型 到其他字符串。涵盖的字符串类型包括 char 、 wchar_t、_bstr_t、CComBSTR、CString、basic_string 和 System.String。 在所有情况下,当转换为新的字符串时都会生成一个副本 类型。对新字符串所做的任何更改都不会影响原始字符串 字符串,反之亦然。

【讨论】:

    猜你喜欢
    • 2014-04-04
    • 2012-04-03
    • 2010-10-11
    • 1970-01-01
    • 1970-01-01
    • 2016-02-14
    • 2015-09-27
    • 2023-04-01
    • 2011-11-23
    相关资源
    最近更新 更多