天天看点

窗口可不是什么便宜的东西

尽管Windows操作系统以窗口为中心,但窗口本身可不是便宜的东西。更重要的是,1985 年那会严格的内存限制,迫使系统设计者做出各种不同的设计决策。

那我们以列表框控件举例。

在现代设计中,你可以将列表框控件设计为一系列的子窗口列表,每个子窗口代表列表中的一个条目。 那么,具有20000个项目的列表框将具有20000个子窗口。 然而,这种设计理念,在1985年是完全可笑的。

回想一下,当时的Windows是围绕16位处理器构建的。窗口句柄是16位值,内部只是接近 64K堆内存的指针。 一个窗口对象是88个字节(我还真数过),这意味着在内存不足之前你最多可以创建大约700个左右的窗口。

更重要的是,菜单创建在同一个64K的内存堆中,因此实际限制要低得多。 即使窗口管理器在内部使用了大于64K的堆(Windows 95 就是这样做的),20000 个窗口也超过了 1.5MB的内存。由于 8086 的最大地址空间为 1MB,即使你将每个字节的内存都用于窗口对象,你仍然没有足够的内存。

此外,使每个列表框项成为窗口意味着每个列表框都将是一个可变高度的列表框,这带来了管理具有可变高度项的容器的复杂性。

这违背了 API 设计的两个一般原则:(1)简单的事情应该简单,(2)“付费游戏”,如果你做简单的事情,你不应该支付复杂事情的成本。

用实际窗口填充列表框也会使“虚拟列表框”的设计变得更加棘手。使用现有的设计,你可以说“有一百万个项目”,而无需实际创建它们。 (这也是为什么窗口空间被划分为“客户”和“非客户”区域而不是让非客户区域由小子窗口组成的原因。)

为了保持与 16 位 Windows 程序的兼容性(多亏了 WOW 层,这些程序仍然可以在 Windows XP运行),系统中的窗口句柄不能超过 65536 个,因为超过这个数就会影响 16 位程序的正常运行。(一旦创建了第 65537 个窗口,将有两个具有相同 16 位句柄值的窗口,这要归功于鸽巢原理。)(是的,即使在今天,16/32 位互操作性仍然很重要。)

如果系统可以创建的窗口上限为65536个窗口句柄,则包含100000个文件的列表将遇到严重问题。 随着新功能不断地被添加到窗口管理器中,窗口对象的大小随着时间的推移而增加。 今天它甚至比过去的88字节还要大。 最好不要创建不必要的窗口。 如果你的应用程序设计需要你创建数千个窗口,则你应该考虑迁移到无窗口模型(windowless model),例如Internet Explorer、Word、列表框、树视图、列表视图,甚至我们的滚动条示例程序。

通过无窗口模型,你摆脱了完整的窗口句柄的系统开销,以及随之而来的所有包袱。 由于窗口句柄对所有进程都是可见的,因此集中管理窗口列表会产生很多开销。 如果你的应用程序没有窗口,那么唯一可以访问你的内容的程序就是你自己的程序。 你不必担心编组、跨进程同步、Unicode/ANSI 转换、外部子类化、挂钩……如果你想要的话,还可以使用1GB的内存来跟踪你的无窗口数据,因为无窗口控件不影响任何其他进程。

其他进程可以访问窗口句柄这一事实,对可以在不影响整个系统的情况下创建多少个窗口句柄施加了实际的限制。

我相信WinFX使用了“屏幕上的一切都是元素”的模型。 据我了解,他们构建了一个无窗口框架,因此你就不用自己实现了。 (不过,我不确定这一点,我自己不是 WinFX 人。)

总结

需要警惕窗口数量的剧增,一方面这会增加管理难度,另一方面,进程的GDI对象太多,也会显著降低运行时性能。

交互设计上,不妨简单点,再简单点,花里胡哨不是我们所追求的,好的程序,应该是用完即走的。

最后