在正式学习Windows编程之前,我也曾编写过一些MFC的小程序,当时对那些满屏幕的LPTSTR之类的数据类型非常的不理解,只知道C语言中的Char类型和C++的string两个表示字符串的类型,那为什么我们会看到类似LPTSTR之类的类型呢,本节就来解答这个疑问。
1.为什么要使用Unicode
1.1 双字节字符集(DBCS)
我们知道在电脑中英语和某些西欧语言使用的是使用ASCII码进行编码表示的,比如说ASCII码值位65时表现的是大写字母'A',然而如果只使用这种编码方式,那么像中文、日文之类的语言将无法在计算机中显示。对于程序员来说就涉及到程序中其他语言的正确显示。当一个字节无法表示出所需要的字符时(单字节最多能提供256个字符),自然的产生了双字节字符集(DBCS)。
在双字节字符集中,字符串中的每个字符可以包含一个字节或包含两个字节。因此在一个字符串中,判断一个字节表示一个字符还是两个字节表示一个字符,给程序员带来了很大的困难。例如对DBCS字符串进行操作可以使用以下函数:
1.2 Unicode:宽字节字符集
在Unicode中每个字符都由两个字节表示,单独的一个字节不具有意义。因此,对Unicode字符串的操作可以直接使用指针的移动得到,不在需要上述繁琐的函数。从Windows2000开始Windows操作系统可以很好的支持Unicode。并且如果调用任何一个Windows函数并给它传递一个ANSI字符串,系统首先要将字符串转换成Unicode,然后将Unicode字符串传递给操作系统。而这一步操作当然是要消耗内存和时间的了。
2.如何使用Unicode
实际上在进行Windows编程的时候完全可以编写出对Unicode和ANSI字符串兼容的程序。这只需要定义两个宏(UNICODE和_UNICODE),就可以修改然后重新编译源文件。
在介绍C运行库和Windows对Unicode的支持之前,先介绍一下这两个库的概念:C运行库头文件是VC自带的,也是一直以来c语言一脉相承延用下来的,包含一些常用函数及算法,头文件一般在C:\Program Files\Microsoft Visual Studio 9.0\VC\include下面;Windows库文件及头文件是Windows自己特有的东西,包含了和Windows相关的一些操作,是Windows SDK的一部分,头文件一般在C:\Program Files\Microsoft SDKs\Windows\v6.0A\Include
2.1 C运行期库对Unicode的支持
标准的C头文件String.h已经做了修改,定义了一个名为wchar_t的数据类型,它是一个Unicode字符的数据类型:
typedef unsigned short wchar_t;
相应的C标准函数库也给出了一系列与标准ANSI C字符串函数对应等价的Unicode函数,见下图:
所有的Unicode函数均以wcs开头,若要调用Unicode函数,只需用前缀wcs来取代ANSI字符串函数的前缀str即可。
那么如果要同时支持ANSI和Unicode怎么办,如果在程序了固定的写入函数和变量的类型,那如果程序移植的时候不是要一条一条的更改源文件吗?C语言给我们提供了很好的解决办法。若要能够支持两种字符集,必须包含TChar.h文件,而不是String.h文件。
TChar.h文件的唯一作用就是帮助创建ANSI/Unicode通用源代码文件。它定义了一组宏,实现在ANSI和Unicode切换的功能。如果在编译源代码文件时定义了_UNICODE,这些宏就会应用wcs这组函数。如果没有定义_UNICODE,那么这组宏将引用str这组宏。对于一个字符串可以用_TEXT()宏包含,得到在ANSI和Unicode切换的效果。如:
TCHAR *szError=_TEXT("error");
对上式,如果没有定义_UNICODE宏,则生成的是ANSI字符串,若定义了则生成的位Unicode字符串。
2.2 Windows定义的Unicode数据类型
Windows头文件包含以下几种Unicode数据类型:
与C运行库类似,Windows也定义了通用的数据类型PTSTR和PCTSTR,这些数据类型既支持ANSI字符串,也可以指Unicode字符串,这取决于当编译程序模块时是否定义了UNICODE宏,注意与C运行库时的_UNICODE宏进行区分!
Windows中很多函数也提供了支持两种字符集的函数如CreateWindowExW和CreateWindowExA,为了便于在两种字符集之间切换,可以直接使用CreateWindowEx函数,而运行时究竟使用的是ANSI还是Unicode由是否定义了UNICODE决定。