最近一直在弄 COM(组件对象模型)的东西,把戏太多,新概念太多。还是写点东西留念下吧,也算是学过,虽然我很不情愿学 COM。
先看下 BSTR,BSTR(基本字符串、二进制字符串)是由 COM、自动化(Automation)、交互函数(Interop functions)使用的一种字符串数据类型。
在 Windows SDK 的头文件中,BSTR 相关的类型有着如下的定义:
typedef wchar_t WCHAR; typedef WCHAR OLECHAR; typedef OLECHAR* BSTR; typedef BSTR* LPBSTR;
注:OLECHAR 也可能被定义成 char,但几乎没有这样定义的 Windows 版本,所以就不再多提了。)
也就是说,BSTR 的底层类型就是一个简单的 wchar_t 字符串。众所周知,在 C语言 中,字符串是以 '\0' 结尾的字符数组来表示(C语言中实际并不存在字符串类型)。因为是以 '\0' 来表示结束的,所以,C语言中的字符串是以计算到第 1 个 '\0' 之前的长度来作为字符串的长度的。因此,其不是二进制安全的。但是,在其它较高级的语言中,出现了真正的字符串类型,如 C++/Visual Basic/Javascript/C#,他们实现的是真正的字符串类型,他们是基于对象的,他们有自己的长度字段。字符串长度的计算不再依靠 '\0' 来求值。
既然 COM 是 语言/平台 无关的一种规范,那么自然就不能再简单地使用 C语言 独自拥有的表示字符串的类型作为通用字符串的传递了。于是 COM 字符串定义了通用的字符串类型,虽然 BSTR 是一个基本类型,一个指向 宽字符数组 的指针类型,但它有着如下的内存模型。
BSTR 是一种复合类型,由三个部分构成:长度前缀、数据字符串、终止符。说明如下:
部分 | 描述 |
---|---|
长度前缀 | 4 字节无符号整数,记录了 BSTR 中第 2 部分 数据字符串 的长度(字节数),因此不包含自身与终止符; |
数据字符串 | 一个可以存放任意字符的 Unicode 字符串; |
终止符 | 两个 L'\0' 字符,因此是 4 个字节; |
注:以上 3 个部分,在内存中紧密相连,中间没有任何间隔(内存空隙、或是因为对齐来带来的),因此:复合类型 BSTR 占用的总内存字节大小为:4 + 数据字符串长度(字符数) * 2 + 4。
值得注意的是:BSTR 指向的是该 复合类型 的第 2 部分的开始地址,简单地说就是:BSTR 这个指针指向 数据字符串 中第 1 个字符,而不是 长度前缀。
因此,在知道了 BSTR 是一种复合类型以后,就不能因为它也是一种 wchar_t 就直接把 wchar_t 字符数组的指针赋值给它。就像下面这样:
BSTR bstr = L"I'm a happy BSTR."; // 错误!
COM 为此提供了一系列的用于操作 BSTR 的函数与类。函数比如:SysAllocString 用于动态分配 BSTR、SysFreeString 用于释放动态分配的 BSTR、SysStringLen 用于计算 BSTR 的长度(字符数),类比如:CComBSTR: 是由 COM 提供的对 BSTR 的包装,能自动管理内存,但不具备引用计数功能、_bstr_t: 是由编译器运行时提供的对 BSTR 的包装,就像是 std::string 对 char* 的包装,能自动管理 BSTR 的内存分配,同时还具体引用计数功能。