翻译《有关编程、重构及其他的终极问题?》——27.狡猾的BSTR字符串

翻译《有关编程、重构及其他的终极问题?》——27.狡猾的BSTR字符串

标签(空格分隔): 翻译 技术 C/C++
作者:Andrey Karpov
翻译者:顾笑群 - Rafael Gu
最后更新:2017年06月20日


27.狡猾的BSSTR字符串

让我们再讨论一个讨厌的数据类型——BSTR(basic string或者binary string)。

下面这段代码来自VirtualBox项目。这段代码包含被PVS-Studio分析诊断的错误: V745 A ‘wchar_t ’ type string is incorrectly converted to ‘BSTR’ type string. Consider using ‘SysAllocString’ function(译者注:大意是“wchar_t “不能正确的转换为“BSTR”类型,请考虑适用“SysAllocString”函数)。

....
HRESULT EventClassID(BSTR bstrEventClassID);
....
hr = pIEventSubscription->put_EventClassID(
                    L"{d5978630-5b9f-11d1-8dd2-00aa004abd5e}");

解释
下面是BSTR类型被定义的方式:

typedef wchar_t OLECHAR;
typedef OLECHAR * BSTR;

第一眼看去,好像“wchar_t *”和BSTR是一模一样的,但其实并非如此,这也导致了很多困惑和错误。

让我们用一种更好的方式来讨论有关BSTR的问题吧。

下面这些信息来自MSDN的官网。虽然读MSDN文档不是太开心的事情,但我们不得不读。

BSTR(基本的字符串或二进制字符串)是一种被COM(译者注:是一种微软历史上大力提倡的组件技术,2000左右就慢慢没落了,现在Windows上还在使用,但不需要用户关注太多了)、自动化和交互操作等函数使用的字符串数据类型。在所有使用BSTR数据类型使用的接口可以被脚本语言调用。下面是BSTR的描述:
1. 前置长度:最前面有4个字节的整型,其中包含了后续紧跟着的整个字符串的字节长度值。这个值不包括字符串结尾符;
2. **字符串数据:**Unicode编码的字符串,还可能包含多个嵌入的null字符。
3. 结尾符:两个null字符。
BSTR是一个指针,其指向字符串数据的第一个字符,而不是指向前置长度。BSTR使用COM内存分配函数分配内存,所以它们由方法返回而无须关心内存分配。下面的代码市错误的:

BSTR MyBstr = L"I am a happy BSTR";

这个代码可以通过编译,但因为没有包含前置长度所以MyBstr不能正常的工作。如果你用调试器区检查这个变量对应位置的内存,你不会看到4个字节用于处理字符串数据的前置长度。相反,请使用如下代码:

BSTR MyBstr = SysAllocString(L"I am a happy BSTR");

现在,检查这个变量对应位置内存的调试器将会看到一个值为34的前置长度。对于一个长度为17的单字节字符串而言,通过“L”字符操作符将其转换为宽字节,34就是其对应的长度。调试器还能在字符串的结尾看到一个两字节的字符串结尾符号(0x0000)。

在原本期望BSTR的COM函数中,如果你只是简单的传入Unicode字符串作为变量,这个COM函数会失败。

我希望这对你来说已经足够了解为何我们应该区分对待BAST和“wchar_t *”字符串类型。

下面是额外的一些参考链接:
1. MSDN:BSTR
2. StackOverflow:针对给BSTR传入“wchar_t *”的静态代码分析
3. StackOverflow:从BSTR到std::string(std::wstring)以及相反的过程
4. Robert Pittenger:BSTR和CString转换的向导
5. Eric Lippert:Eric针对BSTR语法的完整向导

正确的代码

hr = pIEventSubscription->put_EventClassID(
       SysAllocString(L"{d5978630-5b9f-11d1-8dd2-00aa004abd5e}"));

建议
这次说的和上一个章说的类似,如果你看到一个未知的类型,最好不要慌张,并且耐心的去查看相关文档,这非常重要,所以这一章有些啰嗦也情有可原。

猜你喜欢

转载自blog.csdn.net/headman/article/details/73498523