众所周知,在C/C++中,字符串通常采用“以0结束的字符数组”来表示,而在Visual Basic中,字符串则采用“字符数组加长度前缀的方法”来表示。
COM是一个支持多语言的通用组件,它内部到底采用什么样的字符串类型呢?据说这个问题在微软内部经过很长时间的争论,最终VB派的人占了上风,这也直接导致COM在其内部采用BSTR(Basic String,Basic字符串)来作为其字符串类型。
BSTR被定义为OLECHAR*类型,查看OLECHAR类型的声明,可以发现它其实是一个wchar_t类型,也就是说一个BSTR字符串其实是一个UNICODE字符串。
COM中一共提供了两个类来对BSTR进行封装,一个是编译器提供的COM支持类:_bstr_t,另一个则是ATL库提供的包裹类:CComBSTR。
_bstr_t提供的封装层次较低,它仅提供简单的字符串构造、连接以及比较操作,在某种意义上,它更像是一个万能转换头,提供char*、wchar_t*类型到BSTR类型之间的转换。
关于_bstr_t,需要注意的有三点:一是,它内部采用引用计数来管理字符串数组的生命周期;二是,它在构造或操作的过程是有可能抛出异常的;三是,它内部使用SysAllocString和SysFreeString来分配和释放内存,在调用Detach操作之后,_bstr_t内“包裹”的BSTR字符串就会脱离出来,脱离出来的BSTR字符串,是需要手动调用SysFreeString来释放内存的,否则会造成内存泄露问题。
CComBSTR提供的封装层次更高,如果要操作BSTR,建议使用CComBSTR。
在使用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],则同样的代码将不会导致泄漏。
具体说明可参考MSDN官方文档 http://msdn.microsoft.com/zh-cn/library/cc485249(v=vs.71).aspx