一、TCHAR,LPTSTR,LPCTSTR:定義于WinNT.h
//
// Neutral ANSI/UNICODE types and macros
//
#ifdef UNICODE // r_winnt
#ifndef _TCHAR_DEFINED
typedef WCHAR TCHAR, *PTCHAR;
typedef WCHAR TBYTE , *PTBYTE ;
#define _TCHAR_DEFINED
#endif /* !_TCHAR_DEFINED */
typedef LPWSTR LPTCH, PTCH;
typedef LPWSTR PTSTR, LPTSTR;
typedef LPCWSTR PCTSTR, LPCTSTR;
typedef LPUWSTR PUTSTR, LPUTSTR;
typedef LPCUWSTR PCUTSTR, LPCUTSTR;
typedef LPWSTR LP;
#define __TEXT(quote) L##quote????? // r_winnt
#else?? /* UNICODE */?????????????? // r_winnt
#ifndef _TCHAR_DEFINED
typedef char TCHAR, *PTCHAR;
typedef unsigned char TBYTE , *PTBYTE ;
#define _TCHAR_DEFINED
#endif /* !_TCHAR_DEFINED */
typedef LPSTR LPTCH, PTCH;
typedef LPSTR PTSTR, LPTSTR, PUTSTR, LPUTSTR;
typedef LPCSTR PCTSTR, LPCTSTR, PCUTSTR, LPCUTSTR;
#define __TEXT(quote) quote???????? // r_winnt
#endif /* UNICODE */??????????????? // r_winnt
類似的定義出現于Wtypes.h:
typedef char CHAR;
typedef /* [string] */ CHAR *LPSTR;
typedef /* [string] */ const CHAR *LPCSTR;
#ifndef _WCHAR_DEFINED
#define _WCHAR_DEFINED
typedef wchar_t WCHAR;
typedef WCHAR TCHAR;
#endif // !_WCHAR_DEFINED
typedef /* [string] */ WCHAR *LPWSTR;
typedef /* [string] */ TCHAR *LPTSTR;
typedef /* [string] */ const WCHAR *LPCWSTR;
typedef /* [string] */ const TCHAR *LPCTSTR;
注意在TCHAR.h, WinNT.h中都定義了TCHAR,它們是靠#ifdef _TCHAR_DEFINED來判斷的。在VC6.0中:
#include <WINNT.h>只在WINDEF.H中出現過。
#include <windef.h>出現在 Windows.h中。
#include <windef.h>、#include <TCHAR.h>出現在ATL/INCLUDE/ATLBASE.H中
TCHAR.h中重新定義了標準C庫的很多字符串處理函數的宏,只要在工程中定義或取消UNICODE和_UNICODE,就可以在多字節字符串和UNICODE間互相轉換。按Win32編程要求的"Think In UNICODE",TCHAR.h中的函數應該是經常用到的。
二、BSTR(BASIC String):定義于WTypes.h
#if defined(_WIN32) && !defined(OLE2ANSI)
??? typedef WCHAR OLECHAR;
??? typedef /* [string] */ OLECHAR *LPOLESTR;
??? typedef /* [string] */ const OLECHAR *LPCOLESTR;
#?? define OLESTR(str) L##str
#else
??? typedef char????? OLECHAR;
??? typedef LPSTR???? LPOLESTR;
??? typedef LPCSTR??? LPCOLESTR;
#?? define OLESTR(str) str
#endif
typedef /* [wire_marshal] */ OLECHAR *BSTR;
可以看到,BSTR實際上還是一個C-style的寬字符串(如果定義了OLE2ANSI,就是C字符串了,但很少用到,因為在OLE接口中傳遞時都是寬字符串),只不過在使用BSTR時,得用到特殊的申請/釋放函數組。因為BSTR指針位置前面有一個四字節的數據,存儲的是BSTR的長度。就是這個原因,我們不能把OLECHAR*當作BSTR來用,而編譯器在遇到這種方式時還不報錯!
在MFC程序中不用BSTR時,可以在stdafx.h中所有#include之前加一行:
#define _AFX_NO_BSTR_SUPPORT
在ATL對象的接口定義中,無論是雙接口還是自定義接口,我們一般看不到TCHAR等定義,我們看到的是BSTR(雙接口時的VARIANT,如果是BSTR類型,可以直接用BSTR的),這要求我們作一些處理。對于傳入的參數,一般的做法是將參數賦給一個串處理類。對于傳出的參數,一般的方法是內部先處理,最后將字符串復制一份傳出去。
三、CComBSTR
CComBSTR是ATL庫中的一個BSTR封裝類,提供了一些簡單的函數,支持各類字符串到BSTR的轉換,但沒有格式化、搜索、子串處理等基本功能。功能太過簡單使得這個類可以說是一個雞脅,只能作一個轉換工具用。奇怪的是在Visaul .NET 2003版本中還是沒什么大的改進,我本以為M$會把CString的所有功能都搬進去的。
在M$的《用 CComBSTR 進行編程》提到了一些使用CComBSTR時要注意的一些問題。
四、_bstr_t
_bstr_t是一個與ATL無關的COM的支持類,這個類定義在comutil.h文件中(同時還定義有一個_variant_t類)。這個類也是一個功能與CComBSTR類似,主要用于轉換是,也是一個功能不怎么強的類。
在comutil.h還定義了兩個函數用于在標準C字符串與BSTR間的轉換,可以說這是個底層函數。程序員對TCHAR作一次分解就可以寫出TCHAR適用的程序了。
namespace _com_util {
??? // Convert char * to BSTR
??? //
??? BSTR __stdcall ConvertStringToBSTR(const char* pSrc) ;
??? // Convert BSTR to char *
?? //
?? char* __stdcall ConvertBSTRToString(BSTR pSrc) ;
}
五、CString/CStringA/CStringW
CString在VC6.0以前是MFC的一個類,要想在COM中使用,就得加上那個900K的MFC42.DLL。可能是MFC下的CString太好用了(:p,不要打我),所以M$干脆把這個類搬到ATL中。在Visual C++.NET中CStringT(在CStringT.h中實現)成為ATL的一個模板方式的類,只是將以前在MFC中的方法搬了過來;而CString定義在afxstr.h中:
// MFC-enabled compilation. Use MFC memory management and exceptions;
// also, use MFC module state.
// Don't import when MFC dll is being built
#if !defined(_WIN64) && defined(_AFXDLL)
#if defined(_MFC_DLL_BLD)
template class ATL::CSimpleStringT< char, true >;
template class ATL::CStringT< char, StrTraitMFC_DLL< char > >;
template class ATL::CSimpleStringT< wchar_t, true >;
template class ATL::CStringT< wchar_t, StrTraitMFC_DLL< wchar_t > >;
#else
template class __declspec(dllimport) ATL::CSimpleStringT< char, true >;
template class __declspec(dllimport) ATL::CStringT< char, StrTraitMFC_DLL< char > >;
template class __declspec(dllimport) ATL::CSimpleStringT< wchar_t, true >;
template class __declspec(dllimport) ATL::CStringT< wchar_t, StrTraitMFC_DLL< wchar_t > >;
#if defined(_NATIVE_WCHAR_T)
template class __declspec(dllimport) ATL::CSimpleStringT< unsigned short, true >;
template class __declspec(dllimport) ATL::CStringT< unsigned short, StrTraitMFC_DLL< unsigned short > >;
#endif // _NATIVE_WCHAR_T
#endif // _MFC_DLL_BLD
typedef ATL::CStringT< wchar_t, StrTraitMFC_DLL< wchar_t > > CStringW;
typedef ATL::CStringT< char, StrTraitMFC_DLL< char > > CStringA;
typedef ATL::CStringT< TCHAR, StrTraitMFC_DLL< TCHAR > > CString;
#else
typedef ATL::CStringT< wchar_t, StrTraitMFC< wchar_t > > CStringW;
typedef ATL::CStringT< char, StrTraitMFC< char > > CStringA;
typedef ATL::CStringT< TCHAR, StrTraitMFC< TCHAR > > CString;
#endif // !_WIN64 && _AFXDLL
關于ATL7.0中CString的用法,在M$的《自 Visual C++ 6.0 以來 ATL 7.0 和 MFC 7.0 中的重大更改》中有提到:從 BSTR 轉換到 CString
在 Visual C++ 6.0 中,使用下面的代碼是可以接受的:
BSTR bstr = SysAllocString(L"Hello");
CString str = bstr;
SysFreeString(bstr);對于 Visual C++ .NET 下的新項目,這將在 ANSI 版本下導致下面的錯誤error C2440: 'initializing' : cannot convert from 'BSTR' to
'ATL::CStringT<BaseType,StringTraits>'
現在有 CString 的 UNICODE 和 ANSI 版本(CStringW 和 CStringA)。若要標記任何由隱式轉換導致的不必要的系統開銷,采用反向類型(如帶 UNICODE 參數的 CStringA,或者帶 ANSI 參數的 CStringW)的構造函數現在在 stdafx.h 中被使用下面的項標記為顯式的:
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS
若要避免此錯誤,請執行下列操作之一:
使用 CStringW 以避免轉換:
???
BSTR bstr = SysAllocString(L"Hello");
??? CStringW str = bstr;
??? SysFreeString(bstr);
顯式調用該構造函數:
??? BSTR bstr = SysAllocString(L"Hello");
??? CString str = CString(bstr);
??? SysFreeString(bstr);
從 stdafx.h 中移除 #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS 行。
在VC6.0及以前,很多程序員都做過將MFC中的CString,這種在網上很多。我看到的一個很完整的是gosh的gd::CString,網址是
http://www.codeproject.com/soap/serialize_xml.asp
。據這位老兄自己說,他自己還沒有VC 6.0編譯器。
六、std::string和std::wstring
std::string在我看來是std::vector<char>。但這個類可以說是一個有史以來計算機程序員對字符串處理類的一個總結,它的算法應該是沒得說的。
七、字符串(類)間的轉換
以上各類字符串間的轉換從概念上有兩種方式,第一種是已經有的轉換接口,另一種是基于某一個中間格式作轉換,其實第一種實現也是基于第二種的。
char 與 wchar_t 間的轉換:
簡單地說,是兩個Win32函數:WideCharToMultiByte和MultiByteToWideChar。當然中間還涉及到代碼頁和線程代碼頁等參數。如果用標準C函數wctomb和mbtowc也可以。
在M$的《自Visual C++6.0以來ATL7.0和MFC7.0中的重大更改》中有提到:字符串轉換
Visual C++ 6.0 中 ATL 3.0 和 ATL 3.0 以前的 ATL 版本中,使用 atlconv.h 中的宏的字符串轉換始終是使用系統 (CP_ACP) 的 ANSI 代碼頁執行的。從 Visual C++ .NET 中的 ATL 7.0 開始,字符串轉換將使用當前線程的默認 ANSI 代碼頁執行,除非定義了 _CONVERSION_DONT_USE_THREAD_LOCALE(此情況下,如以前一樣使用系統的 ANSI 代碼頁)。
請注意,字符串轉換類(如 CW2AEX)使您得以將用于轉換的代碼頁傳遞給它們的構造函數。如果未指定代碼頁,這些類使用與宏相同的代碼頁。
有關更多信息,請參見 ATL 和 MFC 字符串轉換宏。
COM程序中,接口一般都是BSTR,所以無論在編寫COM組件時還是使用組件時,轉為BSTR和從BSTR轉出都可能遇到。如果全部采用UNICODE,麻煩要少一些,如果使用到了char或std::string,麻煩就不少了。但一般而言,我們有以下方法可以用:
在接口(組件)內部,全部用UNICODE處理。將傳入的BSTR轉換為std::wstring、gd:CString或是其它的什么類,在內部成員變量聲明時用同樣的類,內部處理時不用管外面,生成結果時用一個CComBST或是_bstr_t類作一次轉換就可以了。
在接口外部,調用COM組件的接口函數前,將字符串用_bstr_t或者comutil.h的函數作一個轉換,傳入組件進行處理;傳出時,作一個反向的轉換。
非COM程序中,接口一般用TCHAR*,這種方式的代價最小。
Trackback: http://tb.donews.net/TrackBack.aspx?PostId=245116
posted on 2009-12-07 17:43
我風 閱讀(2014)
評論(0) 編輯 收藏 引用