WINDOWS核心编程--第二章UNICODE

来源:互联网 发布:淘宝ifashion是什么 编辑:程序博客网 时间:2024/05/01 19:28
下面让我们进一步明确一下“M i c r o s o f t公司对U n i c o d e支持的情况”:
• Windows 2000既支持U n i c o d e,也支持A N S I,因此可以为任意一种开发应用程序。
• Windows 98只支持A N S I,只能为A N S I开发应用程序。
• Windows CE只支持U n i c o d e,只能为U n i c o d e开发应用程序。

如何编写U n i c o d e源代码
M i c r o s o f t公司为U n i c o d e设计了Windows API,这样,可以尽量减少对你的代码的影响。实
际上,你可以编写单个源代码文件,以便使用或者不使用U n i c o d e来对它进行编译。只需要定
义两个宏(U N I C O D E和_ U N I C O D E),就可以修改然后重新编译该源文件。

为了利用U n i c o d e字符串,定义了一些数据类型。标准的C头文件S t r i n g . h已经作了修改,
以便定义一个名字为w c h a r _ t的数据类型,它是一个U n i c o d e字符的数据类型:
typedef unsigned char  wchar_t;
标准的C运行期字符串函数,如
s t r c p y、s t r c h r和s t r c a t等,只能对A N S I字符串进行操作,不能正确地处理U n i c o d e字符串。因此,
ANSI C也拥有一组补充函数。,所有的U n i c o d e函数均以w c s开头,w c s是宽字符串的英文缩写。若要调用U n i c o d e函数,只需用前缀w c s来取代A N S I字符串函数的前缀s t r即可

对于包含了对s t r函数或w c s函数进行显式调用的代码来说,无法非常容易地同时为A N S I和
U n i c o d e对这些代码进行编译。本章前面说过,可以创建同时为A N S I和U n i c o d e进行编译的单
个源代码文件。若要建立双重功能,必须包含T C h a r. h文件,而不是包含S t r i n g . h文件。

T C h a r. h文件的唯一作用是帮助创建A N S I / U n i c o d e通用源代码文件。它包含你应该用在源
代码中的一组宏,而不应该直接调用s t r函数或者w c s函数。如果在编译源代码文件时定义了
_ U N I C O D E,这些宏就会引用w c s这组函数。如果没有定义_ U N I C O D E,那么这些宏将引用s t r
这组宏。
例如,在T C h a r. h中有一个宏称为_ t c s c p y。如果在包含该头文件时没有定义_ U N I C O D E ,那
么_ t c s c p y就会扩展为A N S I的s t r c p y函数。但是如果定义了_UNICODE, _tcscpy将扩展为U n i c o d e
的w c s c p y函数。拥有字符串参数的所有C运行期函数都在T C h a r. h文件中定义了一个通用宏。如
果使用通用宏,而不是A N S I / U n i c o d e的特定函数名,就能够顺利地创建可以为A N S I或U n i c o d e
进行编译的源代码。

按照默认设置, M i c r o s o f t公司的C + +编译器能够编译所有的字符串,就像它们是A N S I字符串,而不是U n i c o d e字符串。因此,如果没有定义_ U N I C O D E,该编译器将能正确地编译这一行代码:TCHAR *szError="Error"。但是,如果定义了_ U N I C O D E,就会产生一个错误。若要生成一个U n i c o d e字符串而不是A N S I字符串,必须将该代码行改写为下面的样子:TCHAR *szError=L"Error";字符串(literal string)前面的大写字母L,用于告诉编译器该字符串应该作为U n i c o d e字符串来编译。当编译器将字符串置于程序的数据部分中时,它在每个字符之间分散插入零字节。这种变更带来的问题是,现在只有当定义了_ U N I C O D E时,程序才能成功地进行编译。我们需要另一个宏,以便有选择地在字符串的前面加上大写字母L。这项工作由_ T E X T宏来完成,_ T E X T宏也在T C h a r. h文件中做了定义。使用该宏_TEXT("Error"),可以改写上面这行代码,这样,无论是否定义了_ U N I C O D E宏,它都能够正确地进行编译.

Wi n d o w s定义的U n i c o d e数据类型
Wi n d o w s头文件也定义了A N S I / U n i c o d e通用数据类型P T S T R和P C T S T R。这些数据类型既可以指A N S I字符串,也可以指U n i c o d e字符串,这取决于当编译程序模块时是否定义了U N I C O D E宏。请注意,这里的U N I C O D E宏没有前置的下划线。_ U N I C O D E宏用于C运行期头文件,而U N I C O D E宏则用于Wi n d o w s头文件。当编译源代码模块时,通常必须同时定义这两个宏。

C r e a t e Wi n d o w E x W是接受U n i c o d e字符串的函数版本。函数名结尾处的大写字母W是英文w i d e
(宽)的缩写。每个U n i c o d e字符的长度是1 6位,因此,它们常常称为宽字符。C r e a t e Wi n d o w E x A
的结尾处的大写字母A表示该函数可以接受A N S I字符串。但是,在我们的代码中,通常只包含了对C r e a t e Wi n d o w E x的调用,而不是直接调用C r e a t e Wi n d o w E x W或者C r e a t e Wi n d o w E x A。在Wi n U s e r. h文件中,C r e a t e Wi n d o w E x实际上是定义为一个宏.

