BSTR CComBSTR及ATL字符串转换宏

BSTR CComBSTR及ATL字符串转换宏
一 BSTR及CComBSTR

MSDN文档中说,BSTR是四字节长度前缀的,NULL结尾的宽字符串,称之为VB字符串或BINARY字符串。
依次查找几个头文件,不难发现,BSTR被typedef成OLECHAR *,而OLECHAR被typedef成WCHAR(未定义OLE2ANSI预处理宏)或char (定义OLE2ANSI预处理宏);继续顺藤摸瓜,WCHAR被typedef成wchar_t,而wchar_t被typedef成unsigned short。

最终的结果就是,BSTR被typedef成unsigned short *(未定义OLE2ANSI预处理宏)或char *(定义OLE2ANSI预处理宏)。

原来BSTR只是一个指针。

本文不考虑定义预处理宏OLE2ANSI的情况。

在COM编程中,凡是使用BSTR的地方,我们可以使用类CComBSTR来代替BSTR。CComBSTR封装了BSTR,通过各种重载构造函数和操作符重载,仅仅使用对象定义,type cast等简单语句,就可以实现BSTR与各种类型字符串,包括LPSTR,LPOLESTR之间的转换,赋值,连接,比较等操作。

二 ATL字符串转换宏
除了CComBSTR类支持各种字符串类型到BSTR的转换以外,ATL还有一组字符串转换宏,可以方便的进行各种类型字符串之间的转换。这些宏有统一的形式:X2[C]Y或Z2BSTR。其中X,Y,Z可以为A,W,T,OLE中的任一种,X与Y不相同;C表示转换的目标类型为const——因此,这样的组合方式总共有4x3x2+4=28种。

根据预处理宏_UNICODE定义与否,T被替代成W或A,所以这28个转换宏最终可以归结为两类:一类是A和W之间的转换,另一类是A,W到BSTR的转换。

下面看看第一类中A2W的实现。
//  Exerpt from ATLCONV.H   
#define  A2W(lpa) (\
        ((_lpa 
=  lpa)  ==  NULL)  ?  NULL : (\
            _convert 
=  (lstrlenA(_lpa) + 1 ),\
            ATLA2WHELPER((LPWSTR) alloca(_convert
* 2 ), _lpa, _convert)))

A2W是支持从ANSI字符串到UNICODE字符串的转换宏。可以看到,A2W的主体是一个?:表达式,根据源字符串是否为NULL,对冒号之前或之后的表达式进行求值。冒号后的部分又是一个逗号表达式:先计算源字符串的长度,再调用被定义为同名宏(大小写不同)的转换函数,转换函数中调用MultiByteToWideChar系统函数进行具体的转换,逗号表达式的值是转换函数的返回值。最终,转换宏的值是NULL或者转换函数的返回值。

值得注意的是,在A2W宏定义中引用了类似_lpa, _convert等标识符,那么这些标识符是什么,又是哪里来的呢?
这就是为什么在使用这些字符串转换宏之前,需要统一加上一句
USES_CONVERSION;
USES_CONVERSION也是宏定义,就是用来声明转换宏中使用的标识符的。

另外,MSDN中特别提到,A和W之间的转换(A2W,W2A),以及通过预处理宏_UNICODE翻译成A和W之间转换的宏,它们无一例外的都是从调用函数栈上分配空间存放转换结果字符串。因此,转换结果字符串在调用函数返回后自动被清除,也就是不能保留转换结果用于调用函数外使用。

与之相对,再看看第二类,A,W,T,OLE到BSTR的转换宏实现。
// Exerpt from ATLCONV.H
inline BSTR OLE2BSTR(LPCOLESTR lp) { return  ::SysAllocString(lp);}
#if  defined(_UNICODE)
//  in these cases the default (TCHAR) is the same as OLECHAR
    inline BSTR T2BSTR(LPCTSTR lp) { return  ::SysAllocString(lp);}
    inline BSTR A2BSTR(LPCSTR lp) {USES_CONVERSION; 
return  A2WBSTR(lp);}
    inline BSTR W2BSTR(LPCWSTR lp) {
return  ::SysAllocString(lp);}
#elif  defined(OLE2ANSI)
//  in these cases the default (TCHAR) is the same as OLECHAR
    inline BSTR T2BSTR(LPCTSTR lp) { return  ::SysAllocString(lp);}
    inline BSTR A2BSTR(LPCSTR lp) {
return  ::SysAllocString(lp);}
    inline BSTR W2BSTR(LPCWSTR lp) {USES_CONVERSION; 
return  ::SysAllocString(W2COLE(lp));}
#else
    inline BSTR T2BSTR(LPCTSTR lp) {USES_CONVERSION; 
return  A2WBSTR(lp);}
    inline BSTR A2BSTR(LPCSTR lp) {USES_CONVERSION; 
return  A2WBSTR(lp);}
    inline BSTR W2BSTR(LPCWSTR lp) {
return  ::SysAllocString(lp);}
#endif

