C++字符串完全指南 - Win32字符编码(二)作者: 翻译:连波 类别: VC/VC.NET 日期: 2003-1-6 14:41:02 Win32 API中的MBCS 和 UnicodeAPI的二个字符集http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39098306-3,00.htm也许你没有注意到,Win32的API和消息中的字符串处理函数有二种,一种为MCBS字符串,另一种为Unicode字符串。例如,Win32中没有SetWindowText()这样的接口,而是用SetWindowTextA()和 SetWindowTextW()函数。后缀A (表示ANSI)指明是MBCS函数,后缀W(表示宽字符)指明是Unicode函数。编写Windows程序时,可以选择用MBCS或Unicode API接口函数。用VC AppWizards向导时,如果不修改预处理器设置,缺省使用的是MBCS函数。但是在API接口中没有SetWindowText()函数,该如何调用呢?实际上,在winuser.h头文件中做了以下定义:BOOL WINAPI SetWindowTextA ( HWND hWnd, LPCSTR lpString );BOOL WINAPI SetWindowTextW ( HWND hWnd, LPCWSTR lpString );#ifdef UNICODE #define SetWindowText SetWindowTextW#else #define SetWindowText SetWindowTextA#endif编写MBCS应用时,不必定义UNICODE,预处理为:#define SetWindowText SetWindowTextA然后将SetWindowText()处理为真正的API接口函数SetWindowTextA() (如果愿意的话,可以直接调用SetWindowTextA() 或SetWindowTextW()函数,不过很少有此需要)。如果要将缺省应用接口改为Unicode,就到预处理设置的预处理标记中去掉 _MBCS标记,加入UNICODE 和 _UNICODE (二个标记都要加入,不同的头文件使用不同的标记)。不过,这时要处理普通字符串反而会遇到问题。如有代码:HWND hwnd = GetSomeWindowHandle();char szNewText[] = "we love Bob!";SetWindowText ( hwnd, szNewText );编译器将"SetWindowText"置换为"SetWindowTextW"后,代码变为:HWND hwnd = GetSomeWindowHandle();char szNewText[] = "we love Bob!";SetWindowTextW ( hwnd, szNewText );看出问题了吧,这里用一个Unicode字符串处理函数来处理单字节字符串。第一种解决办法是使用宏定义:HWND hwnd = GetSomeWindowHandle();#ifdef UNICODE wchar_t szNewText[] = L"we love Bob!";#else char szNewText[] = "we love Bob!";#endifSetWindowText ( hwnd, szNewText );要对每一个字符串都做这样的宏定义显然是令人头痛的。所以用TCHAR来解决这个问题:TCHAR的救火角色TCHAR 是一种字符类型,适用于MBCS 和 Unicode二种编码。程序中也不必到处使用宏定义。TCHAR的宏定义如下:#ifdef UNICODE typedef wchar_t TCHAR;#else typedef char TCHAR;#endif所以,TCHAR中在MBCS程序中是char类型,在Unicode中是 wchar_t 类型。对于Unicode字符串,还有个 _T() 宏,用于解决 L 前缀:#ifdef UNICODE #define _T(x) L##x#else #define _T(x) x#endif## 是预处理算子,将二个变量粘贴在一起。不管什么时候都对字符串用 _T 宏处理,这样就可以在Unicode编码中给字符串加上L前缀,如:TCHAR szNewText[] = _T("we love Bob!");SetWindowTextA/W 函数族中还有其它隐藏的宏可以用来代替strxxx() 和 _mbsxxx() 字符串函数。例如,可以用 _tcsrchr 宏取代strrchr(),_mbsrchr(),或 wcsrchr()函数。_tcsrchr 根据编码标记为_MBCS 或 UNICODE,将右式函数做相应的扩展处理。宏定义方法类似于SetWindowText。不止strxxx()函数族中有TCHAR宏定义,其它一些函数中也有。例如,_stprintf (取代sprintf()和swprintf()),和 _tfopen (取代fopen() 和 _wfopen())。MSDN的全部宏定义在"Generic-Text Routine Mappings"栏目下。String 和 TCHAR 类型定义Win32 API 文件中列出的函数名都是通用名(如"SetWindowText"),所有的字符串都按照TCHAR类型处理。(只有XP除外,XP只使用Unicode类型)。下面是MSDN给出的常用类型定义:类型MBCS 编码中的意义Unicode 编码中的意义WCHARwchar_twchar_tLPSTRzero-terminated string of char (char*) zero-terminated string of char (char*) LPCSTRconstant zero-terminated string of char (constchar*) constant zero-terminated string of char (constchar*) LPWSTRzero-terminated Unicode string (wchar_t*) zero-terminated Unicode string (wchar_t*) LPCWSTRconstant zero-terminated Unicode string (const wchar_t*) constant zero-terminated Unicode string (const wchar_t*) TCHARcharwchar_tLPTSTRzero-terminated string of TCHAR (TCHAR*) zero-terminated string of TCHAR (TCHAR*) LPCTSTRconstant zero-terminated string of TCHAR (const TCHAR*) constant zero-terminated string of TCHAR (const TCHAR*) 何时使用TCHAR 和Unicode可能会有疑问:“为什么要用Unicode?我一直用的都是普通字符串。”在三种情况下要用到Unicode:程序只运行于Windows NT。处理的字符串长于MAX_PATH定义的字符数。 程序用于Windows XP中的新接口,那里没有A/W版本之分。 大部分Unicode API不可用于Windows 9x。所以如果程序要在Windows 9x上运行的话,要强制使用MBCS API (微软推出一个可运行于Windows 9x的新库,叫做Microsoft Layer for Unicode。但我没有试用过,无法说明它的好坏)。相反,NT内部全部使用Unicode编码,使用Unicode API可以加速程序运行。每当将字符串处理为MBCS API时,操作系统都会将字符串转换为Unicode并调用相应的Unicode API 函数。对于返回的字符串,操作系统要做同样的转换。尽管这些转换经过了高度优化,模块尽可能地压缩到最小,但毕竟会影响到程序的运行速度。NT允许使用超长文件名(长于MAX_PATH 定义的260),但只限于Unicode API使用。Unicode API的另外一个优点是程序能够自动处理输入的文字语言。用户可以混合输入英文,中文和日文作为文件名。不必使用其它代码来处理,都按照Unicode编码方式处理。最后,作为Windows 9x的结局,微软似乎抛弃了MBCS API。例如,SetWindowTheme() 接口函数的二个参数只支持Unicode编码。使用Unicode编码省却了MBCS与Unicode之间的转换过程。如果程序中还没有使用到Unicode编码,要坚持使用TCHAR和相应的宏。这样不但可以长期保持程序中DBCS编码的安全性,也利于将来扩展使用到Unicode编码。那时只要改变预处理中的设置即可!