建议最好使用操作系统函数,而不要使用C运行期字符串函数。这将有助于稍稍提高你的应用程序的运行性能,因为操作系统字符串函数常常被大型应用程序比如操作系统的外壳进程E x p l o r e r. e x e所使用。由于这些函数使用得很多,因此,在你的应用程序运行时,它们可能已经被装入R A M。在经典的操作系统函数样式中,操作系统字符串函数名既包含大写字母,也包含小写字母,它的形式类似这个样子: S t r C a t、S t r C h r、S t r C m p和S t r C p y等。若要使用这些函数,必须加上S h l WA p i . h头文件。另外,如前所述,这些字符串函数既有A N S I版本,也有U n i c o d e版本,例如S t r C a t A和S t r C a t W。由于这些函数属于操作系统函数,因此,当创建应用程序时,如果定义了U N I C O D E(不带前置下划线),那么它们的符号将扩展为宽字符版本。


成为符合A N S I和U n i c o d e的应用程序

下面是应该遵循的一些基本原则:
• 将文本串视为字符数组,而不是c h a r s数组或字节数组。
• 将通用数据类型(如T C H A R和P T S T R)用于文本字符和字符串。
• 将显式数据类型(如B Y T E和P B Y T E)用于字节、字节指针和数据缓存。
• 将T E X T宏用于原义字符和字符串。
• 执行全局性替换(例如用P T S T R替换P S T R)。
• 修改字符串运算问题。例如函数通常希望你在字符中传递一个缓存的大小,而不是字节。
这意味着你不应该传递s i z e o f ( s z B u ff e r ) ,而应该传递( s i z e o f ( s z B u ff e r ) / s i z e o f ( T C H A R )。另外,如果需要为字符串分配一个内存块,并且拥有该字符串中的字符数目,那么请记住要按字节来
分配内存。这就是说,应该调用malloc(nCharacters *sizeof(TCHAR)), 而不是调用m a l l o c
( n C h a r a c t e r s )。在上面所说的所有原则中,这是最难记住的一条原则,如果操作错误,编译器
将不发出任何警告。

资源
当资源编译器对你的所有资源进行编译时,输出文件是资源的二进制文件。资源(字符串
表、对话框模板和菜单等)中的字符串值总是写作U n i c o d e字符串。在Windows 98和Wi n d o w s
2 0 0 0下,如果应用程序没有定义U N I C O D E宏,那么系统就会进行内部转换。
例如,如果在编译源代码模块时没有定义U N I C O D E,调用L o a d S t r i n g实际上就是调用
L o a d S t r i n g A函数。这时L o a d S t r i n g A就从你的资源中读取字符串,并将该字符串转换成A N S I字
符串。A N S I形式的字符串将从该函数返回给你的应用程序。

在U n i c o d e与A N S I之间转换字符串
Wi n d o w s函数M u l t i B y t e To Wi d e C h a r用于将多字节字符串转换成宽字符串。下面显示了
M u l t i B y t e To Wi d e C h a r函数。
u C o d e P a g e参数用于标识一个与多字节字符串相关的代码页号。d w F l a g s参数用于设定另一
个控件,它可以用重音符号之类的区分标记来影响字符。这些标志通常并不使用,在d w F l a g s
参数中传递0。p M u l t i B y t e S t r参数用于设定要转换的字符串, c c h M u l t i B y t e参数用于指明该字符
串的长度(按字节计算)。如果为c c h M u l t i B y t e参数传递- 1,那么该函数用于确定源字符串的长
度。
转换后产生的U n i c o d e版本字符串将被写入内存中的缓存,其地址由p Wi d e C h a r S t r参数指
定。必须在c c h Wi d e C h a r参数中设定该缓存的最大值(以字符为计量单位)。如果调用
M u l t i B y t e To Wi d e C h a r,给c c h Wi d e C h a r参数传递0,那么该参数将不执行字符串的转换,而是
返回为使转换取得成功所需要的缓存的值。一般来说,可以通过下列步骤将多字节字符串转换
成U n i c o d e等价字符串:
1) 调用M u l t i B y t e To Wi d e C h a r函数,为p Wi d e C h a r S t r参数传递N U L L,为c c h Wi d e C h a r参数
传递0。
2) 分配足够的内存块,用于存放转换后的U n i c o d e字符串。该内存块的大小由前面对
第2章Unicode计计23
下载
M u l t B y t e To Wi d e C h a r的调用返回。
3) 再次调用M u l t i B y t e To Wi d e C h a r,这次将缓存的地址作为p Wi d e C h a r S t r参数来传递,并
传递第一次调用M u l t i B y t e To Wi d e C h a r时返回的缓存大小,作为c c h Wi d e c h a r参数。
4. 使用转换后的字符串。
5) 释放U n i c o d e字符串占用的内存块
原创粉丝点击