【问题标题】:How do you efficiently copy BSTR to wchar_t[]?如何有效地将 BSTR 复制到 wchar_t[]?
【发布时间】:2010-09-09 11:25:06
【问题描述】:

我有一个 BSTR 对象,我想将其转换为复制到 wchar__t 对象。棘手的是 BSTR 对象的长度可能从几千字节到几百千字节不等。有没有一种有效的方法来复制数据?我知道我可以只声明一个 wchar_t 数组并始终分配它需要保存的最大可能数据。然而,这意味着为可能只需要几千字节的东西分配数百千字节的数据。有什么建议吗?

【问题讨论】:

    标签: c++ com wchar-t bstr


    【解决方案1】:

    BSTR 对象包含一个长度前缀,因此查找长度很便宜。找出长度,分配一个足够大的新数组来保存结果,处理它,并记住在完成后释放它。

    【讨论】:

      【解决方案2】:

      使用 ATL 和 CStringT 然后你可以只使用赋值运算符。或者您可以使用 USES_CONVERSION 宏,这些宏使用堆分配,因此您可以确保不会泄漏内存。

      【讨论】:

        【解决方案3】:

        永远不需要转换。 BSTR 指针指向字符串的第一个字符,它以空值结尾。长度存储在内存中第一个字符之前。 BSTRs 始终是 Unicode (UTF-16/UCS-2)。曾经有一个称为“ANSI BSTR”的东西——在遗留 API 中有一些引用——但在当前的开发中你可以忽略这些。

        这意味着您可以将BSTR 安全地传递给任何期望wchar_t 的函数。

        在 Visual Studio 2008 中,您可能会遇到编译器错误,因为 BSTR 被定义为指向 unsigned short 的指针,而 wchar_t 是本机类型。您可以强制转换或关闭 wchar_t/Zc:wchar_t 的合规性。

        【讨论】:

        • wchar_t 不能保证正好是短片的大小。
        • 我认为这个操作总是安全的,但可能并不总是给出预期的结果。 BSTR 可以在其主体中包含空字符(因此是长度前缀),而需要 wchar_t * 的函数会将第一个空字符解释为字符串的结尾。
        • 您不能“将 BSTR 安全地传递给任何期望 wchar_t* 的函数”。比较 SysStringLen(NULL) 和 wcslen(NULL)。
        • 只是为了扩展康斯坦丁的评论 - BSTR 可以有效地为 NULL,它被 定义 等同于空字符串 ("")。相比之下,大多数期望 wchar_t* 的函数都强调 不会 将 NULL 视为指向空字符串的指针...
        【解决方案4】:

        要记住的一件事是BSTR 字符串可以并且经常包含嵌入的空值。 null 并不意味着字符串的结尾。

        【讨论】:

          【解决方案5】:

          首先,如果您只需要阅读内容,您实际上可能根本不需要做任何事情。 BSTR 类型已经是指向以 null 结尾的 wchar_t 数组的指针。事实上,如果你查看headers,你会发现BSTR本质上是这样定义的:

          typedef BSTR wchar_t*;
          

          因此,即使它们具有不同的语义,编译器也无法区分它们。

          有两个重要的警告。

          1. BSTR 应该是不可变的。在初始化 BSTR 后,您永远不应该更改它的内容。如果您“更改它”,则必须创建一个新指针并分配新指针并释放旧指针(如果您拥有它)。
            [更新:这不是真的;对不起!您可以就地修改 BSTR;我很少有需要。]

          2. BSTR 允许包含嵌入的空字符,而传统的 C/C++ 字符串则不允许。

          如果您对 BSTR 的源有相当多的控制权,并且可以保证 BSTR 没有嵌入的 NULL,则可以从 BSTR 中读取,就好像它是 wchar_t 一样,并使用传统的字符串方法(wcscpy,等)来访问它。如果没有,你的生活会变得更加艰难。您必须始终将数据作为更多 BSTR 或作为 wchar_t 的动态分配数组来操作。大多数与字符串相关的函数将无法正常工作。

          假设您可以控制自己的数据,或者不用担心 NULL。我们还假设您确实需要制作副本并且不能直接读取现有的 BSTR。在这种情况下,您可以执行以下操作:

          UINT length = SysStringLen(myBstr);        // Ask COM for the size of the BSTR
          wchar_t *myString = new wchar_t[lenght+1]; // Note: SysStringLen doesn't 
                                                     // include the space needed for the NULL
          
          wcscpy(myString, myBstr);                  // Or your favorite safer string function
          
          // ...
          
          delete myString; // Done
          

          如果您为 BSTR 使用类包装器,则包装器应该有一种方法可以为您调用 SysStringLen()。例如:

          CComBString    use .Length();
          _bstr_t        use .length();
          

          更新:这是一篇比我知识渊博的人写的关于这个主题的好文章:
          "Eric [Lippert]'s Complete Guide To BSTR Semantics"

          更新:在示例中将 strcpy() 替换为 wcscpy()

          【讨论】:

          • AFAIK,BSTR 不是应该是不可变的。这就是为什么它们没有被声明为 const*。
          • 嗯...我找不到任何支持我的立场的参考资料。我在想什么?我会改正的。
          • 你不应该使用 wcscpy 而不是 strcpy 吗?
          • @arolson101(在 wcscpy 上):你当然是对的。感谢您注意到我的失误。
          猜你喜欢
          • 2023-03-14
          • 2011-02-06
          • 2013-04-12
          • 2021-04-23
          • 2020-05-16
          • 2011-01-30
          • 2011-05-27
          相关资源
          最近更新 更多