Windows 版 (精华区)
发信人: bali (阿奔), 信区: Windows
标 题: Microsoft Windows 2000 应用程序兼容性(4)
发信站: 紫 丁 香 (Fri May 5 23:35:15 2000), 转信
长文件名和长打印机名
自从 Windows 95 问世之后,我们便一直在谈论长文件名及打印机名。最初,我
们仅仅是要求应用程序能够支持这两者;在升级到 Windows 2000 后,我们要求
程序能够正确地支持它们。我们发现在许多地方,应用程序并没有实现对长文件
名的正确支持。但这并不是说这些应用程序对它们一点都不支持(尽管有少数的
确如此),而是我们发现了应用程序在支持长文件名方面存在一些错误。例如,
有一个应用程序,声称为实现对长文件名的支持,为全部 256 个字符提供了一个
缓冲区。但是当我们将文件移走,并为应用程序提供了一个指向所寻找文件的较
长路径(大约有 50 个字符)时,程序崩溃了。这表明尽管该应用程序告诉我们
它拥有一个长缓冲区,而实际上只提供给我们一个较短的缓冲区。这只是应用程
序中的一个简单的错误;由于我们将“My Documents”文件夹转移到了“Docume
nts and Settings”,而不是将其置于根目录或 Windows 系统目录下,您会经常
遇到这类错误。路径有越变越长的趋势,现在的平均路径长度是 60 到 70 字符
,而不再是 30 到 40 字符。长路径名的使用正在暴露出越来越多的错误。
我们在“Documents and Settings”文件夹方面发现的另一个问题是“Document
s and Settings”这种写法造成了许多应用程序无法正常访问到它。应用程序会
对目录进行分析,只要发现“Documents”一词,程序就会认为到了“My Docume
nts”的结尾。这样,应用程序会中断,同时认为“我发现了‘Documents’一词
,我已经找到‘My Documents’了”。这当然行不通。
一定要全面检查长文件名支持功能,并进行测试。您会在 Windows 2000 应用程
序规范(http://msdn.microsoft.com/certification/appspec.asp(英文))中
发现一个相当长的字符串,可以使用它来确认应用程序是否可以正确地支持长文
件名。
堆管理
另一个应用程序稳定性问题源于在 Windows NT 平台上对堆管理所进行的改动。
这是我在这儿提到的最使人震惊的问题,它有极大的危险性,可以导致您的应用
程序出现各种问题。它实际上与我们在旧版 C 语言中常常遇到的问题相同:出现
了指针错误或内存使用错误。但它是很难处理的问题之一。
实际上这一问题起始于 Windows NT 4.0,Service Pack 4。我们对堆管理器进行
了某些改动,目的是使它效率更高、运行更快,特别是针对拥有多处理器的计算
机。Windows 2000 在此基础上又进行了一些更改,所以,能够在 Windows NT 4
.0 上运行的程序,在安装 Service Pack 4 后无法运行。或者在 Service Pack
4 下可以运行的程序,在 Windows 2000 下却崩溃了,这是因为我们已经对堆管
理器的工作方式作过很多改进。很明显,如果您要提高系统性能,并加快堆管理
器的运行速度,可做的事情还是很多的。我们并未对 API 本身作出改动,也没有
对堆的工作方式进行逻辑上的改动,但是我们对块的重复利用方式进行了一些微
妙的改变,从而使应用程序中的错误暴露出来。
简而言之,我们所作的改动如下所示:以前,当一个块被释放出来,它将被列入
未应用的空块列表,位于表的末尾,最后将被筛选出来再一次利用。现在,我们
将缓存最后一次被使用的块,并把它们放在表的顶端,当您需要另一个块时,您
更可能首先调回这一个块。如果您需要调回一个块,又生成了一个空块,您将会
调回刚刚使用过的块,这样可以使自己保持在同一页,并且提高系统整个工作方
式的速度。
这就是我们从一开始便在 C 语言设计及 C++ 程序开发中遇到的问题。实际上并
没有更好地发现这些问题的途径。您可以利用一些商业工具去找出这些问题。除
此之外,您需要在 Windows 2000 上尽可能彻底地测试这些软件。
我们发现许多应用程序都存在堆问题,最普遍的一个问题是试图访问已经释放的
内存。结果是应用程序将进行分配,它将对这些块进行读写操作,然后释放这些
块,而后又对这些块进行读写操作。这种做法的危险之处是会导致数据的破坏,
您的应用程序就会崩溃。但是因为它驻留在已归您支配的某一块或页中,并且因
为您正在读写允许您进行写操作的块,就不会导致存取侵犯,而是导致数据的破
坏。但愿导致系统崩溃,因为比较而言这种情况更容易补救,但这也很难说,任
何一种情况都可能发生。
另一出现问题的情况是:为了使某一页的空间得到更充分的利用,在您已经重分
配了一个较小的块的情况下,Windows 2000 以及 Service Pack 4 还可能会移动
这种分配区。很多开发人员持有一种受怀疑的优化观点,“如果我重新分配一个
比我原先指定的块更小的块,将没有人可以把指针指向我,我就可以进行重分配
,并相信不再移动的指针。因此只要我使它更小,就没有人可以移动它”。这样
将会导致全面错误。
调用规则
下面讨论调用规则问题。资料上说,您必须为所有窗口过程使用 STDCALL。不幸
的是,我们发现许多应用程序并没有为它的窗口过程使用 STDCALL,对话框过程
也是如此。如果您不使用 STDCALL,您的程序可能无法正常工作。在 Windows 9
5 及 Windows 98 中,您可以通过使用 C_DECL 规则而避开这一问题;换句话说
,如果您仅仅忘记在窗口过程中置入 C_DECL 规则,系统不会崩溃。
我们在 Windows 2000 中为此设置了功能区,因此我们可以尽可能地捕获它们。
然而就在上一星期,我还是发现了一个错误,它跳出来对我说,“是的,我们已
经使这些处理程序就位,并且,我们试着自己处理标准调用,但是因为应用程序
没有使用标准调用,我们仍然没能正确截获它们。”如果您的窗口过程使用 STD
CALL 而不是其他调用规则的话,事情便会简单得多。
我们也发现有些应用程序采用了正确的调用规则,却没有正确实施,或者是编译
器中的错误显示出没有正确使用调用规则,或应用程序较快进入注册过程。因为
我们正在进行进一步的优化,目的是更严格地使用注册内容并且使它占用资源更
少、运行更快,因而,我们遇到过许多因未能严格遵循规则而导致应用程序不兼
容的情况。
非缓冲文件的文件 I/O
如果您希望不使用系统提供的缓冲区处理一些文件 I/O,则需要使用 Create Fi
le FILE_FLAG_NO_BUFFERING 上的标志(意思是,您自己提供缓冲区,而不使用
系统为这些读写操作而分配的缓冲区)。您必须确认传递给 ReadFile 和 Write
File API 的缓冲区已针对设备进行了正确对齐。这些与以前没有什么区别,但是
,我们发现,这些对齐方式在 Windows 2000 上稍微有些差别,特别在对新型 U
ltra 66 IDE 驱动器等新设备的支持方面。所以,您必须确认分配缓冲区的方式
是否正确。实现这一目的的最简便方式是使用 VirtualAlloc。VirtualAlloc 将
始终将缓冲区按偶边界对齐,因此,无论设备完成文件 I/O 所需的缓冲区大小是
多少,它总可以正确对齐。记住,当您进行这些操作的时候,您必须确认您的读
写操作时使用的是 I/O 设备实际扇区大小的若干倍。您可以通过 GetDiskFreeS
pace 得到扇区大小,并且确保您仅分配和读取这些扇区大小的数倍。
大型驱动器
另一个与驱动器有关的问题是某些大型驱动器所带来的。显然,目前的驱动器要
比 4GB 大得多,一般来说,其中的可用空间也要比 4GB 多得多。然而如果您使
用 GetDiskFreeSpace,它将返回一串 32 位数值,该数值并不是准确值,因为实
际数值要比之多得多,这种情况会在很多地方出现。我们曾发现有的应用程序返
回的磁盘空间的数量是负值,由此出现了各种各样的问题。
您需要应用 GetDiskFreeSpaceEx,它采用了 ULARGE_INTEGER_ 代替了INT,因此
能够为您提供可用空间的真实数据。
打开另一个 HKEY_CURRENT_USER
有些时候有些应用程序(尤其是某些服务器应用程序),需要从其他 HKEY_CURR
ENT_USER 而不是它们最初或当前所使用的 HKEY_CURRENT_USER 中获取信息。问
题在于 HKEY_CURRENT_USER 实际所采用的缓存方式是建立在每一进程的基础上。
应用程序会试图关闭 HKEY_CURRENT_USER,模拟新的用户,然后打开 HKEY_CURR
ENT_USER,并希望他们获取的是正确的 HKEY_CURRENT_USER。问题是如果使用多
个线程进行这一操作,其中的一个线程可能已经完成,而另一个线程可能正处于
操作过程中。您无法搞清楚结束的是哪一个 HKEY_CURRENT_USER,因为第二次打
开时系统会声明“我已打开了 HKEY_CURRENT_USER,我用的就是缓存中的那一个
。”这种操作具有相当的危险性。为了改善这一状况,我们增加了一个新的、名
为 RegOpenCurrentUser 的 API,有了该 API,您能够正确地实施模拟过程,以
便获取真正需要的 HKEY_CURRENT_USER。
检查位 (Bit) 标志
另一个低级 C 类问题:我们已发现有的应用程序在对位标志进行检查时,使用的
是等式运算符,而不是实际检查某一特定位是否存在。我们会在 Windows 2000
以及 Windows 2000 以后的所有版本中添加标志,因此您需要确保检查的是位,
而不是是否相等。我们已添加了不带焦点值和加速值的所有者图形,以使其带有
不同类型的绘图参数。以下是检查位标志的方式:
if (fItemState & ODS_FOCUS)
消息的顺序
我们一直在告诫开发人员不要使用消息的顺序或依靠应用程序接收消息的顺序去
表达某种特殊含义。这是靠不住的。我们甚至发现有些应用程序在某些多线程应
用中依赖消息的顺序。例如,有可能会发生下述情况:有一个线程关闭,然后向
主消息循环发出一条信息,然后另一线程关闭,也发出一条消息。应用程序可能
会将消息发出的顺序作为线程关闭的顺序。我们已对线程计划程序的操作方式进
行了改动。理想情况下,它们完成了消息的发布,会依次将消息送入队列,以便
进行处理,但实际情况并非如此。如果您尝试一下,会发现它们发出消息的顺序
与线程关闭的顺序不一致,由此,就会发生各种问题。再次强调,如果您需要依
靠消息的顺序(尤其是在跨线程的情况下),您不要想当然地认为消息本身就是
进行同步的方式,而要增加自己的同步机制。
多个监视器
从 Windows 98 开始,Windows 具有了处理多个监视器的能力。Windows 2000 是
第一个具有这种能力的、基于 Windows NT 的平台。由此出了一个大问题:您必
须确认您的应用程序能正确处理负坐标和超大坐标或表现为超大坐标的情况。如
果设置了多个监视器,而主监视器在副监视器的右侧,则副监视器将完全处于负
坐标区域。如果您的应用程序调出一个应位于副监视器的窗口,而应用程序希望
将窗口最大化,假设应用程序无法正确处理多监视器系统,它会因当前坐标完全
为负而将窗口整个移动到主显示器中。这种情况是不应该发生的。如果您需要定
位窗口,则一定要在多显示器系统中对应用程序进行测试,确认一定能正确处理
这些负坐标。对于超大正向坐标也应如此处理。您需要采用图中所示的新的系统
目标,确保将窗口置于它应处的位置。
Windows 平台之间的差异
我将讨论最后一类问题(与其他一些问题相比,这一问题要简要得多):Window
s 平台的基本差异。Windows 2000 是以 Winsows NT 为基础的。我们认定大量用
户会将他们的 Windows 9x 升级到 Windows 2000。我们一直在测试一些应用程序
,将它们从 Windows 9x 移到 Windows 2000 平台。您将在杂志文章和 SDK 中发
现关于这个问题的大量信息。您需要确保您的应用程序并不紧密针对 Windows 9
x 平台,相反它应当紧密针对 Windows NT 平台。
目标严格限制的应用程序
第一种目标严格限制的情况是:应用程序使用的 API 仅能在 Windows 9x 平台上
实现,在 Windows NT 中不能实现。我常常使用的 Tool Help API 就是这样的一
个例子。在 Windows 2000 中我们已经可以在某种程度上支持 Tool Help API,
但是您会发现在 Windows 9x 中有大量的 API 还没有找到升级到 Windows NT 的
途径。没有一个真正可行的方法能解决这一难题。您可以使用 SDK 中的 .csv 文
件,该文件实际上只是一个电子表格,它能告诉您关于任何 API 的情况:哪里可
执行、哪里不能、如何工作,除此之外还有各种其他资料。另一种方式是对应用
程序进行切实的测试,确保您的应用程序能够在 Windows 98 和 Windows NT 平
台之间进行迁移,确保它们能够运行。这种方式可能要简单得多,执行起来也快
得多。
您需要注意以下事实;Windows NT 平台在其 GDI 调用中使用的是全 32 位的坐
标系统,而 Windows 9x 使用的是 16 位。一定要知道这些不同之处。事实上,
Windows NT 中的所有句柄用的是完全的 32 位。有些开发人员试图利用以下事实
:在 Windows 9x 中句柄是 32 位,但却只用到 16 位。如果在 Windows NT 上
这样使用,后果将非常糟糕。
通用替换
在应用替换的过程中有许多应用程序也会陷入功能障碍。Windows 9x 所采用的方
式可以称之为“直接替换 (flat thunk)”:它允许 16 位应用程序调入 32 位应
用程序,也允许 32 位应用程序直接调入一个 16 位组件,或 16 位应用程序。
Windows 2000 不支持这一功能,尤其是不支持 32 位应用程序直接调入 16 位应
用程序。Windows 2000 和 Windows NT 所采用的方式可以称之为“通用替换 (g
eneric thunk)”:通用替换允许 16 位应用程序调入 32 位组件,也允许 16 位
应用程序启动对 32 位组件的调用过程,然后由 32 位组件回调 16 位应用程序
,而不支持由 32 位代码直接调用 16 位组件,这一过程无法生效。您只能由 1
6 位启动对 32 位的调用,反之不能。另外针对替换过程还需要记住一点:即 W
indows 9x 和 Windows NT 之间的基础进程模型都是有区别的。由通用替换中您
可以看出一些区别。最简单的办法是将 16 位组件移植到 32 位组件。您需要对
这两个平台的替换问题有清醒的认识。
--
在时间面前,没有永恒
------一个热爱自由的人
※ 来源:.紫 丁 香 bbs.hit.edu.cn.[FROM: 202.108.67.114]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:3.665毫秒