从char/wchar_t到TCHAR

一.ANSI和UNICODE

1.为什么要使用UNICODE? 

(1) 可以很容易地在不同语言之间进行数据交换。

(2) 使你能够分配支持所有语言的单个二进制.exe文件或DLL文件。

(3) 提高应用程序的运行效率。

Windows 2000是使用UNICODE从头进行开发的,如果调用任何一个Windows函数并给它传递一个ANSI字符串,那么系统首先要将字符串转换成UNICODE,然后将UNICODE字符串传递给操作系统。如果希望函数返回ANSI字符串,系统就会首先将UNICODE字符串转换成ANSI字符串,然后将结果返回给你的应用程序。进行这些字符串的转换需要占用系统的时间和内存。通过从头开始用UNICODE来开发应用程序,就能够使你的应用程序更加有效地运行。

Windows 98只支持ANSI,只能为开发ANSI应用程序。 Windows CE 就是使用UNICODE的操作系统,完全不支持ANSI版函数。

Microsoft将COM从Win16转换成Win32时,所有COM接口方法都只能接受UNICODE字符串。

2.ANSI字符和UNICODE字符

ANSI字符类型为CHAR,指向字符串的指针PSTR(LPSTR),指向一个常数字符串的指针PCSTR(LPCSTR);对应的Windows定义的UNICODE字符类型为WCHAR(typedef WCHAR wchar_t),指向UNICODE字符串的指针PWSTR ,指向一个常数UNICODE字符串的指针PCWSTR 。

ANSI "ANSI";

UNICODE L"UNICODE";

ANSI/UNICODE_T("string") or _TEXT("string");

3.ANSI字符和UNICODE字符串的操作

双字节(DBCS)字符集中,字符串的每个字符可以包含一个或两个字节。如果只是调用strlen()函数,那么你就无法知道字符串到底有多少个字符,它只能告诉你到达结尾的0之前有多少个字节。

标准c中的strcpy,strchr,strcat等只能用于ANSI字符串,不能正确处理UNICODE字符串,因此也提供了一组补充函数,功能等价,但用于UNICODE码。我们来看看string .h字符串头文件中是怎样处理char*和wchar_t*两个字符串版本的:

// …/Microsoft Visual Studio 8/VC/include/string.h

char *strcat(char*,constchar*);

wchar_t *wcschr(wchar_t*,const wchar_t*);

类似的还有strchr/wcschr,strcmp/wcscmp,strlen/wcslen etc. ANSI 操作函数以str开头 strcpy ,UNICODE 操作函数以wcs开头 wcscpy。

MBCS 操作函数以_mbs开头 _mbscpy

ANSI/UNICODE 操作函数以_tcs开头 _tcscpy(C运行期库)

ANSI/UNICODE 操作函数以lstr开头 lstrcpy(Windows API-KERNEL32.DLL)

所有新的和未过时的函数在Windows2000中都同时拥有ANSI和UNICODE两个版本。ANSI版本函数结尾以A表示;UNICODE版本函数结尾以W表示。操作系统根据是否定义了UNICODE宏来调用合适版本的API。

    Windows XP默认字符集为gbk(包含gb2312)。wchar_t wcs[] = {0xCD, 0xCD, 0xEE, 0xFE, 0x00};表示汉字“屯铪”。参考在线汉字编码查询

 

二.ANSI/UNICODE通用字符/字符串类型TCHAR/LPTSTR/LPCTSTR


二.ANSI/UNICODE通用字符/字符串类型TCHAR/LPTSTR/LPCTSTR

Neutral ANSI/UNICODE types

1.通用字符型TCHAR

TCHAR

ifdef UNICODE it is wchar_t(WCHAR)for Unicode platforms;

else it is char for ANSI and DBCS platforms.

2.通用字符串指针LPTSTR

LPTSTR

ifdef UNICODE it is LPWSTR(*wchar_t) for Unicode platforms;

else it is LPSTR (*char) for ANSI and DBCS platforms.

3.通用通用常数字符串指针LPCTSTR

LPCTSTR

ifdef UNICODE it is LPCWSTR(*const wchar_t) for Unicode platforms;

else it is LPCSTR (*const char)for ANSI and DBCS platforms.

typedef LPWSTR LP;

#define __TEXT(quote) L##quote // r_winnt



<1>_UNICODE宏用于C运行期头文件,UNICODE宏则用于Windows头文件,当编译代码模块时,通常必须同时定义这两个宏。

<2>如果定义了_UNICODE,若要生成一个Unicode字符串,字符串前要加L宏,用于告诉编译器该字符串应该作为Unicode字符串来编译处理。但是这样又有个问题就是如果没有定义_UNICODE则编译出错。为了解决这个问题我们必须用到_TEXT宏,这个宏也在TChar.h中做了定义。使用该宏后,无论源文件有没有定义_UNICODE都不会出现编译错误。

<3>Unicode与ANSI字符串的转换:Windows函数MultiByteToWideChar函数用于将多字节字符串转换成宽字符串,函数WideCharToMultiByte将宽字符串转换成等价

的多字节字符串。


有的人爱用strcpy等标准ANSI函数,有的人爱用_tXXXX函数,有必要把来龙去脉搞清楚。为了搞清这些函数,就必须理请几种字符类型的写法。char就不用说了,先说一些wchar_t。wchar_t是Unicode字符的数据类型,它实际定义在<string.h>里:

typedef unsigned short wchar_t;

不能使用类似strcpy这样的ANSI C字符串函数来处理wchar_t字符串,必须使用wcs前缀的函数,例如wcscpy。为了让编译器识别Unicode字符串,必须以在前面加一个“L”,例如:

wchar_t *szTest=L"This is a Unicode string."

wchar_t是Unicode字符的数据类型,它实际定义在<string.h>里:

typedef unsigned short wchar_t;

下面在看看TCHAR。如果你希望同时为ANSI和Unicode编译的源代码,那就要include TChar.h。TCHAR是定义在其中的一个宏,它视你是否定义了_UNICODE宏而定义成char或者wchar_t。如果你使用了TCHAR,那么就不应该使用ANSI的strXXX函数或者Unicode的wcsXXX函数了,而必须使用TChar.h中定义的_tcsXXX函数。另外,为了解决刚才提到带“L”的问题,TChar.h中定义了一个宏:“_TEXT”。

以strcpy函数为例子,总结一下:

.如果你想使用ANSI字符串,那么请使用这一套写法:

char szString[100];

strcpy(szString,"test");

.如果你想使用Unicode字符串,那么请使用这一套:

wchar_t szString[100];

wcscpyszString,L"test");

.如果你想通过定义_UNICODE宏,而编译ANSI或者Unicode字符串代码:

TCHAR szString[100];

_tcscpy(szString,_TEXT("test"));

2, ANSI与Unicode

Unicode称为宽字符型字串,COM里使用的都是Unicode字符串。

将ANSI转换到Unicode

(1)通过L这个宏来实现,例如: CLSIDFromProgID( L"MAPI.Folder",&clsid);

(2)通过MultiByteToWideChar函数实现转换,例如:

char *szProgID = "MAPI.Folder";

WCHAR szWideProgID[128];

CLSID clsid;

long lLen = MultiByteToWideChar(CP_ACP,0,szProgID,strlen(szProgID),szWideProgID,sizeof(szWideProgID));

szWideProgID[lLen] = '

(3)通过A2W宏来实现,例如:

USES_CONVERSION;

CLSIDFromProgID( A2W(szProgID),&clsid);

将Unicode转换到ANSI

(1)使用WideCharToMultiByte,例如:

// 假设已经有了一个Unicode 串 wszSomeString...

char szANSIString [MAX_PATH];

WideCharToMultiByte ( CP_ACP, WC_COMPOSITECHECK, wszSomeString, -1, szANSIString, sizeof(szANSIString), NULL, NULL );

(2)使用W2A宏来实现,例如:

USES_CONVERSION;

pTemp=W2A(wszSomeString);


<1>_UNICODE宏用于C运行期头文件(如下_tprintf示例),UNICODE宏则用于Windows头文件(如上winnt.h中),当编译代码模块时,通常必须同时定义这两个宏或都不定义。

winnt.h中定义了TEXT(__TEXT)宏,用于做UNICODE环境的自适应。对于非UNICODE环境,__TEXT宏不对参数quote做任何处理;对于UNICODE环境,__TEXT宏在字符串quote前添加L宏,以表示宽字符串(wchar_t[])。

// #include <winnt.h>

typedef CHAR char;

typedef WCHAR wchar_t;

 

#ifdef  UNICODE                    // r_winnt

typedef WCHAR TCHAR, *PTCHAR;

typedef WCHAR *LPWSTR;

typedef LPWSTR LPTSTR;

typedef CONST WCHAR *LPCWSTR;

typedef LPCWSTR LPCTSTR;

#define __TEXT(quote)L##quote // r_winnt

#else   /* UNICODE */             // r_winnt

typedef CHAR TCHAR, *PTCHAR;

typedef CHAR *LPSTR;

typedef LPSTR LPTSTR;

typedef CONST CHAR *LPCSTR,

typedef LPCSTR LPCTSTR;

#define __TEXT(quote)quote

#endif /* UNICODE */             // r_winnt

#define TEXT(quote)__TEXT(quote)  // r_winnt

Microsoft的VC提供了tchar.h头文件用于字符集的自适应(To be used for compatibility between single-byte, multi-byte and Unicode text models)。对常用的函数做了T定义,例如_tprintf:

// #include <tchar.h>

#ifdef  _UNICODE

#define __T(x)      L## x

typedef wchar_t     TCHAR;

#define _tprintf    wprintf

#else   /* ndef _UNICODE */

#define __T(x)      x

#define _tprintf    printf

#endif /* _UNICODE */

#define _T(x)      __T(x)

#define _TEXT(x)   __T(x)

<2>如果定义了_UNICODE,若要生成一个UNICODE字符串,字符串前要加L宏,用于告诉编译器该字符串应该作为UNICODE字符串来编译处理。但是这样又有个问题就是如果没有定义_UNICODE则编译出错。为了解决这个问题我们必须用到_T(_TEXT)宏,同winnt.h中定义的TEXT(__TEXT)宏。使用该宏后,无论源文件有没有定义_UNICODE都不会出现编译错误。

<3>ANSI与UNICODE的一些互操作QA

Q1:如何判断一个文本文件是ANSI还是Unicode?

A1:如果文本文件的开头两个字节是0xFF和0xFE,那么就是Unicode,否则是ANSI。

Q2:如何判断一段字符串是ANSI还是Unicode?

A2:用IsTextUnicode进行判断。IsTextUnicode使用一系列统计方法和定性方法,以便猜测缓存的内容。由于这不是一种确切的科学方法,因此 IsTextUnicode有可能返回不正确的结果。

Q3:如何在Unicode与ANSI之间转换字符串?

A3:Windows函数MultiByteToWideChar/mbstowcs函数用于将多字节字符串转换成宽字符串,函数WideCharToMultiByte/wcstombs将宽字符串转换成等价的多字节字符串。

 

三.ANSI/UNICODE字符串通用函数lstrcpy/lstrcmp/lstrcat/lstrlen

ntdll.dll中实现了许多CRT基本函数:

strlen/wcslen、strcpy/wcscpy、strncpy/wcsncpy、strcat/wcscat、strncat/wcsncat、strcmp/wcscmp、strncmp/wcsncmp、strchr/wcschr、strrchr/wcsrchr、strstr/wcsstr、sprintf/swprintf、strtol/wcstol、strtoul/wcstoul。

kernel32.dll(依赖ntdll.dll)中实现了:

lstrlen(lstrlenA/lstrlenW)、lstrcpy(lstrcpyA/lstrcpyW)、lstrcpyn(lstrcpynA/lstrcpynW)、lstrcat(lstrcatA/lstrcatW)、lstrcmp(lstrcmpA/lstrcmpW)、lstrcmpi(lstrcmpiA/lstrcmpiW)。

// …/Microsoft Visual Studio 8/VC/PlatformSDK/Include/Winbase.h(已包含在windows.h中)

WINBASEAPI

LPSTR

WINAPI

lstrcpyA(

    __out LPSTR lpString1,

    __in  LPCSTR lpString2

    );

WINBASEAPI

LPWSTR

WINAPI

lstrcpyW(

    __out LPWSTR lpString1,

    __in  LPCWSTR lpString2

    );

#ifdef UNICODE

#define lstrcpy  lstrcpyW

#else

#define lstrcpy  lstrcpyA

#endif // !UNICODE

 

四.使用shlwapi头文件中定义的函数StrCat/StrCmp/StrCpy

Shlwapi.dll(依赖kernel32.dll)是UNC和URL地址动态链接库文件,用于注册键值和色彩设置。因为操作系统字符串函数常常被大型应用程序比如操作系统的外壳进程Explorer.exe所使用。由于这些函数使用得很多,因此,在应用程序运行时,它们可能已经被装入RAM。这将有助于稍稍提高应用程序的运行性能。

注意:使用StrCat、StrCmp、StrCpy etc时要

#include <Shlwapi.h>

#pragma comment(lib, "Shlwapi.lib")

// …/Microsoft Visual Studio 8/VC/PlatformSDK/Include/Shlwapi.h

LWSTDAPI_(LPWSTR)  StrCatW(LPWSTR psz1, LPCWSTR psz2);

LWSTDAPI_(int)     StrCmpW(LPCWSTRpsz1, LPCWSTR psz2);

LWSTDAPI_(LPWSTR)  StrCpyW(LPWSTR psz1, LPCWSTR psz2);

#ifdef UNICODE

#define StrCat      StrCatW

#define StrCmp      StrCmpW

#define StrCpy      StrCpyW

#else

#define StrCat      lstrcatA

#define StrCmp      lstrcmpA

#define StrCpy     lstrcpyA

由上可以看出StrCpy调用的是lstrcpy,StrCat调用的是lstrcat,StrCmp调用的是lstrcmp。

 

五.MFC动态字符串类CString

// …/Microsoft Visual Studio 8/VC/atlmfc/include/afx.h

一个CString对象由可变长度的一队字符组成。CString使用类似于Basic的语法提供函数和操作符。连接和比较操作符以及简化的内存管理使CString对象比普通字符串数组容易使用。

CString是基于TCHAR数据类型的对象。如果在你的程序中定义了符号_UNICODE,则TCHAR被定义为类型wchar_t,即16位字符类型;否则,TCHAR被定义为char,即8位字符类型。在UNICODE方式下,CString对象由16位字符组成。非UNICODE方式下,CString对象由8位字符组成。 而VS2005默认TCHAR是wchar而不是char.

当不使用_UNICODE时,CString是多字节字符集(MBCS,也被认为是双字节字符集,DBCS)。注意,对于MBCS字符串,CString仍然基于8位字符来计算,返回,以及处理字符串,并且你的应用程序必须自己解释MBCS的开始和结束字节。

CString 提供 operator LPCTSTR 来在 CString 和 LPCTSTR 之间进行转换。

有关CString的操作请参考MSDN MFC类库。参考源码文件AFX.H、AFX.INL、STRCORE.CPP和STREX.CPP。

 

六.更安全的C语言字符串处理函数 Strsafe.h

// …/Microsoft Visual Studio 8/VC/PlatformSDK/Include/strsafe.h

注意:使用StringCchCopy/StringCchPrintf时要#include<Shlwapi.h>.

STRSAFEAPI是为了解决现有的 C 语言运行时函数的代码太容易产生的“内存溢出”问题。当我们引用 strsafe 系列函数时,原有的 C 语言字符串处理函数都将被自动进行 #undef 处理。调试过程中的警告或出错信息将会告诉我们哪些函数哪些不安全,哪些已经被相应的 strsafe 系列函数取代了。 

1.不赞成使用不安全的函数,以避免产生编译错误

2.如果你不要安全处理,你可以在包含strsafe.h头文件之前,

#define STRSAFE_NO_DEPRECATE

#ifdef DEPRECATE_SUPPORTED

// First all the names that are a/w variants (or shouldn't be #defined by now anyway).

#pragma deprecated(strcpy)

#pragma deprecated(wcscpy)

#pragma deprecated(lstrcpy)

#pragma deprecated(StrCpy)

类似的Strcat/wcscat/lstrcat/StrCat,sprintf/wsprintf

以下是D3D中预编译头文件dxstdafx.h

#pragma warning( disable : 4996 ) // 将报警置为无效

#include <strsafe.h>

#pragma warning(default : 4996 ) // 将报警置为默认

有关#pragma warning请参考《关于#pragma warning

以下是D3D从VS2003移植到VS2005时遇到的安全警告:

warning C4996: 'wcscpy' was declared deprecated

see declaration of 'wcscpy'

Message: 'This function or variable may be unsafe.

Consider using wcscpy_s instead. To disable deprecation, use _CRT_SECURE_NO_DEPRECATE. See online help for details.'

warning C4995: 'lstrcpy': name was marked as #pragma deprecated

warning C4995: 'wsprintf': name was marked as #pragma deprecated

推荐使用新的安全可靠的TRSAFEAPI:

STRSAFEAPI

StringCchCopyA(

__out_ecount(cchDest)STRSAFE_LPSTR pszDest,

__in size_tcchDest,

__in STRSAFE_LPCSTRpszSrc);

STRSAFEAPI

StringCchCopyW(

              __out_ecount(cchDest)STRSAFE_LPWSTR pszDest,

              __in size_t cchDest,

              __in STRSAFE_LPCWSTR pszSrc);

#ifdef UNICODE

#define StringCchCopy  StringCchCopyW (WWideUnicode)

#else

#define StringCchCopy  StringCchCopyA (AANSI)

#endif // !UNICODE

#undef strcpy

#define strcpy      strcpy_instead_use_StringCbCopyA_or_StringCchCopyA;

#undef wcscpy

#define wcscpy      wcscpy_instead_use_StringCbCopyW_or_StringCchCopyW;

#undef wsprintf

#define wsprintf    wsprintf_instead_use_StringCbPrintf_or_StringCchPrintf;

// Then all the windows.h names - we need to undef and redef based on UNICODE setting

#undef lstrcpy // 取消已定义的宏

#pragma deprecated(lstrcpy) // 安全警告

#ifdef UNICODE // 使用UNICODE编程

#define lstrcpy    lstrcpyW // 重定义

#else

#define lstrcpy    lstrcpyA // 重定义

#endif

类似的有对lstrcat/wsprintf/wvsprintf的#undef,#pragma deprecated,#define。

推荐使用新的安全可靠的TRSAFEAPI:

#undef lstrcpy

#define lstrcpy    lstrcpy_instead_use_StringCbCopy_or_StringCchCopy;

// Then the shlwapi names - they key off UNICODE also.

#undef  StrCpy

#pragma deprecated(StrCpy)

#ifdef UNICODE

#define StrCpy  StrCpyW

#else

#define StrCpy  lstrcpyA

#endif

类似的有#undef StrCpyA /StrCpy /StrCatA /StrCat /StrNCat /StrCatN以及对StrCpy/StrCat/StrNCat的#undef,#pragma deprecated,#define。

推荐使用新的安全可靠的TRSAFEAPI:

#undef StrCpy

#define StrCpy     StrCpy_instead_use_StringCbCopy_or_StringCchCopy;

// Then all the CRT names - we need to undef/redef based on _UNICODE value.

 

七.VC编译UNICODE版本

VC6.0支持Unicode,但在缺省安装情况下,没有把相关的部件安装上去,所以第一步要安装相关的组件。从安装向导中选择自定义,在选择要安装的组件清单时,把vc里面带unicode的子项全部选中安装即可。

如果采用默认安装,后期需要编译发布Unicode版本,在“Project SettingàC/C++CategoryPreprocessoràPreprocessor definitions”处使用_UNICODE UNICODE替换掉_MBCS

程序运行可能会提示找不到MFC42U.DLL、MFC42UD.DLL、MFCO42UD.DLL(U:Unicode,D:Debug,O:Ole)等,这是由于VC++ 安装的时候没有选择UNICODE支持的缘故,导致没有安装debug版本的MFC42*.DLL。如果只是运行需要的话,可以从VC安装盘(Visual Studio 6安装包目录/VC98/REDIST/MFC42U.DLL;Visual Studio 6安装包目录/VC98/DEBUG/MFC*.DLL)或其它机器copy过来,如果编译使用,建议还是重装来得实在。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值