VC++ Windows平台字符透明编程大总结
程 化
1. 前言
Windows平台有用Unicode和不用的区分:WinNT到Windows2003一直使用Unicode;WindowsCE也是如此;Win95和Win98就非如此。Windows编程对于字符使用也有各种情况:Windows API的处理方式、MFC的处理方式、VC++的处理方式、COM的处理方式。本文对所有这些方式作了一个总结,期望程序员能够以本文为引子,找到各种情况下处理字符透明编程的方法。
所谓字符透明编程,主要针对Unicode和ANSI字符。本来Unicode是比较简单的一个东西,说起来一个Unicode字符就是一个无符号短整数而已(16位,2个字节),但是,我相信大多数VC++程序员都有这样的困惑:VC++和Win32API中那些用来实现ANSI和Unicode透明编程的,样子长得很像的宏,都在哪儿定义的?它们之间的关系如何?
这就要求我们了解编程平台和操作系统支持ANSI和Unicode透明编程的方法。具体来说,就是要了解VC++的运行库和Win32API是如何解决该问题的。更进一步,我们还应该了解COM解决该问题的方式。最后,由于许多VC++程序员使用MFC框架进行编程,了解MFC框架处理该问题的方法也有必要。
本文内容在《ATL技术内幕》的第二章也有比较详细的讲述,尤其是关于COM的内容。但是,该书没有讲VC++相关内容;也没有将内容整理得更清晰一些(尤其是没有给出各种情况下的表格以供查找)。所以,本文将重点放在VC++和Windows针对字符透明编程采用方法的归纳和比较上。
本文可以作为一个出发点,有了本文介绍的基础之后,想要更多地了解BSTR的细节,可以看《ATL技术内幕》;想要更多地了解针对字符透明编程问题,可以看其他的相关资料。
2. VC++对字符透明编程
首先要说的是,对宽字符的支持其实是ANSI C标准的一部分,用以支持多字节表示一个字符。宽字符和Unicode并不是一回事,Unicode只是宽字符能支持的一种编码方式。但是,由于我们现在主要考虑Unicode,不妨把这两种东西当作同义。
2.1. 宽字符的定义
在ANSI中,一个字符(char)的长度为一个字节(Byte)。使用Unicode时,一个字符应该占据一个字(Word),VC++在wchar.h头文件中定义了最基本的宽字符类型wchar_t:
typedef unsigned short wchar_t;
从这里我们可以清楚地看到,所谓的宽字符就是无符号短整数。
2.2. 常量宽字符串
对C++程序员而言,构造字符串常量是一项经常性的工作。那么,如何构造宽字符字符串常量呢?很简单,只要在字符串常量前加上一个大写的L就可以了,比如:
L“Hello, world!”
这个L非常重要,只有带上它,编译器才知道你要将字符串存成每个字符1个字。还要注意,在L和字符串之间不能有空格。
2.3. 宽字符串库函数
为了操作宽字符串,VC++专门定义了一套函数,比如,求宽字符串长度的函数是
size_t __cdel wchlen ( const wchar_t * )
为什么要专门定义这些函数呢?最根本的原因是,ANSI下的字符串都是以“/0”来标识字符串尾的,许多字符串函数的正确操作均以此为基础进行。而我们知道,在宽字符的情况下,一个字符在内存中要占据一个字的空间,这就会使操作ANSI字符的字符串函数无法正确操作。以“Hello”字符串为例,在宽字符下,它的五个字符是:
0x0048 0x0065 0x006c 0x006c 0x006f
在内存中,实际的排列是:
48 00 65 00 6c 00 6c 00 6f 00
于是,ANSI字符串函数,如 strlen,在碰到第一个48后的00时,就会认为字符串到尾了,用 strlen 对宽字符串求长度的结果就永远会是1!
2.4. 用宏实现对ANSI和Unicode的透明编程
看到这儿,想必程序员都会感到沮丧:“完了,两套字符串函数!”不用说,针对ANSI字符和Unicode字符维护两套代码是令人讨厌的事情。就算是自己在一套代码中写一些预编译语句执行条件编译,也是非常麻烦的事,因为要用字符串的地方实在是太多了。为了减轻大家的编程负担,VC++定义了一系列的宏,帮助实现对ANSI和Unicode的透明编程。从上面的讨论我们可以看到,要做的工作是两个:一,透明定义字符和常量字符串;二,透明调用字符串函数。下面就分别讲用于这两个方面的宏。
2.4.1. 透明定义字符和常量字符串
该工作主要是由 tchar.h 头文件中定义的若干宏完成的,根据“_UNICODE”(注意,有下划线)定义与否,这些宏展开为ANSI或Unicode字符(字符串)。有兴趣者可以去查看头文件,这里我做了如下归纳:
字符宏: