📄 windows核心编程1-8章.txt
字号:
这些域将在第24章中详细讲述。现在,你需要知道的重要域是第29位的信息。Microsoft公司规定,他们建立的所有错误代码的这个信息位均使用0。如果你创建自己的错误代码,你必须在这个信息位中输入1。这样,就可以确保你的错误代码与Microsoft公司目前或者将来定义的错误代码不会发生冲突,
1.2 ErrorShow示例应用程序
ErrorShow应用程序“01 ErrorShow.exe"(在图1-2中列出)展示了如何获取错误代码的文本描述的方法。该应用程序的源代码和资源文件位于本书所附光盘上的01-ErrorShow目录下。一般来说,该应用程序用于显示调试程序的Watch窗口和Error Lookup程序是如何运行的。当你启动该程序时,就会出现下面这个窗口。
见原书P9的插图
你可以将任何错误代码键入该编辑控件。当你单击Look Up按钮时,在底部的滚动窗口中就会显示该错误的文本描述。该应用程序唯一令人感兴趣的特性是如何调用FormatMessage函数。下面是我使用该函数的方法:
见原书P10的程序(1)
第一个代码行用于从编辑控件中检索错误代码的号码。然后,建立一个内存块的句柄并将它初始化为NULL。FormatMessage函数在内部对内存块进行分配,并将它的句柄返回给我们。
当调用FormatMessage函数时,我传递了FORMAT_MESSAGE_FROM_SYSTEM标志。该标志告诉FormatMessage函数,我们想要系统定义的错误代码的字符串。我还传递了FORMAT_MESSAGE_ALLOCATE_BUFFER标志,告诉该函数为错误代码的文本描述分配足够大的内存块。该内存块的句柄将在hlocal变量中返回。第三个参数指明我们想要查找的错误代码的号码,第四个参数指明我们想要文本描述使用什么语言。
如果FormatMessage函数运行成功,那么错误代码的文本描述就位于内存块中,我将它拷贝到对话框底部的滚动窗口中。如果FormatMesage函数运行失败,我设法查看NetMsg.dll模块中的消息代码,以了解该错误是否与网络有关。使用NetMsg.dll模块的句柄,我再次调用FormatMessage函数。你会看到,每个DLL(或.exe)都有它自己的一组错误代码,你可以使用Message Compiler(MC.exe)将这组错误代码添加给该模块,并将一个资源添加给该模块。这就是Visual Studio的Error Lookup工具允许你用Modules对话框进行的操作。
图1-2 ErrorShow示例应用程序
见原书P11—16
第2章 UNICODE
随着Microsoft公司的Windows操作系统在全世界日益广泛的流行,对于我们这些软件开发人员来说,将我们的目标瞄准国际上的各个不同市场,已经成为一个越来越重要的问题。美国的软件版本比国际版本提前6个月推向市场,这曾经是个司空见惯的现象。但是,由于各国对Windows操作系统提供了越来越多的支持,因此就更加容易为国际市场生产各种应用软件,从而缩短了软件的美国版本与国际版本推出的时间间隔。
Windows操作系统始终不逾地提供各种支持,以帮助软件开发人员进行应用程序的本地化工作。应用软件可以从各种不同的函数中获得特定国家的信息,并可观察控制面板的设置,以确定用户的首选项。Windows甚至支持不同的字体,以适应我们的应用的需要。
我之所以将这一章放在本书的开头,是因为我考虑到Unicode是开发任何应用程序时要采用的基本步骤。关于Unicode的问题,我在本书的每一章中几乎都要讲到,而且本书中给出的所有示例应用程序都是“用Unicode实现的”。如果你为Microsoft Windows 2000或Microsoft Windows CE开发应用程序,你应该使用Unicode进行开发。如果你为Microsoft Windows 98开发应用程序,你必须对某些问题作出决定。本章也要讲述Windows 98的有关问题。
2.1 字符集
软件的本地化要解决的真正问题,实际上就是如何来处理不同的字符集。多年来,我们许多人一直将文本串作为一系列单字节字符来进行编码,并在结尾处放上一个零。对于我们来说,这已经成了习惯。当我们调用strlen函数时,它在以0结尾的单字节字符数组中返回字符的数目。
问题是,有些文字和书写规则(比如日文中的汉字就是个典型的例子)的字符集中的符号太多了,因此单字节(它提供的符号最多不能超过256个)是根本不敷使用的。为此我们创建了双字节字符集(DBCS),以支持这些文字和书写规则。
2.1.1 单字节与双字节字符集
在双字节字符集中,字符串中的每个字符可以包含一个字节,也可以包含两个字节。例如,日文中的汉字,如果第一个字符在0x81与0x9F之间,或者在0xE0与0xFC之间,那么你就必须观察下一个字节,才能确定字符串中的这个完整的字符。如果要使用双字节字符集,对于程序员来说简直是个很大的难题,因为有些字符只有一个字节宽,而有些字符则是两个字节宽。
如果只是调用strlen函数,那么你无法真正了解字符串中究竟有多少字符,它只能告诉你到达结尾的0之前有多少个字节。ANSI的C运行期库中没有配备相应的函数,使你能够对双字节字符集进行操作。但是,Microsoft Visual C++的运行期库却包含许多函数,如_mbslen,它可以用来操作多字节(既包括单字节也包括双字节)字符串。
为了帮助你对DBCS字符串进行操作,Windows提供了下面的一组帮助函数。
函数 描述
PTSTR CharNext 返回字符串中的下一个字符的地址
(PCTSTR pszCurrentChar);
PTSTR CharPrev 返回字符串中的上一个字符的地址
(PCTSTR pszStart,
PCTSTR pszCurrentChar);
BOOL IsDBCSLendByte 如果该字节是DBCS字符的第一个字节,则返
(BYTE bTestChar); 回TRUE
2.1.2 Unicode:宽字节字符集
Unicode是Apple和Xerox公司于1988年建立的一个技术标准。1991年,成立了一个集团机构负责Unicode的开发和推广应用。该集团由Apple、Compaq、HP、IBM、Microsoft、Oracle、Silicon Graphics、Sybase、Unisys和Xerox等公司组成。(若要了解该集团的全部成员,请通过网址www.Unicode.org查找。)该集团公司负责维护Unicode标准。Unicode的完整描述可以参阅AddisonWesley出版的《Unicode Standard》一书。(该书可以通过网址www.Unicode.org订购。)
Unicode提供了一种简单而又一致的表示字符串的方法。Unicode字符串中的所有字符都是16位的字符(两个字节)。它没有专门的字节来指明下一个字节是属于同一个字符的组成部分,还是一个新字符。这意味着你只需要对指针进行递增或递减,就可以遍历字符串中的各个字符。你不再需要调用CharNext,CharPrev和IsDBCSLeadByte之类的函数。
由于Unicode用一个16位的值来表示每个字符,因此总共可以得到65000个字符,这样,它就能够对世界各国的书面文字中的所有字符进行编码。这远远超过了单字节字符集的256个字符的数目。
目前,已经为阿拉伯文、中文拼音、西里尔字母(俄文)、希腊文、西伯莱文、日文、韩文和拉丁文(英文)字母定义了Unicode代码点1。这些字符集中还包含了大量的标点符号、数学符号、技术符号、箭头、装饰标志、区分标志和其他许多字符。如果你将所有这些字母和符号加在一起,总计约达35000个不同的代码点,这样,总计的65000个代码点中,大约还有一半可供将来扩充时使用。
这65536个字符可以分成不同的区域。下面这个表显示了一部分这样的区域以及分配给这些区域的字符。
16位代码 字符 16位代码 字符
0000-007F ASCII 0300-036F 通用区分标志
0080-00FF 拉丁文1字符 0400-04FF 西里尔字母
0100-017F 欧洲拉丁文 0530-058F 亚美尼亚文
0180-01FF 扩充拉丁文 0590-05FF 西伯莱文
0250-02AF 标准拼音 0600-06FF 阿拉伯文
02B0-02FF 修改型字母 0900-097F 梵文
注1. 代码点是指字符集中的一个符号的位置
目前尚未分配的代码点大约还有29000个,不过它们是保留供将来使用的。另外,大约有6000个代码点是保留供你个人使用的。
2. 2 为何应该使用Unicode
当你开发应用程序时,你当然应该考虑利用Unicode的优点。即使现在你不打算对你的应用程序进行本地化,开发时将Unicode放在心上,肯定可以简化将来的代码转换工作。此外,Unicode还具备下列功能:
* 可以很容易地在不同语言之间进行数据交换
* 使你能够分配支持所有语言的单个二进制.exe文件或DLL文件
* 提高你的应用程序的运行效率(本章后面还要详细介绍)
2.3 Windows 2000与Unicode
Windows 2000是使用Unicode从头进行开发的,用于创建窗口、显示文本、进行字符串操作等的所有核心函数都需要Unicode字符串。如果你调用任何一个Windows函数并给它传递一个ANSI字符串,那么系统首先要将字符串转换成Unicode,然后将Unicode字符串传递给操作系统。如果你希望函数返回ANSI字符串,系统就会首先将Unicode字符串转换成ANSI字符串,然后将结果返回给你的应用程序。所有这些转换操作都是在你看不见的情况下发生的。当然,进行这些字符串的转换需要占用系统的时间和内存开销。
例如,如果你调用CreateWindowEx函数,并传递类名字和窗口标题文本的非Unicode字符串,那么CreateWindowEx必须分配内存块(在你的进程的默认堆中),将非Unicode字符串转换成Unicode字符串,并将结果存储在分配到的内存块中,然后调用Unicode版本的CreateWindowEx函数。
对于用字符串填入缓存的函数来说,系统必须首先将Unicode字符串转换成非Unicode字符串,然后你的应用程序才能处理该字符串。由于系统必须执行所有这些转换操作,因此你的应用程序需要更多的内存,并且运行的速度比较慢。通过从头开始用Unicode来开发应用程序,你就能够使你的应用程序更加有效地运行。
2. 4 Windows 98与Unicode
Windows 98不是一种全新的操作系统。它继承了16位Windows操作系统的特性,它不是用来处理Unicode。如果要增加对Unicode的支持,其工作量非常大,因此在该产品的特性列表中没有包括这个支持项目。由于这个原因,Windows 98象它的前任产品一样,几乎都是使用ANSI字符串来进行所有的内部操作的。
你仍然可以编写用于处理Unicode字符和字符串的Windows应用程序,不过,使用Windows函数要难得多。例如,如果你想要调用CreateWindowEx函数并将ANSI字符串传递给它,这个调用的速度非常快,不需要从你进程的默认堆栈中分配缓存,也不需要进行字符串转换。但是,如果你想要调用CreateWindowEx函数并将Unicode字符串传递给它,你就必须明确分配缓存,并调用函数,以便执行从Unicode到ANSI字符串的转换操作。然后你可以调用CreateWindowEx,传递ANSI字符串。当CreateWindowEx函数返回时,你就能释放临时缓存。这比使用Windows 2000上的Unicode要麻烦得多。在本章的后面部分中,我要介绍如何在Windows 98下进行这些转换。
虽然Unicode函数的大多数代码在Windows 98中不起任何作用,但是有少数Unicode函数确实拥有非常有用的实现代码。这些函数是:
见原书的P21
可惜的是,这些函数中有许多函数在Windows 98中会出现各种各样的错误。有些函数无法使用某些字体,有些函数会破坏内存堆栈,有些函数会使打印机驱动程序崩溃,如此等等。如果你要使用这些函数,你必须对它们进行大量的测试。即使这样,你可能仍然无法解决问题。因此你必须向用户说明这些情况。
2. 5 Windows CE与Unicode
Windows CE操作系统是为小型设备开发的,这些设备的内存很小,并且不带磁盘存储器。你可能认为,由于Microsoft公司的主要目标是建立一种尽可能小的操作系统,因此它会使用ANSI作为自己的字符集。但是Microsoft公司并不是鼠目寸光。他们懂得,采用Windows CE的设备要在世界各地销售,他们希望降低软件开发成本,这样就能更加容易地开发应用程序。为此,Windows CE本身就是使用Unicode的一种操作系统。
但是,为了使Windows CE尽量做得小一些,Microsoft公司决定完全不支持ANSI Windows函数。因此,如果你要为Windows CE开发应用程序,你必须懂得Unicode,并且在整个应用程序中使用Unicode。
2. 6 需要注意的问题
下面让我们进一步明确一下“Microsoft公司对Unicode支持的情况”:
* Windows 2000既支持Unicode,也支持ANSI,因此你可以为它们当中的任何一种开发应用程序
* Windows 98 只支持ANSI,你只能为ANSI开发应用程序
* Windows CE只支持Unicode,你只能为Unicode开发应用程序
虽然Microsoft公司试图让软件开发人员能够非常容易地开发在这3种平台上运行是软件,但是Unicode与ANSI之间的差异使得事情变得困难起来,并且这种差异通常是我遇到的最大的问题之一。请不要误解,Microsoft公司坚定地支持Unicode,并且我也坚决鼓励你使用它。不过你应该懂得,你可能遇到一些问题,需要一定的时间来解决这些问题。我建议你尽可能使用Unicode。如果你运行Windows 98,那么只有在必要时才要转换到ANSI。
不过,还有另一个小问题你应该了解,那就是COM。
2.7 对COM的简单说明
当Microsoft公司将COM从16位Windows转换成Win32时,公司作出了一个决定,即,需要字符串的所有COM接口方法都只能接受Unicode字符串。这是个了不起的决定,因为COM通常用于使不同的组件能够互相之间进行通信,而Unicode则是传递字符串的最佳手段。
如果你为Windows 2000或Windows CE开发应用程序,并且也使用COM,那么你将会如虎添翼。在你的整个源代码中使用Unicode,将使与操作系统进行通信和与COM对象进行通信的操作变成一件轻而易举的事情。
如果你为Windows 98开发应用程序,并且也使用COM,那么你将会遇到一些问题。COM要求你使用Unicode字符串。操作系统的大多数函数要求你使用ANSI字符串。那是多么难办的事情啊!我曾经从事过若干个项目的开发,在这些项目中,我编写了许多代码,仅仅是为了来回进行字符串的转换。
2. 8 如何编写Unicode源代码
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -