<2011年12月>
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

统计

  • 随笔 - 24
  • 文章 - 0
  • 评论 - 17
  • 引用 - 0

常用链接

留言簿(4)

随笔分类

随笔档案

相册

搜索

  •  

最新评论

阅读排行榜

评论排行榜

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;
}


代码运行结果:

posted on 2011-12-03 22:56 小葱蘸酱 阅读(5222) 评论(0)  编辑 收藏 引用 所属分类: COM


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理