BSTR
BSTR(Basic string or binary string)是COM 、Automatic、Interop 使用的string数据类型。在从脚本访问的所有接口中使用BSTR。
BSTR被定义为OLECHAR*类型,查看OLECHAR类型的声明,可以发现它其实是一个wchar_t类型,也就是说一个BSTR字符串其实是一个UNICODE字符串。
COM 是一种跨编程语言的平台,需要提供语言无关的数据类型。多数编程语言有自己的字符串表示。
· C++ 字符串是以 0 结束的 ASCII 或 Unicode 字符数组
· Visual Basic 字符串是一个 ASCII 字符数组加上表示长度的前缀。
· Java 字符串是以 0 结束的 Unicode 字符数组。
需要定义一种通用的字符串类型,可以很容易的匹配到不同编程语言。 在 C++ 中,就是 BSTR 。
//头文件 wtypes.h
typedef WCHAR OLECHAR;
typedef OLECHAR* BSTR;
typedef BSTR* LPBSTR;
BSTR是一个复合性的数据类型,其包含长度前缀(length prefix),字符串数据(Data string),和结尾标志(Terminator)。
长度前缀:4字节的整数,描述字符串数据的长度。出现在字符串数据的第一个字符之前。
字符串数据:Unicode编码的字符串,可包含多个嵌入的空字符。
结尾标志:空的WCHAR
一个BSTR是一个指针,其指向string数据的第一个字符,而非长度前缀。
BSTR由COM内存分配函数分配,所以能被函数直接返回,不用考虑内存的分配问题。
//BSTR->char*
#include <comutil.h>
#include <stdio.h>#pragma comment(lib, "comsuppw.lib")int main() {BSTR bstrText = ::SysAllocString(L"Test");//声明BSTRwprintf_s(L"BSTR text: %s\n", bstrText);char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);printf_s("char * text: %s\n", lpszText2);SysFreeString(bstrText);delete[] lpszText2;
}
//char* ->BSTR:
#include <comutil.h>
#include <stdio.h>#pragma comment(lib, "comsuppw.lib")
#pragma comment(lib, "kernel32.lib")int main() {char* lpszText = "Test";printf_s("char * text: %s\n", lpszText);BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);wprintf_s(L"BSTR text: %s\n", bstrText);SysFreeString(bstrText);
}
COM中一共提供了两个类来对BSTR进行封装,一个是编译器提供的COM支持类:_bstr_t,另一个则是ATL库提供的包裹类:CComBSTR。
_bstr_t
Header:
Lib: comsuppw.lib or comsuppwd.lib
_bstr_t 是微软 C++ COM 扩展的一部分。 _bstr_t 封装了 BSTR 数据类型。 _bstr_t 通过 SysAllocString and SysFreeString 等 BSTR APIs 管理资源的分配和释放
_bstr_t提供的封装层次较低,它仅提供简单的字符串构造、连接以及比较操作,在某种意义上,它更像是一个万能转换头,提供char*、wchar_t*类型到BSTR类型之间的转换。
关于_bstr_t,需要注意的有三点:
一是,它内部采用引用计数来管理字符串数组的生命周期;
二是,它在构造或操作的过程是有可能抛出异常的;
三是,它内部使用SysAllocString和SysFreeString来分配和释放内存,在调用Detach操作之后,_bstr_t内“包裹”的BSTR字符串就会脱离出来,脱离出来的BSTR字符串,是需要手动调用SysFreeString来释放内存的,否则会造成内存泄露问题。
CComBSTR
Header: atlbase.h
CComBSTR 是 ATL 提供的 BSTR 包装类,建议使用CComBSTR。
CComBSTR Method |
Description |
CComBSTR |
多个版本的构造函数用来创建新的 BSTR 。可以使用的参数包括 LPCOLESTR, LPCSTR, CComBSTR 。 |
~CComBSTR, Empty |
释放内部封装的 BSTR. |
Attach, Detach, Copy |
Attach 把一个已经存在 BSTR 加入类中。 Detach 把劣种的 BSTR 剥离,以便在超出作用域的时候,析构函数不会释放 BSTR 。 Detach 用于把 CComBSTR 赋给 [out] 参数。 Copy 用于产生一个 BSTR 的副本。一般用于用于把 CComBSTR 内容赋给 [out] 参数。 |
operator BSTR, operator& |
允许直接操作内部的 BSTR 。 operator BSTR 用于把 CComBSTR 传给 BSTR 输入 [in] 参数。 operator& 用于把 CComBSTR 传给 BSTR* 类型输出 [out] 参数。 |
operator=, operator+=, operator |
重载运算符,用于赋值、字符串连接、简单比较。 |
Append, AppendBSTR |
字符串连接 |
Length |
计算字符串长度 |
LoadString |
利用字符串资源初始化 BSTR 。 |
ToLower, ToUpper |
字符串大小写转换。 |
WriteToStream,ReadFromStream |
从 IStream 中读 / 写 BSTR 。 |
在使用CComPtr时的注意事项如下:
1、转换问题:虽然一些 CComBSTR 方法自动将 ANSI 字符串参数转换为 Unicode,但这些方法总是返回 Unicode 格式的字符串。若要将输出字符串转换回 ANSI,请使用 ATL 转换类。另外,如果使用字符串来修改 CComBSTR 对象,请使用宽字符字符串。这会减少不必要的转换。
2、范围问题:与任何功能完善的类一样,CComBSTR 在超出范围时将释放其资源。如果函数返回指向 CComBSTR 字符串的指针,这会引起问题,因为指针将引用已经释放的内存。在这种情况下,请使用 Copy 方法。
3、显式释放 CComBSTR 对象:在对象超出范围之前,可以手动调用SysFreeString显式释放包含在 CComBSTR 对象中的字符串。如果字符串被释放,则 CComBSTR 对象无效。
4、在循环中使用 CComBSTR 对象:在CComBSTR 类分配缓冲区来执行某些运算时,如 += 运算符或 Append 方法,建议不要在紧密型循环内执行字符串操作。在这种情况下,CString可以提供更好的性能;
5、将已初始化的 CComBSTR 的地址作为 [out] 参数传递到函数会导致内存泄漏。若要避免泄漏,请在作为 [out] 参数传递地址之前,对现有的 CComBSTR 对象调用 Empty 方法。请注意,如果函数的参数是 [in, out],则同样的代码将不会导致泄漏。