如何有效地将 BSTR 复制到 wchar_t[]?

Posted

技术标签:

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

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

【问题讨论】:

【参考方案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*;

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

有两个重要的警告。

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

    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 上):你当然是对的。感谢您注意到我的失误。

以上是关于如何有效地将 BSTR 复制到 wchar_t[]?的主要内容,如果未能解决你的问题,请参考以下文章

C++如何把string类型的字符串负值给wchar_t?求完整实例。

错误 C2440:“默认参数”:无法从“const wchar_t [1]”转换为“BSTR”

如何最好地将 CString 转换为 BSTR 以将其作为“in”参数传递给 COM 方法?

如何有效地将 OleVariant 数组复制到我自己的结构中?

如何以异步方式有效地将变量从 Matlab 传递到 GPU?

如何有效地将数字字符串值分配给整数? [复制]