《Windows核心编程002.pdf》由会员分享,可在线阅读,更多相关《Windows核心编程002.pdf(16页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、下载第2章U n i c o d e随着M i c r o s o f t公司的Wi n d o w s操作系统在全世界日益广泛的流行,对于软件开发人员来说,将目标瞄准国际上的各个不同市场,已经成为一个越来越重要的问题。美国的软件版本比国际版本提前6个月推向市场,这曾经是个司空见惯的现象。但是,由于各国对 Wi n d o w s操作系统提供了越来越多的支持,因此就更加容易为国际市场生产各种应用软件,从而缩短了软件的美国版本与国际版本推出的时间间隔。Wi n d o w s操作系统始终不逾地提供各种支持,以帮助软件开发人员进行应用程序的本地化工作。应用软件可以从各种不同的函数中获得特定国家的信
2、息,并可观察控制面板的设置,以确定用户的首选项。Wi n d o w s甚至支持不同的字体,以适应应用的需要。之所以将这一章放在本书的开头,是因为考虑到 U n i c o d e是开发任何应用程序时要采用的基本步骤。本书的每一章中几乎都要讲到关于 U n i c o d e的问题,而且书中给出的所有示例应用程序都是“用U n i c o d e实现的”。如果你为Microsoft Windows 2000或Microsoft Windows CE开发应用程序,你应该使用U n i c o d e进行开发。如果你为Microsoft Windows 98开发应用程序,你必须对某些问题作出决定。
3、本章也要讲述Windows 98的有关问题。2.1 字符集软件的本地化要解决的真正问题,实际上就是如何来处理不同的字符集。多年来,许多人一直将文本串作为一系列单字节字符来进行编码,并在结尾处放上一个零。对于我们来说,这已经成了习惯。当调用s t r l e n函数时,它在以0结尾的单字节字符数组中返回字符的数目。问题是,有些文字和书写规则(比如日文中的汉字就是个典型的例子)的字符集中的符号太多了,因此单字节(它提供的符号最多不能超过 2 5 6个)是根本不敷使用的。为此出现了双字节字符集(D B C S),以支持这些文字和书写规则。2.1.1 单字节与双字节字符集在双字节字符集中,字符串中的每
4、个字符可以包含一个字节或包含两个字节。例如,日文中的汉字,如果第一个字符在 0 x 8 1与0 x 9 F之间,或者在0 x E 0与0 x F C之间,那么就必须观察下一个字节,才能确定字符串中的这个完整的字符。使用双字节字符集,对于程序员来说简直是个很大的难题,因为有些字符只有一个字节宽,而有些字符则是两个字节宽。如果只是调用s t r l e n函数,那么你无法真正了解字符串中究竟有多少字符,它只能告诉你到达结尾的0之前有多少个字节。A N S I的C运行期库中没有配备相应的函数,使你能够对双字节字符集进行操作。但是,Microsoft Visual C+的运行期库却包含许多函数,如_
5、m b s l e n,它可以用来操作多字节(既包括单字节也包括双字节)字符串。为了帮助你对D B C S字符串进行操作,Wi n d o w s提供了下面的一组帮助函数(见表2-1)。前两个函数CharNext 和Char Prev 允许前向或逆向遍历DBCS 字符串,方法是每次一个字符。第三个函数 IsDBCSLeadByte,在字节返回到一个两字字节符的第一个字节时将返回T R U E。表2-1 对D B C S字符串进行操作的帮助函数函数描述PTSTR CharNext(PCTSTR pszCurrentChar);返回字符串中的下一个字符的地址PTSTR CharPrev(PCTST
6、R pszStart,PCTSTR 返回字符串中的上一个字符的地址p s z C u r r e n t C h a r);BOOL IsDBCSLeadByteTRUE(BYTE bTestChar);如果该字节是DBCS字符的第一个字节,则返回尽管这些函数使得我们对 D B C S的操作更容易,但还需要,一个更好的方法让我们来看看U n i c o d e。2.1.2 Unicode:宽字节字符集U n i c o d e是A p p l e和X e r o x公司于1 9 8 8年建立的一个技术标准。1 9 9 1年,成立了一个集团机构负责U n i c o d e的开发和推广应用。该集
7、团由A p p l e、C o m p a q、H P、I B M、M i c r o s o f t、O r a c l e、Silicon Graphics,Inc.、S y b a s e、U n i s y s和X e r o x等公司组成(若要了解该集团的全部成员,请通过网址w w w.U n i c o d e.o rg查找)。该集团负责维护U n i c o d e标准。U n i c o d e的完整描述可以参阅A d d i s o n We s l e y出版的Unicode Standard一书(该书可以通过网址w w w.U n i c o d e.o rg订购)。U
8、n i c o d e提供了一种简单而又一致的表示字符串的方法。U n i c o d e字符串中的所有字符都是1 6位的(两个字节)。它没有专门的字节来指明下一个字节是属于同一个字符的组成部分,还是一个新字符。这意味着你只需要对指针进行递增或递减,就可以遍历字符串中的各个字符,不再需要调用C h a r N e x t、C h a r P r e v和I s D B C S L e a d B y t e之类的函数。由于U n i c o d e用一个1 6位的值来表示每个字符,因此总共可以得到 65 000个字符,这样,它就能够对世界各国的书面文字中的所有字符进行编码,远远超过了单字节字符
9、集的 2 5 6个字符的数目。目前,已经为阿拉伯文、中文拼音、西里尔字母(俄文)、希腊文、西伯莱文、日文、韩文和拉丁文(英文)字母定义了 U n i c o d e代码点。这些字符集中还包含了大量的标点符号、数学符号、技术符号、箭头、装饰标志、区分标志和其他许多字符。如果将所有这些字母和符号加在一起,总计约达3 5 0 0 0个不同的代码点,这样,总计 65 000多个代码点中,大约还有一半可供将来扩充时使用。这65 536个字符可以分成不同的区域。表 2-2 显示了这样的区域的一部分以及分配给这些区域的字符。表2-2 区域字符1 6位代码字符16 位 代 码字符0 0 0 0-0 0 7 F
10、A S C I I0 3 0 0-0 3 6 F通用区分标志0 0 8 0-0 0 F F拉丁文1字符0 4 0 0-0 4 F F西里尔字母0 1 0 0-0 1 7 F欧洲拉丁文0 5 3 0-0 5 8 F亚美尼亚文0 1 8 0-0 1 F F扩充拉丁文0 5 9 0-0 5 F F西伯莱文0 2 5 0-0 2 A F标准拼音0 6 0 0-0 6 F F阿拉伯文0 2 B 0-0 2 F F修改型字母0 9 0 0-0 9 7 F梵文目前尚未分配的代码点大约还有29 000个,不过它们是保留供将来使用的。另外,大约有6 0 0 0个代码点是保留供个人使用的。12计计第一部分程序员必
11、读下载代码点是字符集中符号的位置。2.2 为什么使用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还具备下列功能:可以很容易地在不同语言之间进行数据交换。使你能够分配支持所有语言的单个二进制.e x e文件或D L L文件。提高应用程序的运行效率(本章后面还要详细介绍)。2.3 Windows 2000与U n i c o d eWindows 2000是使用U n i c o d e从头进行开发的,
12、用于创建窗口、显示文本、进行字符串操作等的所有核心函数都需要 U n i c o d e字符串。如果调用任何一个 Wi n d o w s函数并给它传递一个A N S I字符串,那么系统首先要将字符串转换成 U n i c o d e,然后将U n i c o d e字符串传递给操作系统。如果希望函数返回 A N S I字符串,系统就会首先将 U n i c o d e字符串转换成A N S I字符串,然后将结果返回给你的应用程序。所有这些转换操作都是在你看不见的情况下发生的。当然,进行这些字符串的转换需要占用系统的时间和内存。例如,如果调用C r e a t e Wi n d o w E x
13、函数,并传递类名字和窗口标题文本的非U n i c o d e字符串,那么C r e a t e Wi n d o w E x必须分配内存块(在你的进程的默认堆中),将非U n i c o d e字符串转换成U n i c o d e字符串,并将结果存储在分配到的内存块中,然后调用U n i c o d e版本的C r e a t e Wi n d o w E x函数。对于用字符串填入缓存的函数来说,系统必须首先将 U n i c o d e字符串转换成非U n i c o d e字符串,然后你的应用程序才能处理该字符串。由于系统必须执行所有这些转换操作,因此你的应用程序需要更多的内存,并且运
14、行的速度比较慢。通过从头开始用 U n i c o d e来开发应用程序,就能够使你的应用程序更加有效地运行。2.4 Windows 98与U n i c o d eWindows 98不是一种全新的操作系统。它继承了1 6位Wi n d o w s操作系统的特性,它不是用来处理U n i c o d e的。如果要增加对U n i c o d e的支持,其工作量非常大,因此在该产品的特性列表中没有包括这个支持项目。由于这个原因,Windows 98像它的前任产品一样,几乎都是使用A N S I字符串来进行所有的内部操作的。仍然可以编写用于处理U n i c o d e字符和字符串的Wi n d
15、 o w s应用程序,不过,使用Wi n d o w s函数要难得多。例如,如果想要调用 C r e a t e Wi n d o w E x函数并将A N S I字符串传递给它,这个调用的速度非常快,不需要从你进程的默认堆栈中分配缓存,也不需要进行字符串转换。但是,如果想要调用C r e a t e Wi n d o w E x函数并将U n i c o d e字符串传递给它,就必须明确分配缓存,并调用函数,以便执行从U n i c o d e到A N S I字符串的转换操作。然后可以调用 C r e a t e Wi n d o w E x,传递A N S I字符串。当C r e a t
16、e Wi n d o w E x函数返回时,就能释放临时缓存。这比使用 Windows 2000上的U n i c o d e要麻烦得多。本章的后面要介绍如何在Windows 98下进行这些转换。虽然大多数U n i c o d e函数在Windows 98中不起任何作用,但是仍有少数U n i c o d e函数确实非常有用。这些函数是:E n u m R e s o u r c e L a n g u a g e s WG e t Te x t E x t e n t P o i n t 3 2 WE n u m R e s o u r c e N a m e s WG e t Te x
17、t E x t e n t P o i n t W第 2章 Unicode计计13下载E n u m R e s o u r c e Ty p e s WL s t r l e n WE x t Te x t O u t WM e s s a g e B o x E xWF i n d R e s o u r c e WM e s s a g e B o x WF i n d R e s o u r c e E x WTe x t O u t WG e t C h a r Wi d t h WWi d e C h a r To M u l t i B y t eG e t C o m m a
18、n d L i n e WM u l t iBy t e To Wi d e C h a r可惜的是,这些函数中有许多函数在Windows 98中会出现各种各样的错误。有些函数无法使用某些字体,有些函数会破坏内存堆栈,有些函数会使打印机驱动程序崩溃,等等。如果要使用这些函数,必须对它们进行大量的测试。即使这样,可能仍然无法解决问题。因此必须向用户说明这些情况。2.5 Windows CE与U n i c o d eWindows CE操作系统是为小型设备开发的,这些设备的内存很小,并且不带磁盘存储器。你可能认为,由于 M i c r o s o f t公司的主要目标是建立一种尽可能小的操作系统
19、,因此它会使用A N S I作为自己的字符集。但是M i c r o s o f t公司并非鼠目寸光,他们懂得,Windows CE的设备要在世界各地销售,他们希望降低软件开发成本,这样就能更加容易地开发应用程序。为此,Windows CE本身就是使用U n i c o d e的一种操作系统。但是,为了使Windows CE尽量做得小一些,M i c r o s o f t公司决定完全不支持ANSI Wi n d o w s函数。因此,如果要为Windows CE开发应用程序,必须懂得U n i c o d e,并且在整个应用程序中使用U n i c o d e。2.6 需要注意的问题下面让我
20、们进一步明确一下“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开发应用程序。虽然M i c r o s o f t公司试图让软件开发人员能够非常容易地开发在这 3种平台上运行的软件,但是U n i c o d e与A N S I之间的差异使得事情变得困难起来,并且这种差异通常是我遇到的最大
21、的问题之一。请不要误解,M i c r o s o f t公司坚定地支持U n i c o d e,并且我也坚决鼓励你使用它。不过你应该懂得,你可能遇到一些问题,需要一定的时间来解决这些问题。建议你尽可能使用U n i c o d e。如果运行Windows 98,那么只有在必要时才需转换到A N S I。不过,还有另一个小问题你应该了解,那就是C O M。2.7 对C O M的简单说明当M i c r o s o f t公司将C O M从1 6位Wi n d o w s转换成Wi n 3 2时,公司作出了一个决定,即需要字符串的所有C O M接口方法都只能接受U n i c o d e字符串
22、。这是个了不起的决定,因为C O M通常用于使不同的组件能够互相进行通信,而U n i c o d e则是传递字符串的最佳手段。如果你为Windows 2000或Windows CE开发应用程序,并且也使用C O M,那么你将会如虎添翼。在你的整个源代码中使用U n i c o d e,将使与操作系统进行通信和与C O M对象进行通信的操作变成一件轻而易举的事情。如果你为Windows 98开发应用程序,并且也使用C O M,那么将会遇到一些问题。C O M要14计计第一部分程序员必读下载求使用U n i c o d e字符串,而操作系统的大多数函数要求使用 A N S I字符串。那是多么难办
23、的事情啊!我曾经从事过若干个项目的开发,在这些项目中,我编写了许多代码,仅仅是为了来回进行字符串的转换。2.8 如何编写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),就可以修改然后重新编译该源文件。2.8.1 C运行期库对U n i c o d e的支持为了利用U n i c o d e字符串,定义了一些
24、数据类型。标准的 C头文件S t r i n g.h已经作了修改,以便定义一个名字为w c h a r _ t的数据类型,它是一个U n i c o d e字符的数据类型:例如,如果想要创建一个缓存,用于存放最多为 9 9个字符的U n i c o d e字符串和一个结尾为零的字符,可以使用下面这个语句:该语句创建了一个由 1 0 0个1 6位值组成的数组。当然,标准的 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也拥有一组补充函数。清单2-
25、1显示了一些标准的ANSI C字符串函数,后面是它们的等价U n i c o d e函数。清单2-1 标准的ANSI C字符串函数和它们的等价U n i c o d e函数请注意,所有的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即可。注意大多数软件开发人员可能已经不记得这样一个非常重要的问题了,那就是M i c r o s o f t公司提供的C运行期库与A N S I的标准C运行期库是一致的。ANSI C规定,C运行期库支持U n i c o d
26、 e字符和字符串。这意味着始终都可以调用C运行期函数,以便对U n i c o d e字符和字符串进行操作,即使是在Windows 98上运行,也可以调用这些函数。换句话说,w c s c a t、w c s l e n和w c s t o k等函数都能够在Windows 98上很好地运行,这些都是必须关心的操作系统函数。第 2章 Unicode计计15下载对于包含了对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进行编译的单个源代
27、码文件。若要建立双重功能,必须包含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
28、,那么_ 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进行编译的源代码。但是,除了使用这些宏之外,还有一些操作是必须进行的。T C h a r.h文件包含了另外一些宏。若要定义一个A N S I/U n i c o d e通用的字符
29、串数组,请使用下面的 T C H A R数据类型。如果定义了_ U N I C O D E,T C H A R将声明为下面的形式:如果没有定义_ U N I C O D E,则T C H A R将声明为下面的形式:使用该数据类型,可以像下面这样分配一个字符串:也可以创建对字符串的指针:不过上面这行代码存在一个问题。按照默认设置,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,该编译器将能正确地编译这一行代码。但是,如果定义了 _ U N I C
30、O D E,就会产生一个错误。若要生成一个U n i c o d e字符串而不是A N S I字符串,必须将该代码行改写为下面的样子:字符串(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文件中做了定义。如果定义了
31、 _ U N I C O D E,那么_ T E X T定义为下面的形式:如果没有定义_ U N I C O D E,_ T E X T将定义为使用该宏,可以改写上面这行代码,这样,无论是否定义了 _ U N I C O D E宏,它都能够正确地进行编译。如下所示:16计计第一部分程序员必读下载_ T E X T宏也可以用于字符串。例如,若要检查一个字符串的第一个字符是否是大写字母 J,只需编写下面的代码即可:2.8.2 Wi n d o w s定义的U n i c o d e数据类型Wi n d o w s头文件定义了表2-3列出的数据类型。表2-3 Uincode 数据类型数 据 类 型说
32、明W C H A RU n i c o d e字符P W S T R指向U n i c o d e字符串的指针P C W S T R指向一个恒定的U n i c o d e字符串的指针这些数据类型是指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
33、C O D E宏用于C运行期头文件,而U N I C O D E宏则用于Wi n d o w s头文件。当编译源代码模块时,通常必须同时定义这两个宏。2.8.3 Wi n d o w s中的U n i c o d e函数和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接受U n i c o d e字符串,另一个C r e a t e Wi n d o w E x接受A N S I字符串。情况确实如此,不过,这两个函数的原型实际上是下面的样子:第 2章 Unicode计计17下载C r
34、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
35、 r.h文件中,C r e a t e Wi n d o w E x实际上是定义为下面这种形式的一个宏:当编译源代码模块时,U N I C O D E 是否已经作了定义,将决定你调用的是哪个C r e a t e Wi n d o w E x版本。当转用一个1 6位的Wi n d o w s应用程序时,你在编译期间可能没有定义U N I C O D E。对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 A的调用,即对C r e a t e Wi n d o w E x的A N S I版本的调用。由于 1
36、6位Wi n d o w s只提供了C r e a t e Wi n d o w s E x的A N S I版本,因此可以比较容易地转用它的应用程序。在Windows 2000下,M i c r o s o f t的C r e a t e Wi n d o w E x A源代码只不过是一个形实替换程序层或翻译层,用于分配内存,以便将 A N S I字符串转换成U n i c o d e字符串。该代码然后调用 C r e a t eWi n d o w E x W,并传递转换后的字符串。当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
37、 E x A便释放它的内存缓存,并将窗口句柄返回给你。如果要创建其他软件开发人员将要使用的动态链接库(D L L),请考虑使用下面的方法。在D L L中提供两个输出函数。一个是A N S I版本,另一个是U n i c o d e版本。在A N S I版本中,只需要分配内存,执行必要的字符串转换,并调用该函数的 U n i c o d e版本(本章后面部分介绍这个进程)。在Windows 98下,M i c r o s o f t的C r e a t e Wi n d o w E x A源代码是执行操作的函数。Windows 98提供了接受U n i c o d e参数的所有Wi n d o
38、w s函数的进入点,但是这些函数并不将 U n i c o d e字符串转换成 A N S I字符串,它们只返回运行失败的消息。调用G e t L a s t E r r o r将返回 E R R O R _C A L L _ N O T _ I M P L E M E N T E D。这些函数中只有A N S I版本的函数才能正确地运行。如果编译的代码调用了任何宽字符函数,应用程序将无法在Windows 98下运行。Windows API中的某些函数,比如Wi n E x e c和O p e n F i l e等,只是为了实现与1 6位Wi n d o w s程序的向后兼容而存在,因此,应该
39、避免使用。应该使用对 C r e a t e P r o c e s s和C r e a t e F i l e函数的调用来取代对Wi n E x e c和O p e n F i l e函数的调用。从系统内部来讲,老的函数完全可以调用新的函数。老的函数存在的一个大问题是,它们不接受 U n i c o d e字符串。当调用这些函数时,必须传递A N S I字符串。另一方面,所有新的和未过时的函数在 Windows 2000中都同时拥有 A N S I和18计计第一部分程序员必读下载U n i c o d e两个版本。2.8.4 Wi n d o w s字符串函数Wi n d o w s还提供了
40、一组范围很广的字符串操作函数。这些函数与 C运行期字符串函数(如s t r c p y和w c s c p y)很相似。但是该操作系统函数是操作系统的一个组成部分,操作系统的许多组件都使用这些函数,而不使用 C运行期库。建议最好使用操作系统函数,而不要使用 C运行期字符串函数。这将有助于稍稍提高你的应用程序的运行性能,因为操作系统字符串函数常常被大型应用程序比如操作系统的外壳进程 E x p l o r e r.e x e所使用。由于这些函数使用得很多,因此,在你的应用程序运行时,它们可能已经被装入R A M。若要使用这些函数,系统必须运行 Windows 2000或Windows 98。如果
41、安装了 I n t e r n e tExplorer 4.0或更新的版本,也可以在较早的Wi n d o w s版本中获得这些函数。在经典的操作系统函数样式中,操作系统字符串函数名既包含大写字母,也包含小写字母,它的形式类似这个样子: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。由于这些函数属于操作系统函数,因此,当创建应用程序时
42、,如果定义了U N I C O D E(不带前置下划线),那么它们的符号将扩展为宽字符版本。2.9 成为符合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的应用程序。下面是应该遵循的一些基本原则:将文本串视为字符数组,而不是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宏用于原义字符和字符串。执行全局性替换(例
43、如用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)。在上面所说的所有原则
44、中,这是最难记住的一条原则,如果操作错误,编译器将不发出任何警告。当我为本书的第一版编写示例程序时,我编写的原始程序只能编译为 A N S I程序。后来,当我开始撰写本章的内容时,我想我应该鼓励使用 U n i c o d e,并且打算创建一些示例程序,以便展示你可以非常容易地编写既可以用 U n i c o d e也可以用A N S I来编译的程序。这时我发现最好的办法是将本书的所有示例程序进行转换,使它们都能够用 U n i c o d e和A N S I进行编译。我用了大约4个小时将所有程序进行了转换。考虑到我以前从来没有这方面的转换经验,这个速度是相当不错了。2.9.1 Wi n d
45、o w s字符串函数Wi n d o w s也提供了一组用于对U n i c o d e字符串进行操作的函数,表2-4对它们进行了描述。第 2章 Unicode计计19下载表2-4 对U n i c o d e字符串进行操作的函数函数描述l s t r c a t将一个字符串置于另一个字符串的结尾处l s t r c m p对两个字符串进行区分大小写的比较l s t r c m p i对两个字符串进行不区分大小写的比较l s t r c p y将一个字符串拷贝到内存中的另一个位置l s t r l e n返回字符串的长度(按字符数来计量)这些函数是作为宏来实现的,这些宏既可以调用函数的 U n
46、 i c o d e版本,也可以调用函数的A N S I版本,这要根据编译源代码模块时是否已经定义了 U N I C O D E而定。例如,如果没有定义U N I C O D E,l s t r c a t函数将扩展为l s t r c a t A。如果定义了U N I C O D E,l s t r c a t将扩展为l s t r c a t W。有两个字符串函数,即l s t r c m p和l s t r c m p i,它们的行为特性与等价的C运行期函数是不同的。C运行期函数s t r c m p、s t r c m p i、w c s c m p和w c s c m p i只是对字
47、符串中的代码点的值进行比较,这就是说,这些函数将忽略实际字符的含义,只是将第一个字符串中的每个字符的数值与第二个字符串中的字符的数值进行比较。而 Wi n d o w s函数l s t r c m p和l s t r c m p i是作为对Wi n d o w s函数C o m p a r e S t r i n g的调用来实现的。该函数对两个 U n i c o d e字符串进行比较。C o m p a r e S t r i n g的第一个参数用于设定语言 I D(L C I D),这是个3 2位值,用于标识一种特定的语言。C o m p a r e S t r i n g使用这个L C
48、I D来比较这两个字符串,方法是对照一种特定的语言来查看它们的字符的含义。这种操作方法比 C运行期函数简单地进行数值比较更有意义。当l s t r c m p函数系列中的任何一个函数调用 C o m p a r e S t r i n g时,该函数便将调用 Wi n d o w s的G e t T h r e a d S t r i n g函数的结果作为第一个参数来传递:每次创建一个线程时,它就被赋予一种语言。函数将返回该线程的当前语言设置。C o m p a r e S t r i n g的第二个参数用于标识一些标志,这些标志用来修改该函数比较两个字符串时所用的方法。表2-5显示了可以使用的
49、标志。表2-5 Compare String 的标志及含义标志含义N O R M _ I G N O R E C A S E忽略字母的大小写N O R M _ I G N O R E K A N AT Y P E不区分平假名与片假名字符N O R M _ I G N O R E N O N S PA C E忽略无间隔字符N O R M _ I G N O R E S Y M B O L S忽略符号N O R M _ I G N O R E W I D T H不区分单字节字符与作为双字节字符的同一个字符S O RT _ S T R I N G S O RT将标点符号作为普通符号来处理当l s t
50、 r c m p调用C o m p a r e S t r i n g时,它传递 0作为f d w S t y l e的参数。但是,当 l s t r c m p i调用C o m p a r e S t r i n g时,它就传递N O R M _ I G N O R E C A S E。C o m p a r e S t r i n g的其余4个参数用于设定两20计计第一部分程序员必读下载个字符串和它们各自的长度。如果为 c c h 1参数传递-1,那么该函数将认为p S t r i n g 1字符串是以0结尾,并计算该字符串的长度。对于p S t r i n g 2字符串来说,参数c c