一 BSTR及CComBSTR
MSDN文檔中說(shuō),BSTR是四字節(jié)長(zhǎng)度前綴的,NULL結(jié)尾的寬字符串,稱(chēng)之為VB字符串或BINARY字符串。
依次查找?guī)讉€(gè)頭文件,不難發(fā)現(xiàn),BSTR被typedef成OLECHAR *,而OLECHAR被typedef成WCHAR(未定義OLE2ANSI預(yù)處理宏)或char (定義OLE2ANSI預(yù)處理宏);繼續(xù)順藤摸瓜,WCHAR被typedef成wchar_t,而wchar_t被typedef成unsigned short。
最終的結(jié)果就是,BSTR被typedef成unsigned short *(未定義OLE2ANSI預(yù)處理宏)或char *(定義OLE2ANSI預(yù)處理宏)。
原來(lái)BSTR只是一個(gè)指針。
本文不考慮定義預(yù)處理宏OLE2ANSI的情況。
在COM編程中,凡是使用BSTR的地方,我們可以使用類(lèi)CComBSTR來(lái)代替BSTR。CComBSTR封裝了BSTR,通過(guò)各種重載構(gòu)造函數(shù)和操作符重載,僅僅使用對(duì)象定義,type cast等簡(jiǎn)單語(yǔ)句,就可以實(shí)現(xiàn)BSTR與各種類(lèi)型字符串,包括LPSTR,LPOLESTR之間的轉(zhuǎn)換,賦值,連接,比較等操作。
二 ATL字符串轉(zhuǎn)換宏
除了CComBSTR類(lèi)支持各種字符串類(lèi)型到BSTR的轉(zhuǎn)換以外,ATL還有一組字符串轉(zhuǎn)換宏,可以方便的進(jìn)行各種類(lèi)型字符串之間的轉(zhuǎn)換。這些宏有統(tǒng)一的形式:X2[C]Y或Z2BSTR。其中X,Y,Z可以為A,W,T,OLE中的任一種,X與Y不相同;C表示轉(zhuǎn)換的目標(biāo)類(lèi)型為const——因此,這樣的組合方式總共有4x3x2+4=28種。
根據(jù)預(yù)處理宏_UNICODE定義與否,T被替代成W或A,所以這28個(gè)轉(zhuǎn)換宏最終可以歸結(jié)為兩類(lèi):一類(lèi)是A和W之間的轉(zhuǎn)換,另一類(lèi)是A,W到BSTR的轉(zhuǎn)換。
下面看看第一類(lèi)中A2W的實(shí)現(xiàn)。
// 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字符串的轉(zhuǎn)換宏。可以看到,A2W的主體是一個(gè)?:表達(dá)式,根據(jù)源字符串是否為NULL,對(duì)冒號(hào)之前或之后的表達(dá)式進(jìn)行求值。冒號(hào)后的部分又是一個(gè)逗號(hào)表達(dá)式:先計(jì)算源字符串的長(zhǎng)度,再調(diào)用被定義為同名宏(大小寫(xiě)不同)的轉(zhuǎn)換函數(shù),轉(zhuǎn)換函數(shù)中調(diào)用MultiByteToWideChar系統(tǒng)函數(shù)進(jìn)行具體的轉(zhuǎn)換,逗號(hào)表達(dá)式的值是轉(zhuǎn)換函數(shù)的返回值。最終,轉(zhuǎn)換宏的值是NULL或者轉(zhuǎn)換函數(shù)的返回值。
值得注意的是,在A(yíng)2W宏定義中引用了類(lèi)似_lpa, _convert等標(biāo)識(shí)符,那么這些標(biāo)識(shí)符是什么,又是哪里來(lái)的呢?
這就是為什么在使用這些字符串轉(zhuǎn)換宏之前,需要統(tǒng)一加上一句
USES_CONVERSION;
USES_CONVERSION也是宏定義,就是用來(lái)聲明轉(zhuǎn)換宏中使用的標(biāo)識(shí)符的。
另外,MSDN中特別提到,A和W之間的轉(zhuǎn)換(A2W,W2A),以及通過(guò)預(yù)處理宏_UNICODE翻譯成A和W之間轉(zhuǎn)換的宏,它們無(wú)一例外的都是從調(diào)用函數(shù)棧上分配空間存放轉(zhuǎn)換結(jié)果字符串。因此,轉(zhuǎn)換結(jié)果字符串在調(diào)用函數(shù)返回后自動(dòng)被清除,也就是不能保留轉(zhuǎn)換結(jié)果用于調(diào)用函數(shù)外使用。
與之相對(duì),再看看第二類(lèi),A,W,T,OLE到BSTR的轉(zhuǎn)換宏實(shí)現(xiàn)。
// 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的轉(zhuǎn)換根據(jù)是否定義預(yù)處理宏_UNICODE進(jìn)行不同的處理,最終歸結(jié)為兩種類(lèi)型的轉(zhuǎn)換:A2BSTR和W2BSTR。對(duì)于A(yíng)2BSTR,由于BSTR也就是W,
所以A2BSTR的轉(zhuǎn)換在理論上同A2W應(yīng)該相同。但實(shí)際上,A2BSTR調(diào)用函數(shù)A2WBSTR,A2WBSTR內(nèi)部從堆上分配空間,再調(diào)用系統(tǒng)轉(zhuǎn)換函數(shù)MultiByteToWideChar進(jìn)行轉(zhuǎn)換——這就是A,W之間的轉(zhuǎn)換與2BSTR類(lèi)型的轉(zhuǎn)換的根本不同之處;W2BSTR就簡(jiǎn)單了,由于BSTR即是W字符串,因此不需要實(shí)際轉(zhuǎn)換,只需分配空間并拷貝源串作為轉(zhuǎn)換結(jié)果即可。
對(duì)于OLE2BSTR,由于BSTR是從OLECHAR定義來(lái)的(見(jiàn)上面BSTR類(lèi)型定義),因此不管預(yù)處理宏如何定義(包括OLE2ANSI是否定義),二者的類(lèi)型始終是一致的,因此,從OLE到BSTR,并不需要進(jìn)行實(shí)際的轉(zhuǎn)換,只需分配空間并拷貝源串作為轉(zhuǎn)換結(jié)果即可。
總結(jié)下來(lái),28個(gè)字符串轉(zhuǎn)換宏中,根據(jù)結(jié)果字符串存放位置分為兩類(lèi),一類(lèi)是A,W之間的轉(zhuǎn)換宏,這一類(lèi)宏的轉(zhuǎn)換結(jié)果字符串放在調(diào)用函數(shù)的棧空間,調(diào)用函數(shù)返回會(huì)該空間自動(dòng)清除,第二類(lèi)為目的類(lèi)型為BSTR的轉(zhuǎn)換宏,其轉(zhuǎn)換結(jié)果字符串放在系統(tǒng)堆,在調(diào)用函數(shù)返回后結(jié)果字符串仍然存在。這是兩類(lèi)轉(zhuǎn)換宏之間的最顯著差別。
測(cè)試代碼
// 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;
}
代碼運(yùn)行結(jié)果:
