如果你进入本文时,若对与 多字节 Unicode ASCII 等这些概念上不清楚的话,请转到如下文章学习: API入门系列之一 -那‘烦人’的Windows数据类型 http://blog.csdn.net/beyondcode/article/details/4015769 API入门系列之二 -Unicode还是ASCII http://blog.csdn.net/beyondcode/article/details/4018731 API入门系列之三 -那迷惑人的Windows字符和字符指针类型 http://blog.csdn.net/beyondcode/article/details/4021085

在 Windows 下编程,说实话要注意的事情比 Linux 多很多,要考虑字符串处理函数是否安全、要考虑 Unicode 和 多字节编码等问题,如何能写出一份代码,既在 Unicode 字符集下可以编译也可以在多字节字符集下编译通过是要了解挺多信息的,除了上面给大家推荐的 “API入门系列只二” 的文章外,本文也详细记录一下能适应 Unicode 和多字节字符集的宏,以及不清楚使用什么宏的情况下要如何查询。我们先来看一下如下例子:

1
2
3
4
5
6
7
8
9
10
11
12
#include "stdafx.h"
#include "Win32Project.h"

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
wchar_t wcMessage[] = L"Hello World";
MessageBox(NULL, wcMessage, NULL, MB_OK);
return 0;
}

以上例子在 Unicode 字符集下可以顺利编译通过,但如果将项目属性修改为“多字节字符集”时,你会发现编译器报出如下错误:

1
error C2664: “int MessageBoxA(HWND,LPCSTR,LPCSTR,UINT)”: 无法将参数 2 从“wchar_t [12]”转换为“LPCSTR”

原因我想大家清楚了,是由于切换了字符集以后,要传递的参数应该由原来的 wchar_t 更换为 char。如果项目比较庞大,又不得不修改字符集以适应需求时,这种情况无疑是让人抓狂的,必须要通过修改源代码的方式解决以上问题。所以我们就有必要知道,编写什么样的代码使其可以随意更换字符集而不影响项目的顺利编译。 要做到在任何字符集下都可以顺利编译通过,我们的代码就要跟随字符集的变化而变化,在 Unicode 下使用 wchar_t,在多字节下使用 char。通过 Windows 提供给我们的宏 TCHAR 就可以实现我们这个需求。再看如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
#include "stdafx.h"
#include "Win32Project.h"

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
TCHAR szMessage[] = _T("Hello World");
MessageBox(NULL, szMessage, NULL, MB_OK);
return 0;
}

我们将 wchar_t 换成了 TCHAR,将字符串 L”Hello World” 换成了 _T(“Hello World”)。其实 TCHAR 和 _T() 都是宏,他们的定义如下(参考上面发的几篇文章连接):

1
2
3
4
#ifdef  UNICODE
typedef WCHAR TCHAR;
#else
typedef char TCHAR

如果是 UNICODE 字符集,则 TCHAR 就是 WCHAR(wchar_t),如果不是 UNICODE 字符集,则 TCHAR 就是 char。这样我们的代码在编译的时候就可以适应不同的字符集了,_T() 也是同样的道理。 既然字符串已经适应了不同字符集的情况,那么你也一定要考虑,一些处理字符串的函数也必须要适应这种情况。比如 strcpy、strcat、strlen 等等,他们又要如何处理呢?你肯定不能写 strlen(szMessage) 这样的代码,因为如果 szMessage 变量是一个 wchat_t,那一定会报错的。 2016-07-23_195008 strlen 只能处理 char*。解决这种问题同样也是宏,那这些宏我们到哪里去找呢?你可以在 VisualStudio IDE 中,敲上 strlen 函数。然后鼠标点击该函数,再按下 F1 键,会跳转到 MSDN。在 MSDN 中,你就可以看到非常详细的介绍了。 2016-07-23_195403 图中 TCHAR.H 的版本就是我们需要的版本,我们可以看到有 TCHAR.H 下面有三个函数,我们只关心第一个就可以,后面两个不在本文的介绍范围内,如果你需要了解可以看看页面中的介绍。 所谓 TCHAR.H 版本,就是可以适应 Unicode 和多字节字符集的宏,使用他们会根据你设置的项目属性自动调用不同的函数,如果你设置的是 Unicode 字符集,则该函数为 wstrlen(),如果你设置的是多字节字符集,则该函数就是 strlen()。那么修改后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "stdafx.h"
#include "Win32Project.h"

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
TCHAR szMessage[] = _T("Hello World");
MessageBox(NULL, szMessage, NULL, MB_OK);

size_t nLength = _tcslen(szMessage);
return 0;
}

以前的 strlen 被我们换成了 _tcslen(),这样无论你怎么切换字符集,我们的代码都可以顺利的编译通过。同样其他的字符串处理函数如果你不知道怎么用,也可以通过这种方式去查找和使用。这样你的程序会非常健壮。不会因为切换个字符集就不能编译通过了。