第1章纵览Windows 2000
毫不隐瞒地说,我是一个科幻迷。但是,我认为现实中的科幻描述很荒谬可笑。虽然我能
接受存在生物圈和星系帝国的事实,甚至可以接受超光速的星际飞行器,但是我不能接受的是
那么容易地与外星人进行交流。当然,在Star Tr e k中,所有外星人都讲英语。事实上,极少数的
科幻故事涉及到第一次接触外星文化会如何。通常都是外星人通过收音机和电视学英语,要么
它们依靠一台功能强大的翻译机。不管哪种方法都在人们看不见的地方进行,因而不会妨碍故
事的进展。
考虑人们之间的交流。不论与谁交流,不论要讨论什么话题,两人的交流大部分都是不言
而喻的,并不需要表达出来,举个什么例子呢?看下面古怪却正确的句子:
“I saw the Grand canyon while flying to New Yo r k .”(在飞往纽约时,我见到了Grand
Canyon。)
你我都知道,说话者那时正飞往纽约,并看见了Grand Canyon (大峡谷)。但从语法上理解,
这句话意指, Grand Canyon大峡谷正飞往纽约, 且我看见了它!当然是废话,因为大家都知道
Grand Canyon大峡谷是不会飞的。而外星人(或电脑,就此而言)并不知道Grand Canyon是什么。
之所以人们能理解这句话,是因为有每个人了解一些常识。
当你穿越世界上的文化界线时,问题则更糟。多年前,英国报纸上刊载在头条的真实新闻
如下:
“British Left Wa ffles on Falklands”(British Left议论F a l k l a n d群岛的局势。)
通常,美国人会感到惊讶,英国人忘记吃早餐为什么值得报道(他们认为英国人在F a l k l a n d s
忘记吃华夫饼干)。然而,如果你了解在英国发生的事件,就会明白British Left是一个政治党派。
头条新闻在说,他们不能接受F a l k l a n d群岛的局势。
英国的报纸编辑是不是疯了?当然不是。他们简单地认为,读者已经有了这些方面的知识,
而多数美国人却没有。
同样的概念在任何复杂的程序设计中也是正确的。当你与其他人讨论编程问题时,你也应
设想他们了解这些问题。若在大街上拦住一个人,就问为什么P r i n t f连接到代码时会失败。除非
你碰巧遇到C 或C + +程序员,不然对方绝对不知所云。
1.1 新的语言
假定此刻你是C + +程序员。你与其他C + +程序员一样知道某些基础知识。如果老板指派你解
决C o b o l语言中的2 0 0 0年问题,没人指望你能顺利地开始这项工作。C o b o l语言的程序员具有和
C + +程序员不同的知识基础。C o b o l程序员和C + +程序员的交流通常十分困难,每个人都认为如
此。
然而,由于某些原因,操作系统并不能共享这种奢侈。老板们似乎期望你轻易地、不绕弯
路地在U N I X、V M S、M V S、M S - D O S和Wi n d o w s间转换。如果你擅长Wi n d o w s的某个版本,这
格外正确。但事实上,这些任务大相径庭。如果你学习过Windows 3.0下的程序设计,那你就可
以慢慢掌握Windows NT或Windows 2000,但这也是努力的结果。
如果你从其他操作系统转而使用Windows 2000,那情形会相当痛苦。作为一个U N I X的老手,
我目睹了许多朋友从U N I X转向Wi n d o w s的经历。他们通常抱怨Wi n d o w s怎么如此糟糕。
事实上, 3 2位版本的Windows 和U N I X并不是完全不同。但确有差异。丢弃原有的基础知
识并重新开始,确实非常困难。如果有人要求“创建一个新进程”,那么U N I X的权威,就会考
虑fork 而不是C r e a t e P r o c e s s。当然,你能在帮助中查到C r e a t P r o c e s s,但就像出国旅行并使用一
本愚蠢的外语常用手册,这会有几分效果,但不舒服也不方便。所以你需要一些基础知识。
本章的目的是要使每个人从相同的起点开始。这里将给大家展示应该了解的(或许已经知
道的)有关Wi n d o w s的一些内容。这样,你就增加了需要了解的书中其余部分的基本信息。
1.2 Wi n d o w s简史
如果我们回去创建公司并开辟Wi n d o w s操作系统市场,不久之后,我们也许会另谋职业。
Wi n d o w s刚面世时,是有争议的。只有M i c r o s o f t这样的公司才能支撑Wi n d o w s,直到开发出实用
的东西。
Wi n d o w s的问题不全是M i c r o s o f t的过失。不要忘记,那时的计算机只有缓慢的处理器、带
宽窄的总线及容量小得可怜的存储器。Wi n d o w s能运行就很幸运了。只是时机尚未成熟。
I n t e l 3 8 6是第一个P C兼容的处理器,它能处理真正像Wi n d o w s这样的系统。M i c r o s o f t悄悄地
推出命名为Wi n d o w s / 3 8 6的产品,且显示出它的优点。这就是Wi n d o w s的第一个版本,运行良好,
深受大多数人的喜爱。
Wi n d o w s / 3 8 6之后不久, M i c r o s o f t推出Windows 3.0。这是第一个Windows 的商业化版本。
该版本并不是没有共享问题,而是问题没有足以影响到人们购买该产品。由于潜在购买者众多,
软件公司急急忙忙进行Wi n d o w s的程序设计(通常,称作应用程序)。
而后不久,Microsoft 又发布Windows 3.1。这个版本不仅修补了Windows 3.0中的许多程序
错误,而且还引入了许多重要的新特性。许多编程人员首先学习了基于Windows 3.0 或3 . 1的
Wi n d o w s编程。
尽管用户们非常喜欢Wi n d o w s的这些版本,但多数编程人员都不喜欢它们。Wi n d o w s的这
些版本还保留了以前的版本遗留的许多包袱。如内存管理是有缺陷的、分段式存储模式常常令
人生厌、多任务处理还很粗糙并需要涉及到的所有程序协同合作完成。如果任一个程序不合作,
整个系统便会崩溃。1 6位系统的根使其对来自U N I X或其他3 2位操作系统的端口代码相当困
难。
另外,市场压力要求Wi n d o w s的应用程序,即编程人员提供的应用程序。不论好坏,这就是
大多数程序员的起点。
M i c r o s o f t也曾发布过不同版本的Windows 3.1,包括Windows for Workgroups 3.1和3 . 11、
Windows 3.11。从开发角度讲,这些并不重要。
1.2.1 Windows NT
然而,真正的举动是M i c r o s o f t决定何时开发Windows NT。因为M i c r o s o f t深信,如果不是受
以前处理器的限制,Wi n d o w s会更加出色。同时也知道,在连网和分布处理时, Wi n d o w s无法与
U N I X这样的操作系统竞争。
那时,使M i c r o s o f感到焦虑的事情是:其他操作系统或微处理器结构的问世会挤垮Wi n d o w s
和I n t e l硬件平台。M i c r o s o f t确信只有借助N T的灵活性,使其仿效其他操作系统运行于不同硬件
平台,才不致受到影响。
N T的发布可以说毫无生气。它对硬件的需求,远远超过当时人们所具有的硬件环境。在
1 6 M B内存的情况下(而那时很少有内存超过8 M B),仅仅得到最小的性能。同时,还要有C D -
R O M去安装N T,而那时极少数人有光驱(事实上,我买光驱完全是出于安装NT 的需要)。
即使已经具有N T运行所需要的硬件环境,这里仍存在两个主要的问题:缺少设备支持和缺
少应用程序。因此,当时世界上只有少数P C机运行N T,而多数仍运行Wi n d o w s 3 . 1。硬件和软件
的开发商缺少支持N T的动力。当然, N T还可运行多数Windows 3.1的程序,而不是所有的程序
都可以运行。为什么要买N T,难道只是为了运行Windows 3.1的程序吗?尤其, N T需要更多的
费用,并需要昂贵的硬件升级。
然而,M i c r o s o f t仍支撑着N T(就像当初开发Wi n d o w s产品那样),并默默地在几个版本上改
进N T。另外,M i c r o s o f t知道必须要吸引第三方开发商来编制N T的应用程序。
1.2.2 Windows 95
解决上述问题的答案是Windows 95。M i c r o s o f t推出新产品Windows 95替代Windows 4.0。
M i c r o s o f t希望将Windows 95看作Windows NT Lite(体会其特性,而不是其替代品)。然而,实
际上Windows 95更像Windows 3.1。
Windows 95的关键是其名义上支持如同Windows NT一样的编程界面。那么,为什么不编写
运行于Windows 95和N T的程序代码呢?这样会使能运行于N T上的软件大量出现,不是吗?为达
到这个目的,M i c r o s o f t所要做的是使Windows 3.1的每个用户立即购买Windows 95。这形成一些
设计选择,它们在其他方面是难于调整的。
这不仅是一个运作得非常好的战略性工作, 3 2位应用程序的数量激增。那么,已经有了N T
产品的经销商们会如何呢?这不正是为N T打开Windows 3.1市场的大好时机吗?然而事实并非如
此。Windows 95应用程序接口(Application Programming Intertace, API)只是Windows NT应用
程序接口的一个子集,如果了解了这一点,就能编写在两个平台都能运行的代码,但是除非从
开始就有打算,否则编写的代码是靠不住的。在技术上, Windows 3.1 API称作Wi n 1 6的A P I,而
其他版本则使用Wi n 3 2的A P I。不过,在Windows 95下的Wi n 3 2与在Windows NT下的Wi n 3 2并不
完全是一回事。
然而,从Windows 3.1程序转到新的Windows 95/NT API显得突然。许多程序员都趋之若骛。
对于那些仍执迷于Windows 3.1的用户,M i c r o s o f t为开发者开发了称作Wi n 3 2 S的产品。如果在程
序中使用了Wi n 3 2 S的库,则会形成一个很小的Windows NT命令子集。Wi n 3 2 S库会自动地将其转换成Windows 3.1式的命令。理论上,代码的一部分可以运行在Windows 3.1、Windows 95和Windows NT上。事实上,Wi n 3 2 S失败了。部分原因是其运行不稳定,另外的原因是实际上构成的命令子集
非常小,并对开发者的限制太多。虽然仍有少数开发者在改善Wi n 3 2 S,最终M i c r o s o f t还是放弃
了它。
总而言之,M i c r o s o f t的战略是非常成功的。也能运行于N T的Windows 95的应用程序在各地
发展起来。突然发现,人们可以在Windows NT上运行真正的3 2位程序了。
1.2.3 其他方面
与此同时,硬件方面的技术发展的也相当快。奔腾( P e n t i u m)级的处理器已成为标准,内
存的价格迅速下跌。高性能视频卡和打印机也已具有8 M B的随机存储器(Random Access
M e m o r y, RAM),许多人的P C机装上6 4 M B或更大的R A M。这些P C机足以满足运行N T的要求,
因此许多人开始转而使用NT 3.5或NT 3.51。多处理器的主板价格的下跌,更刺激人们转向使
用N T。(在多处理器系统上,运行Windows 95是一种浪费,因为Wi n 9 5任何时候都只使用一个处
理器)
I n t e r n e t的蓬勃发展更激发了人们使用N T的兴趣。N T是一个相当不错的网络服务器平台,最
重要的是M i c r o s o f t在N T服务器软件包中包含了I n t e r n e t工具。
Windows NT 3.51的唯一问题是,从用户角度看,它的外观和运作太象Windows 3.1了。许
多用户更喜欢Windows 95 的界面风格、即插即用、及仅适用于Windows 95 的其他特点。
Windows NT 4.0的发布,则带给N T用户更多这方面的东西(和一些新功能)。
对于Windows 2000,M i c r o s o f t带来更多的用户所希望的Wi n d o w s特性(如支持多台监视器),
并为编程人员新增了不少新工具。时至今日,大多数(并不是所有)的家庭用户使用Wi n d o w s
9 5或Windows 98。大多数公司用户,开发者和超级用户(也不是所有的)在运行Windows NT,
并可能转而使用Windows 2000。
1.3 Wi n d o w s版本
Wi n d o w s的每个版本都有不同的风格。多数情况,编程人员并不在意他们使用哪个版本。不
同风格间的主要区别是M i c r o s o f t在磁盘上放置的额外软件和许可证号(像协作用户所允许的号
码)。
单桌面用户的首选是专业版( Professional Edition )。它包含作为个人操作系统而使用
Wi n d o w s所需的全部功能。甚至可以用专业版构成一个小型的网络。
如果要使Wi n d o w s运行在网络或We b服务器上,则应选择服务器版( Server Edition)。根据
需要,服务器产品有几个版本(例如, Site Server提供M i c r o s o f t个人化系统和用于大容量We b站
点的工具)。
最后,Windows Advanceed Server和Datacenter Server使程序潜在地寻址更大的虚拟空间。
Wi n d o w s的这些特殊版本都提供了先进的联网特性和支持附加的多进程处理。
然而,作为编程人员,这些版本的Wi n d o w s之间没有真正的差别。可以用同样的方法,使用
同样的工具,做相同的调用。如果依靠额外的组件(或3 G B虚拟内存的限制),则也许需要特殊
的版本,但通常情况,不必在意程序必须运行于哪一种Wi n 3 2的平台上。
1.4 Wi n d o w s体系结构
从编程人员的观点, Windows 2000提供了相当简单的环境。每个程序都有一个地址范围从
0到2G B的平面(非分段式的存储结构)存储空间。当然,大部分程序并没有全部使用此空间,
的确,今天并不是所有系统都有那么充裕的硬盘空间和物理存储空间。但这不是问题,因为
Wi n d o w s只有在程序使用时才分配其真正的存储空间。
地址是从0到4 G B的3 2位整数值(尽管只有0到2GB有效)。你不必担心使用老版本的
Wi n d o w s的分段结构,跟踪内存或其他古怪的事情。
顺便指出, P e n t i u m处理器允许寻址到4 G B的空间。然而,操作系统保留了2 G B的最高限
制。操作系统真能用到那么大的空间吗?不,能够运行Wi n d o w s的处理器有2 G B的用户程序空
间和2G B的操作系统监控程序空间。因此这种模式与微处理器匹配得非常好。
Windows API是系统的标准接口。我们可以构成针对操作系统的调用命令,而操作系统实际
上寄存于系统动态链接库( Dynamic Link Libraries, DLL)。如果使用其他操作系统,这些命令
则与之不同,但是功能相同。这些功能包括分配存储空间、打开文件等等。这些特性主要被人
们用来编写程序设计语言(像C + +)。你将经常使用程序语言中的结构去实现上述功能。例如,
你大概不会调用Windows API中的C r e a t e F i l e,而会调用C + +库中的f o p e n。另一方面,C r e a t e F i l e
可以实现f o p e n不能处理的某些功能。
如果你有先验的Wi n d o w s编程经验,就会发现这些函数的命令有几分相似。许多函数与
Windows 3.1及更早的版本中的调用函数相同。而另一些有些细微的差别。有时, M i c r o s o f t稍微
改变命令的名字,以表示其参数或返回值存在不同。对名字的常见改动是在其尾部加上E x。这
样,在Windows 3.1 中使用G e t C u r r e n t P o s i t i o n,而在Windows 2000 (或N T)中则使用
G e t C u r r e n t P o s i t i o n E x 。它们有什么不同呢?当两个1 6位字表示的3 2位字返回值时,
G e t C u r r e n t P o s i t i o n返回当前描述的位置。而G e t C u r r e n t P o s i t i o n E x则返回作为变元传送的指针结
构中的两个3 2位字。这些函数有相同的目的,而Wi n 3 2变量则以3 2位运作。
另外的改变发生在接收字符串的任一函数中。N T和Wi n 2 0 0 0内部不使用A N S I字符串。取而
代之,它们使用称作U N I C O D E的1 6位的字符(且后者使用的更多)。然而,大多数程序使用
A N S I(或考虑8位的A S C I I字符)。所以接收字符串每个函数有两种情况:其一给出A N S I字符串
(并将其转换为U N I C O D E),另一种则直接接收U N I C O D E字符串。
例如,考虑Windows 3.1的函数M e s s a g e B o x。大家知道,这个函数弹出带文本字符串的对话
框,Wi n 3 2没有此函数。但它有M e s s a g e B o x A给出A N S I变元及M e s s a g e B o x W处理U N I C O D E。
然而,文档也引用M e s s a g e B o x。这是因为C + +编译器和许多其他工具根据涉及U N I C O D E的优
先权,来选择正确的结果。在C + +程序中,当引用M e s s a g e B o x时,根据对编译器的指令,使用
的扩展为M e s s a g e B o x A和M e s s a g e B o x W宏。当然,这意味着编译器的错误信息涉及到哪个函数
会被拒绝,例如M e s s a g e B o x A。
1.4.1 实际情况
上节讨论Wi n d o w s体系结构,本节内容却很少。事实上,这些显示属于Wi n d o w s的程序是虚
假的。Windows 2000 (和Windows NT)实际上并不用Win32 API运作。而使用A P I s特殊集,其中
包含执行程序(E x e c u t i v e)。M i c r o s o f t并没有公开执行程序的文档,但确是基本集, M i c r o s o f t及
其授权用户能够用来构建操作系统的A P I。这些执行程序执行所有运作。
然而,用户不直接调用执行程序,而是调用一个子系统。该子系统提供个性化的程序。例
如,Wi n 3 2子系统执行如G e t C u r r e n t P o s i t i o n E x和M e s s a g e B o x A函数,并调用使其工作的执行程
序。还有其他子系统(比如,提出贫乏P O S I X的子系统)。
NT 4以前,Wi n 3 2子系统就像其他子系统一样是一个单独的模块。然而,当NT 4和Wi n d o w s
2 0 0 0面世时,Microsoft 意识到几乎所有程序实际上都要用Wi n 3 2子系统。因此,现在的N T版本
中,Wi n 3 2子系统实际上是执行程序的一部分。对应用程序编程人员来说,这没什么差别,只是
其速度更快些。
执行程序之下是硬件分离层( Hardware Abstraction Layer, HAL)程序。这段软件甚至被普
通编程人员进一步移除。这段代码知道如何执行Wi n d o w s所需的所有机器特定的任务。所以如
果你想将Wi n d o w s输送到其他类型的计算机中,你只需编译Wi n d o w s代码和编写客户的H A L。当
然,为了获得所有代码和文档,就必须得到M i c r o s o f t的同意,因为M i c r o s o f t不公开支持H A L
(或Wi n d o w s源代码)。对于单处理器或多处理器,存在不同的H A L。对于不同的机器体系结构,
如在Power PC中也会找到H A L。
1.4.2 Wi n 9 5与Wi n 9 8
Windows 95和Windows 98(合起来,称Windows 9x)如何适于本形象描述呢?如前所述,
Windows 9x更象Windows 3.1而不象Windows NT Lite 。尽管在Windows 95中提供了大部分
Win32 API命令,但它们中的大多数并没有比Wi n 1 6副本功能强多少。通常,也有例外,如果在
Wi n 1 6中都做不到,那在Windows 95或9 8中也无法实现。
起初,这看起来不可思议。为什么新技术业已成熟时( Windows NT 已研发成功),
M i c r o s o f t仍然发布基于旧技术新产品呢?答案就是市场。Windows 95的一个关键目标是鞭策开
发3 2位的程序。由此,几乎所有Windows 3.1 的用户不得不转而使用Windows 95 。否则,
Windows 95会成为另一个Windows NT一样的牺牲品—没有应用软件的强大系统。
M i c r o s o f t编写Wi n d o w s - O n - Windows (WOW)模块,以使Windows NT运行Windows 3.1的程
序,并令人惊喜。许多重要问题利用Win16 API出现的缺陷和未公布的特性。这些程序在W O W
情况下运行时会失败, W O W是根据文档运转的。
使W O W真正成为Win16 API是棘手的,所以M i c r o s o f t的回答是保留W O W以使它们做应该
做的事。如果程序无法运行,就是存在缺陷。这意味着一些Windows 3.1程序无法运行在N T上。
对Windows 95来说,没人准备采用W O W。家庭用户不大可能更新他们的机器,即使他们也
怀疑一些程序后来无法运行。于是M i c r o s o f t需要把Wi n 1 6子系统复制到Windows 95中。
Microsoft 采用显而易见的解决办法:采用现有的Wi n 1 6子系统。这保证所有现存的Wi n d o w s
3 . 1软件实际上运行在Windows 95下,还能使老的Windows 3.1驱动程序能工作。M i c r o s o f t重写了
完全使用3 2位代码的程序部分,但仅仅是一小部分(特别是字体翻译和内存管理)。
那么,Windows 9x作为3 2位系统会怎么样?由于它包含Win32 API,它将Wi n 3 2命令转换成
Wi n 1 6命令(更象走厄运的Wi n 3 2 S产品)。由于这种措施,使许多N T特性不存在于Windows 9x。
其他特性在Windows 95和N T中也不同。为了适应Wi n 1 6程序,内存设置也有所不同。
该书不是讲述Windows 95 (或延伸到Windows 98 )的。然而,它有助于理解如何适合
Wi n d o w s 2 0 0 0。不要在意有关Windows 95/98的内容,但要了解来自N T家族的显著差别。
1.5 Wi n d o w s的特性与差别
如果用惯了早期版本的Wi n d o w s,或不同类的操作系统,就会发现有关Wi n d o w s 2 0 0 0的奇怪
特性。你也不必为此惊叹不已,而需要逐渐习惯之。当然,我们没有预先安排,只是阐述
Windows 2000是如何工作的。
1.5.1 多任务处理与线程处理
如果使用过U N I X,再使用Windows 2000的多任务处理,也许会有失败感。理由是U N I X
(或其他操作系统)虽有处理多任务的不同方式,但其许多术语是相同的。
在Windows 2000下运行程序时,可以创建进程,这并不奇怪。令人惊奇的是操作系统没有
运行进程的进度表。进程拥有打开文件、内存或其他资源,同时还拥有一个线程。
什么是线程呢?线程( t h r e a d)就是一组处理器寄存器的记录。它了解执行到代码的什么地
方,局部变量放在什么地方。Wi n d o w s安排线程的进度,就好象两次交谈。安排线程进度与安
排进程进度有什么区别呢?那就是每个进程只有一个线程。
然而,设想主线程又创建另一个线程会如何呢?而该线程也许又创建更多的线程。重要的
是Wi n d o w s安排线程进度,而不是安排进程进度。连同来自其他进程的线程,每个线程轮流运
行。
那么,线程和进程有什么差别呢?很简单属于一个进程的所有线程容易进行通信,因为它
们共享内存和其他资源,而属于不同进程的线程不易共享数据或通信。的确,它们可以通信,
但需要进程间通信的方法。
1. 派生
对于以前的U N I X编程人员,另外的困难是缺少派生( f o r k)构造。在U N I X中,派生命令复
制当前进程。两个进程相同,每一个都有一个程序数据的私有拷贝。通常,派生之后,就调用
执行( e x e c )。这将一个新文件装载到一个新进程中,去除原进程的拷贝。对Wi n 3 2 的
C r e a t e P r o c e s s函数来说,作为单个命令,派生与执行是相同的。
但是如果想要复制进程及保存两个复制品时,又会如何? Wi n d o w s不易实现这些。这为什么
是重要的?假设你有一个程序,试图优化塑料球生产进度。这是个实例,因为以特殊顺序生产
塑料球会更经济。换句话说,假设必须生产A、B、C三种塑料制品,以满足订单。如果以从A到
B再到C的顺序生产,将浪费两吨塑料。为什么呢?因为每次都要改变生产的塑料制品,在生产
进程中存在化学混合物残渣,将造成一些浪费。但是当B类制品与A类制品更相似,A类制品与C
类制品没有太大的化学性质上的差别时,情况又怎样呢?那么,当先生产B制品,然后生产A制
品,再生产C制品时,也许只浪费5 0 0磅。
对于这个假设的程序,第一步是阅读所有的顺序及当前产品清单,并使之相互匹配。其余
的事就是必须自己加工。现在,你的程序想要尝试几种优化的策略。每次尝试一个策略,与之
不同U N I X程序可以派生多次,为每个优化方法创建新进程。每个新进程得到一个在第一次运行
时搜集到的数据的私有拷贝。最后,主进程将检验结果,并决定哪种方法最好。
在Windows 2000 中,这将很难做到。你可以使用C r e a t e P r o c e s s开始一个新进程,但是默认
情况下将没有和老程序共有的数据。的确,可以使用共享的存储区域,甚至一个文件,但这样
也不那么简单。
使用线程会怎样呢?这也是一种可能。然而,如果优化方式改变了数据,就不可行了,因
为所有线程共享相同的变量。以派生为例,每一步运行都自动地拥有一个私有数据拷贝。在不
影响相关进程的情况下,每个进程都将改变变量。
有趣的是,由于对于P O S I X子系统是可以做到的执行程序有进行派生的能力。然而没有办
法访问Wi n 3 2的派生处理。请注意:朗讯技术公司的David Korn 已有支持Win32 ( U/Win ) 的
P O S I X库(见w w w. r e s e a r c h . a t t . c o m / s w / t o o l s / u w i n /),其余的就是执行派生。据悉,该库运用
C r e a t e P r o c e s s联合特殊的启动代码,它复制使用进程间通信方法的所有数据。听起来也许有许多
工作,但实际上运行得相当不错。
2. Windows NT与Windows 2000 对Windows 3.1
即使熟悉Windows 3.1,也必须要重新考虑多任务处理。当然, Windows 3.1中没有线程,所
运行的每个程序都是一个进程。由于进程都共享内存和Windows 3.1下的其他资源,它们其实更
像Wi n d o w s线程。
另外的问题是程序如何放弃处理器的控制。在Windows 3.1下,程序执行到它们调用少数系
统函数(GetMessage , PeekMessage或Yi e l d)之一为止。此时,Wi n d o w s会选择中止该程序并恢
复执行另一个。这种相互协作的多任务处理的技术简单地执行和释放程序,而编程人员不必担
心死锁或资源竞争。只要不调用任何特殊函数,就不必担心其他程序抢占正在使用的资源。
这种方案的缺点是,如果程序不调用这些特殊函数,系统就死锁。这就是为什么Wi n d o w s
3 . 1中没有S l e e p命令的原因。如果程序没有进程消息而暂停的话,系统的其他部分就会挂起。
Wi n d o w s使用抢先的多任务处理。还可能通过执行某些命令,使自己睡眠。然而,如果不执
行这些命令,Wi n d o w s最终总置于睡眠状态。
这是好事,对不对?大多数情况下是如此。它防止由于运行错误程序而死机。也使系统对
用户做出更快的响应。但对编程人员来说,这增加了额外的负担。假设有几个程序共享一个错
误登录文件。在Wi n d o w s下,可以打开该文件,改写数据并关闭它。只要没有调用任何特殊的
系统命令,就会知道没有其他程序打开该文件(由于没有其他程序能运行)。
Windows 2000就没有这么简单。你可以打开文件,然后放弃(不知不觉地)对另外线程的
控制。如果有线程试图打开相同的文件,会发生什么呢?每个线程都不能打开文件,或以共享
方式打开文件(这不是所希望的)。即便Wi n d o w s没有抢先,也可能有另外的线程执行其他进程,
而引起相同问题。
对于许多操作系统(如U N I X)来说,这是个老问题,其解决方法并不复杂。Wi n d o w s提供
同步对象(见第4章),它能协助解决这类问题。你需要明白,这类问题总会发生。
1.5.2 UNICODE
默认情况下, Windows 2000 不使用A N S I字符,而所有内部字符串都是U N I C O D E。
U N I C O D E是一个1 6位的字符序列(不象A N S I使用8位)。在额外的空间下, U N I C O D E可以容纳
不同的符号和非英语字符。
当然,大多数编程人员(至少在美国)仍使用A N S I。如果你愿意, Wi n d o w s也允许使用
A N S I(至少,多数情况如此)。其技巧是每个接收或返回字符串A P I函数实际上是两个函数:一
个配合U N I C O D E,另一个配合A N S I。当你设置方案时,开发工具(如Visual C++)通常允许你
选择使用哪一个函数。
假定在C + +程序中用到命令M e s s a g e B o x。其编译器根据是否用U N I C O D E模式,定义符号。
头文件为M e s s a g e B o x定义一个宏,并将其扩展为A N S I编译的M e s s a g e B o x A及U N I C O D E编译的
M e s s a g e B o x W。当错误信息和调试输出引用M e s s a g e B o x A时,这将导致混乱。
若要将字符串从A N S I 转换成U N I C O D E ,可以使用Wi d e C h a r To M u l t i B y t e 和
M u l t i B y t e To Wi d e C h a r函数。当然, U N I C O D E字符串并不能完全无误地转换为A N S I字符串,且
Wi n d o w s可能替代一些A N S I不能表示的一些字符。
1.5.3 文件系统问题
Windows 2000利用可安装的文件系统技术。这意味着在同一台机器上,也许会涉及到不同
的文件系统。可能同时在一个硬盘驱动器上使用V FAT(Windows 95的兼容系统),而在另一个
硬盘驱动器上使用N T F S(Wi n d o w s文件系统所选择的)。
通常,只要不设定文件的名字,这些问题就不会困扰你。例如,设定文件的名字包含1个到
8个字符、1个句点、及1个到3个字符,大概会出错。如果将文件的名字视作神秘的、不可知的
字符串,也许就不存在任何问题。
在NT 4以前,可以安装H P F S(O S / 2方式)的文件系统。NT 4撤消了对H P F S正式支持,虽
然在I n t e r n e t上的报告指明,如果需要的话,也可以从NT 3.15安装盘安装之。
值得注意的是, N T F S并不能在软磁盘上运行。NT 3意味着在软磁盘上不能有长文件名。
NT 4和Wi n 2 0 0 0支持V FAT( Windows 95文件系统),它支持长文件名,因此可以在软磁盘上使
用V FAT。然而,仍没有得到N T F S所提供的其他额外特性。
这些特性是什么呢? N T F S是安全的基础。在FAT和V FAT磁盘上不能保护文件的安全。到底
为什么还要FAT和V FAT呢?因为软磁盘很重要,且不能格式化一个N T F S软磁盘(这是不公开的,
如果你真想这样做的话,请见w w w. n t s e c r e t s . c o m)。还有,许多用户会在同一台机器上安装
Windows 95、Windows NT和Windows 2000。如果没有FAT和V FAT,将不能在两个安装程序间
共享硬盘(还有,不公开地, w w w. n t s e c r e t s . c o m具有针对Windows 95的只读N T F S驱动程序)。
你要在N T F S下运行Wi n d o w s,则所有情况都一样。然而,你不能指望它。一些用户正在使
用V FAT或甚至使用H P F S。即然没有临时的本地硬盘,就别指望能从网上发现什么。关键是假
定你对文件名象什么知道的不多。
1.5.4 DLL
动态链接库(Dynamic Link Libraries, DLL)是Wi n d o w s的构架。每次调用系统命令,都将
调用M i c r o s o f t提供的D L L。这使你能够使用代码库,而实际上在链接时程序中并不包含它们。
这有三个优点:使程序更小、不同的程序能共享相同的库、库的更新不需要与程序重新链
接。当然,也可以创建自己的D L L并使用它们。
Windows 3.1 的D L L有许多Win2000 DLL 不能共享的特性。不同于Windows 3.1,Wi n 2 0 0 0
调用D L L中函数,以提示进程何时装载D L L及何时释放D L L。使用D L L为每个进程分配资源是
十分简单的。
当多于一个进程使用一个D L L时,D L L能够专为每个进程设置某个变量,或在进程之间共
享共同的变量。这考虑到共享存储器的未加工的形式,且如果需要在进程间进行通信,是相当
有用的。
当Wi n d o w s试图将D L L装载到程序进程空间时,首先检测是否为其他进程装载了相同的D L L。
若有,Wi n d o w s将简单地尝试将D L L所在相同的内存映射到两个进程中(当然,除去私有的数据
内存)。只有当第二个进程有空闲内存,而在同一地址,第一个进程在使用D L L的情况下,这样
才有效。
如果第二个进程正在使用一些所需要的内存,会怎么样呢?这时, Wi n d o w s必须重新装载
D L L(除非共享内存页)。这意味着两个使用相同D L L的进程,也许对D L L的地址的意见不一致。
这不是个一般问题,如果在共享内存中使用D L L,就会有问题。最后一行:不要将指针指向共
享内存。相反,考虑使用从共享内存首地址起的偏移量。我们将在第6章学习到有关该题目更多
的内容。
1.6 开发工具
Wi n d o w s当然没有开发工具方面的不足。当然, Wi n d o w s的多数开发者的材料都有C + +编程
人员的功劳,这本书也不例外。即使你不打算使用C + +,但几乎所有M i c r o s o f t文档都是用C + +写
的(至少在用其他语言前,都是用C + +),因此,你要逐渐熟悉它。
然而,当然也有许多可行的Wi n d o w s开发平台。例如, J a v a目前很流行。B o r l a n d公司有
Delphi, 可视化的Pascal (及C++ Builder, 它以同样的方式使用C + + )。经常被忽略的语言是Vi s u a l
Basic (VB)。V B显示出真正的编译器,强大的A c t i v e X支持,可以毫不费力地调用A P I。
然而还有不少其他的开发工具,而C + +、J a v a和V B当然是主要的开发工具,可能用来在底
层开发Wi n d o w s。本书中的示例几乎都是用C + +编写的,但本章中的直接解答部分将展示如何从
这三种语言中调用A P I的方法。
结束语
本章展示了寓于Wi n d o w s的一些概念,以及与所熟悉的其他操作系统之间的差别。当你想要
建立知识的基准时,请记住,Wi n d o w s不同于你所熟悉的操作系统,不要拒绝它。
在以下各章中,你会阅读到Wi n d o w s的更多内容,包括本章中主题内容的展开。读完本书,
你自然会接受Wi n 2 0 0 0,信不信由你!
1.7 直接解答
1.7.1 开发工具的选择
当阅读本书有关Wi n d o w s详细内容时,大概会比其他事情更多地涉及到C + +。另一方面,许
多编程人员已经转而使用J a v a,对于大多数设计项目来说,它也是可取的选择。然而,本书中的
大多数示例都是带有类库( M F C)的C + +程序,因为现在看来大多数C + +编程人员总是使用
M F C。你应当可以读懂这些示例(包括程序清单和附录),尽管有些小困难。图形化的用户界面
是不重要的,在那里,可以看到许多写成控制台应用程序的例子。控制台应用程序允许用户编
写C或C + +的代码,它们都带有m a i n函数和标准I / O函数。
另一个令人感兴趣的语言的选择是B o r l a n d公司的Delphi ( 或相关的C++ Builder)。这个编程
环境是高度图形化的,并且完全能够编写底层代码和调用Pascal (Delphi)或C + +(C++ Builder)
语言中的A P I。
尽管Visual Basic很长时间被系统编程人员轻视,但VB6 也是个不错的选择。如果需要使用
A c t i v e X,选择V B 6尤其正确,因为V B 6在构成A c t i v e X方面做了大量的工作。V B 6还提供本地机
的代码编译,并消除了以前V B版本性能上的许多缺陷。你可以容易地调用多数A P I。其主要缺
点是V B程序需要相当大的运行时间库。这也是M F C程序的缺点,但这并不能阻止人们使用
M F C。
不论选择何种语言,都需要能懂构成Wi n d o w s标记的原理。那是本书所讨论内容的真正目的,
尽管多数示例是用C + +语言描述的。
1.7.2 Windows 的体系结构
对应用程序编程人员来说,Wi n d o w s为每个程序提供2 G B的平面虚拟地址空间(有些版本是
3 G B)。通常使用规定的3 2位指针,它可以从0寻址到2 G B。
面向操作系统的命令都保存在动态链接库中( D L L)。这些命令将你的需求传送给Wi n 3 2子
系统,该子系统转而控制Wi n d o w s执行程序,这些执行程序( E x e c u t i v e)则完成实际工作。通
过该点,执行程序则在硬件抽象层( H A L)进一步调用处理操作的机器规定部分。
1.7.3 理解进程
很重要的一点是,不要混淆Wi n d o w s进程和其他操作系统的进程概念。在Wi n 2 0 0 0中,一个
进程就相当于一个容器。那容器又包含什么呢?存储空间、打开文件、资源和线程。线程实际
就是个执行单元,Wi n 2 0 0 0安排线程进度,但不安排进程进度。
一个进程开始于一个线程,该线程能够创建更多的线程(且这些线程又可以创建更多的线
程)。一个进程里的线程共享该进程中的所有东西。它们具有的唯一性是程序中的执行点,以及
在进程地址空间中局部变量的位置。
存在于不同进程中的线程不共享什么资源,除非通过使用规定的进程间通信技术,使它们
共享资源。
1.7.4 从C + +中调用A P I
从多数C + +环境(包括Visual C++),调用A P I相当简单。只要确信包含适当的头文件(通常
是W I N D O W S . H)及有正确的库(通过在线帮助,就可以找到所需要的库)即可。于是就实现
调用。A P I都被设计成符合C / C + +环境。
由于所应用的环境已经设置好一切,使我们可以轻松地调用A P I,所以通常我们无需考虑那
么多。如果你在使用如M F C一样的库,就需要包含如W I N D O W S . H(或其他文件)的库头文件。
要记住,根据在使用A N S I还是在使用U N I C O D E,编译器会在后台改变许多A P I的名字,以
及它们如何做相应的改变。例如, M e s s a g e B o x命令改变为针对A N S I编译的M e s s a g e B o x A及针对
U N I C O D E的M e s s a g e B o x W。当查看映象文件、调试信息、或编译器错误信息时,就可以见到这
些名字。
顺便说一下,从D e l p h i中调用A P I同样很容易。只要包括正确的单元,及用相应的库链接即
可。还有,这些总是设置好的。
1.7.5 从V B中调用A P I
尽管V B通常不调用A P I,但可以利用一些手段来实现V B中调用A P I。诀窍是V B几乎可以调
用D L L中的任何一个函数。由于Wi n d o w s的A P I就是D L L的集合,所以V B能够构成A P I命令。
因为A P I很大,每次运行任何程序的时候,为了了解每个A P I命令,都会降低V B的编程速度。
然而,多数程序仅仅使用少数命令。M i c r o s o f t提供了A P I文本阅读器( API Text Vi e w e r )。使用该
阅读器,可以选取感兴趣的命令,并且该浏览器将它们作为说明( D e c l a r e)报告输送到所选择
的文件中去。这些说明报告使调用A P I成为可能。
这很方便,但也存在一些问题。阅读器并不了解它们之间依赖关系。根据创建输出文件过
程,当试图运行程序时,可能会出错。例如,在下面的语句中,会得到一个错误:
Public Const KEY_EXECUTE = (KEY_READ)
检查文件,就会发现K E Y _ R E A D是在文件的后面部分定义的。因此,你必须手动地将
K E Y _ R E A D的定义向前移动,以使它处于K E Y _ E X E C U T E之前。
另一个问题很类似。仅仅为了找出基于所需要输出的内容,你可以输出一个函数或标识符
(如上述示例中的K E Y _ R E A D)。一旦确定,就有可能发现还需要增加其他项。这样继续下去,
直到挖掘出所有的依赖关系。
1.7.6 从Visual J++中调用A P I
从Visual J++中调用A P I很有挑战性,因为你必须使用J a v e的本机程序界面,它也很棘手。
12 Windows 2000系统编程
然而,M i c r o s o f t的J / D i r e c t却使其变得相当容易。
J / D i r e c t包括两个部分。第一部分可使你调用D L L s中的任意函数而不会有麻烦,第二部分是
调用通用Win32 API的简化方法。
利用第一部分,使用如下J a v a代码的特殊的注释和程序行,能够定义A N S I的M e s s a g e B o x函
数:
/** @d11.import ( "U S E R 3 2") */
private static native int MessageBox ( int w,String msg,String title,int flags );
还能够调用U N I C O D E版本,如:
/** @d11.import ( "U S E R 3 2",unicode) */
private static native int MessageBox ( int w ,String msg,String title,int flags );
当然,不想对整个A P I编写这些语句,所以M i c r o s o f t应用几个类来包装整个A P I。你也许会
如下面语句,来调用M e s s a g e B o x:
com.ms.win32.User32.MessageBox ( 0 ,"H i","Java Message Box ",0 );
这没什么麻烦,U s e r 3 2类照顾所有数据类型的转换和D L L的输入。
1.7.7 Internet资源
这里,给出查阅资料的U R L:
w w w. a l - w i l l i a m s . c o m—作者的站点,每月都会有各种M F C的提示及关于本书的更新内容。
w w w. s y s i n t e r n a l s . c o m—是寻找N T和Windows 2000系统应用程序的网址。
w w w. z d n e t . c o m / p c c o m p / f e a t u r e s / f e a 0 7 9 7 / n t / w e l c o m e . h t m l—来自PC Computing杂志,题目
为“NT Lies”的有趣文章。
(文章整理中待续......)