同样,除了OLE2BSTR以外,A,W,T到BSTR的转换根据是否定义预处理宏_UNICODE进行不同的处理,最终归结为两种类型的转换:A2BSTR和W2BSTR。对于A2BSTR,由于BSTR也就是W, 所以A2BSTR的转换在理论上同A2W应该相同。

但实际上,A2BSTR调用函数A2WBSTR,A2WBSTR内部从堆上分配空间,再调用系统转换函数MultiByteToWideChar进行转换——这就是A,W之间的转换与2BSTR类型的转换的根本不同之处;W2BSTR就简单了,由于BSTR即是W字符串,因此不需要实际转换,只需分配空间并拷贝源串作为转换结果即可。

对于OLE2BSTR,由于BSTR是从OLECHAR定义来的(见上面BSTR类型定义),因此不管预处理宏如何定义(包括OLE2ANSI是否定义),二者的类型始终是一致的,因此,从OLE到BSTR,并不需要进行实际的转换,只需分配空间并拷贝源串作为转换结果即可。

总结下来,28个字符串转换宏中,根据结果字符串存放位置分为两类,一类是A,W之间的转换宏,这一类宏的转换结果字符串放在调用函数的栈空间,调用函数返回会该空间自动清除,第二类为目的类型为BSTR的转换宏,其转换结果字符串放在系统堆,在调用函数返回后结果字符串仍然存在。这是两类转换宏之间的最显著差别。

测试代码
//  TestATLX2Y.cpp

#include 
< iostream >
#include 
< atlbase.h >
using   namespace  std;

#ifdef _UNICODE
#define  TCOUT wcout
#else
#define  TCOUT cout
#endif

int  main()
{
#ifdef _UNICODE
    TCOUT 
<<  _T( " ----- Test with _UNICODE -------- " <<  endl;
#else
    TCOUT 
<<  _T( " ----- Test without _UNICODE -------- " <<  endl;
#endif

    LPTSTR lptstr
= _T( " TCHAR is either char or wchar_t " );
    LPSTR lpstr
= " How can I indicate i'm a LPSTR? " ;
    LPWSTR lpwstr
= L " I am a wide-character string " ;
    TCOUT 
<<  _T( " lptstr: " <<  lptstr  <<  endl;
    cout 
<<   " lpstr: "   <<  lpstr  <<  endl;
    wcout 
<<  L " lpwstr: "   <<  lpwstr  <<  endl;
    
    USES_CONVERSION;
    TCOUT 
<<  _T( " A2T: " <<  A2T(lpstr)  <<  endl;
    wcout 
<<  L " A2W: "   <<  A2W(lpstr)  <<  endl;
    cout 
<<   " T2A: "   <<  T2A(lptstr)  <<  endl;
    wcout 
<<  L " T2W: "   <<  T2W(lptstr)  <<  endl;
    cout 
<<   " W2A: "   <<  W2A(lpwstr)  <<  endl;
    TCOUT 
<<  _T( " W2T: " <<  W2T(lpwstr)  <<  endl;
    BSTR bstr;
    wcout 
<<  L " A2BSTR: "   <<  (bstr = A2BSTR(lpstr))  <<  endl;
    ::SysFreeString(bstr);
    wcout 
<<  L " W2BSTR: "   <<  (bstr = W2BSTR(lpwstr))  <<  endl;
    
    wcout 
<<  L " T2BSTR: "   <<  (bstr = T2BSTR(lptstr))  <<  endl;
    
//  How to know if we need to free the space pointered by returned value of T2BSTR?
     if (( void   * )bstr != ( void   * )lptstr) ::SysFreeString(bstr);
    
    wcout 
<<  L " OLE2BSTR: "   <<  OLE2BSTR(lpwstr)  <<  endl;
    
return   0 ;
}


代码运行结果:

你可能感兴趣的:(BSTR CComBSTR及ATL字符串转换宏)