【发布时间】:2010-09-09 11:25:06
【问题描述】:
我有一个 BSTR 对象,我想将其转换为复制到 wchar__t 对象。棘手的是 BSTR 对象的长度可能从几千字节到几百千字节不等。有没有一种有效的方法来复制数据?我知道我可以只声明一个 wchar_t 数组并始终分配它需要保存的最大可能数据。然而,这意味着为可能只需要几千字节的东西分配数百千字节的数据。有什么建议吗?
【问题讨论】:
我有一个 BSTR 对象,我想将其转换为复制到 wchar__t 对象。棘手的是 BSTR 对象的长度可能从几千字节到几百千字节不等。有没有一种有效的方法来复制数据?我知道我可以只声明一个 wchar_t 数组并始终分配它需要保存的最大可能数据。然而,这意味着为可能只需要几千字节的东西分配数百千字节的数据。有什么建议吗?
【问题讨论】:
BSTR 对象包含一个长度前缀,因此查找长度很便宜。找出长度,分配一个足够大的新数组来保存结果,处理它,并记住在完成后释放它。
【讨论】:
使用 ATL 和 CStringT 然后你可以只使用赋值运算符。或者您可以使用 USES_CONVERSION 宏,这些宏使用堆分配,因此您可以确保不会泄漏内存。
【讨论】:
永远不需要转换。 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 的合规性。
【讨论】:
要记住的一件事是BSTR 字符串可以并且经常包含嵌入的空值。 null 并不意味着字符串的结尾。
【讨论】:
首先,如果您只需要阅读内容,您实际上可能根本不需要做任何事情。 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()
【讨论】: