Windows 操作系统天然支持 Unicode 字符串。这些字符串经常被用在 UI 组件、文件名等字符相关的地方。因为 Windows 操作系统会涉及到多语言的问题,所以 Unicode 是首选的字符串编码方式。

和 Linux 使用 UTF-8 编码不同,Windows 平台使用 UTF-16 编码方式,每个字符用16位的值表示。UTF-16 字符也被叫做宽字符。Visual Studio C++ 编译器支持内置的宽字符类型 wchar_t ,具体定义在头文件 WinNT.h 中。

typedef wchar_t WCHAR

声明一个宽字符或者一个宽字符串需要将 L 放到文件前面:

wchar_t a = L'a';
wchar_t *str = L"hello";

项目有一些常见的字符串类型:

类型 定义
CHAR char
PSTR or LPSTR char*
PCSTR or LPCSTR const char*
PWSTR or LPWSTR wchar_t*
PCWSTR or LPCWSTR const wchar_t*

1. Unicode 和 ANSI 函数

因为微软提供对 Unicde 的支持,所以它将每个和字符串相关的 API 都提供了两个版本,一种是 ANSI 字符串版本,另一种是 Unicode 字符串版本。例如,下面两个 API 都是设置窗口标题的接口:

  • SetWindowTextA 需要传入 ANSI 字符串。
  • SetWindowTextW 需要传入 Unicode 字符串。

在函数内部,ANSI 版本的接口会将 ANSI 字符串转换为Unicode 字符串,然后再调用 Unicode 版本接口完成操作。

为了方便微软在头文件中还定义了一个 UNICODE 宏来区分不同版本的调用。

#ifdef UNICODE
#define SetWindowText  SetWindowTextW
#else
#define SetWindowText  SetWindowTextA
#endif

使用上面的方法可以避免在代码中区分具体调用的接口是 UNICODE 版本还是 ANSI 版本。相反的,在程序中只需要直接调用 SetWindowText 函数设置标题即可。编译的时候会自动根据 UNICODE 宏定义来决定使用哪个版本。

除非兼容旧的程序或者组件,否则在开发新应用的时候,应该一直使用 Unicode 版本。因为 Windows 操作系统支持多语言,如果使用 ANSI 版本,它将无法支持应用程序的本地化,并且 ANSI 版本的接口效率更低,因为其内部需要进行编码转换。

2. TCHARs

如果你的程序需要同时支持多款操作系统,如:Windows NT、Windows 95、Windows 98 和 Windows Me。这时候你需要明确区分使用的是 ANSI 版本还是 Unicode 版本字符串,为了进一步方面开发,Windows 提供一个宏来完成二者的自动区分。

Unicode ANSI
TCHAR wchar_t char
TEXT("x") L"x" "x"

举个例子:

SetWindowText(TEXT("My Application"));

该语句等价于:

SetWindowTextW(L"My Application"); // Unicode function with wide-character string.

SetWindowTextA("My Application");  // ANSI function.

如今,TEXT 和 TCHAR 宏的用处已经很小了,因为所有的程序都应该使用 Unicode 字符,然而你在一些老的程序中仍然看见它们的身影。

除了上边的问题,在头文件中,微软 C 运行时库中,仍然存在类似的宏定义,例如涉及到字符操作的函数:

#ifdef _UNICODE
#define _tcslen     wcslen
#else
#define _tcslen     strlen
#endif

一些头文件使用 UNICODE 宏,另一些使用 _UNICODE 宏,最好同时定义它们,如果你是用 Visual C++ 创建工程,这些会默认自动进行设置。