你是否有思考过这样一个问题:

如果你有一个窗口句柄,但是它所引用的窗口已经被销毁了,对这个窗口发送消息 (通过调用 SendMessage) 会发生什么事情?更加糟糕的是,窗口句柄可能被其他窗口重用了,这就使得问题更加复杂。

很显然,对一个已销毁的窗口发送消息是程序的一个BUG,但是由于这个问题十分普遍,在 Windows NT 中,负责窗口管理器组件的开发人员决定采取更为主动的方式来解决问题。

在开始讲解之前,请注意,下面所说的属于所谓的”底层内幕”,意味着,这些信息属于实现细节,可能不准确,也可能随着系统的更新而发生改变。

在 Windows NT 中,一个 32 位的窗口句柄被分为两部分:

低 16 位有点类似于 Windows 95 时期的窗口句柄,即它是一个表格的索引位置,而高 16 位被 Windows 95 置零,以保持和 16 位 应用程序兼容。
而 Windows NT 会使用其中的一个位作为一个标识符。

举个例子,如果一个索引 0x0124 被首次使用,则窗口句柄为 0x00010124,当窗口被销毁之后,一个新的窗口被创建,也使用了相同的索引,则新窗口的句柄为 0x00020124。每当索引被重用的时候,这个标识符会增加1。

但是,这样会对 16 位应用程序产生影响吗?

当一个 16 位应用使用一个窗口句柄时,窗口管理器窗口管理器只会使用该值作为表的索引,并不会解释这个标识位,因此,窗口管理器只是对该索引位置中的窗口进行操作。

毕竟,标识位对于窗口管理器的正确操作并不是必不可少的,添加它只是为了解决那些有问题的程序。
因此,16 位程序与 Windows 3.1 和 Windows 95 一样容易受到”窗口句柄重用问题”的影响。

只有 32 位程序获得了额外的保护。但是,由于窗口句柄是一个索引,因此可以使用完整的 16 位范围的窗口句柄(减去 NULL 等特殊值),理论上最大值约为 65000 个窗口。

但是,实际可用值为理论最大值是一半,大约 32700 个窗口。为什么我们失去了一半的范围呢?
因为有些程序假设窗口句柄总是偶数! (意外吧!)

在后面的文章中,我们将会看到每个进程的句柄数最大值为 10000。

总结

所以,在传统基于窗口的用户界面中,所能创建的窗口是有上限的,且创建的窗口越多,运行时性能越慢。
现在时髦的无窗口(Direct UI)设计,则不再使用窗口句柄,而是实时绘制用户界面。
两种玩法中,我属于保守派(不思进取派)。
拓扑梅尔智慧办公平台 (Topomel Box) 使用了传统来创建用户界面,虽不时髦,但重在管用!

最后

Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《How are window manager handles determined in Windows NT?》

打开网易新闻 查看更多图片