冷知识:Windows XP 窗口残影之谜

在 XP 的年代,程序卡顿时出现下图这种窗口残影,是再日常不过的事了。也是从那时候起,我就在好奇这种现象的成因。

今日偶然再次目睹这一情景,搜索一番,发现有三篇解释较为详尽的英文说明,翻译于此。


What causes the Windows XP lagscreen render fail? @Synetech

这篇回答汇总地介绍了 XP 上窗口残影的原因:窗口失去响应时,XP 会将窗口不断地截图重绘。

这是所谓的「幽灵窗口(ghost window)」。如果进程挂起或陷入死锁,将无法响应请求更新窗口画面的绘图消息。这时 Windows 会接管窗口的绘制,直到进程能对作出响应并重新开始绘图(或者崩溃或被结束进程)。

在更老版本的 Windows 中,失去响应的窗口会直接绘上背景色。但在 XP 中,Windows 会将窗口截图,保存窗口最后一次更新时的样子,并以此来绘制窗口。当然,如果窗口的一部分被覆盖(比如被拖到显示器外),Windows 只能用背景色来填充缺失的部分(因此,如果你把窗口在各个方向都拖出屏幕一下,整个窗口就全白了)。

而且,如果你把另一个窗口拖到上面,就会留下残影,因为另一个窗口移开时,这个进程依然没有响应绘图消息。(译注:应该是 XP 中会持续不断地对窗口截屏,用以绘制下一帧,所以会把放在上面的其他窗口也截下来,并在下一次绘图时显示上去。

在 Vista 及以后的系统中,Windows 依然会如 XP 般绘制幽灵窗口。但这里有一点小技巧,可以让失去响应的窗口看起来依然栩栩如生,可以移动和最小化,而不会丢失最后一次绘图的内容,或产生图中这种镜厅现象。在这些系统中幽灵窗口的原理是:系统把真正的宕掉的窗口隐藏起来,新建一个相同尺寸与形状的临时窗口将其替代,用窗口宕掉前的屏幕截图将其填充,还会盖上一层半透明蒙版。这样,当你从上面拖过其他窗口时,它会保留原有的图像,因为你看到的这个窗口其实并没有死掉——它一直在用储存的截图来响应更新绘图消息。

微软员工 Raymond Chen 和 Matt Eason 详细地介绍了这一点。(译注:后面翻译的就是这两篇。

当然,Mac、Linux 等系统,都用自己的办法管理窗口与绘图,所以宕掉的窗口会有不同的表现。


The white flash @Raymond

这篇文章写于 2004 年,介绍的是上古 Windows 到 XP 对未响应窗口的处理方式的变迁。

如果有一个进程暂时不能处理消息,但因为某种原因需要绘制窗口(比如被解除最小化),Windows 通常会失去耐心,直接将窗口填充为白色。

——至少人们是这么认为的。实际上,Windows 填充的是程序的类背景笔刷(class background brush)。因为大多数人用的都是 COLOR_WINDOW,而 COLOR_WINDOW 在大多数色彩搭配中是白色,最后也就导致了窗口上下一白。

为什么要把窗口填成白的呢?为什么不干脆不管它呢?

嗯,以前是这样的,但会导致出现的窗口的内容是屏幕之前显示的内容。比如你正开着文件管理器,然后你解除了一个失去响应的程序的最小化,这个程序窗口中的内容将是……文件管理器的图像。如果人们误以为这块区域依然是文件管理器,尝试双击其中某处,点到的却将是卡死的那个程序。

在 Windows XP 中,停止绘图的程序的处理方式发生了变化。现在,系统会将失去响应的窗口进行截图,并在窗口本身无法进行绘图时,直接将其重绘回去。然而要注意的是,如果系统无法捕捉到完整的窗口——比如窗口被部分遮挡了——那这一部分将会用类笔刷(class brush)填充。

而那常常是白的。


Windows Error Reporting (For Hangs) @Matt Eason

这篇文章主要介绍 Vista 及之后的幽灵窗口。

当某个图形界面失去响应时,用户会(在众多信号中)注意到三个非常明显的出错标志:

  • 鼠标点不了
  • 键盘打不了
  • 窗口不变了

最终,抓狂的用户会放弃挣扎并试图关掉失去响应的窗口。当然,这本应毫无用处,毕竟窗口都已经失去响应了。然而,Windows 会竭尽所能帮助抓狂的用户,并为宕住的窗口提供最基本的交互能力。确切地说,系统会让窗口看起来处于半响应状态——它会创造一个幽灵窗口。

幽灵窗口

当 Windows 检测到某个窗口失去响应,就会将这个窗口隐藏起来,并原地创建一个新的窗口,即幽灵窗口。它具有以下性质:

  • 窗口类为「Ghost」
  • 可响应
  • 由另一个进程管理(dwm.exe)
  • 窗口标题附加「(未响应)」
  • 显示宕掉窗口最后的图像

虽然外表相似,但完全无法通过新的窗口控制旧的窗口。

如果用户和幽灵窗口交互,幽灵窗口的表面会盖上一层蒙版。这是为了通知用户这个窗口有问题。这层蒙版被称之为「雪霜(frost)」,因此,这种窗口有时也被称为「霜冻窗口(frosted window)」。

覆盖了雪霜蒙版的幽灵窗口的例子
Spy++ 证实这的确是一个幽灵窗口

在 Vista 和 Windows 7 中,幽灵窗口只在满足以下所有条件时出现:

  • GUI 线程超过 5 秒没有接受消息(Vista 与 Windows 7 的默认值)。
  • 窗口接收到了用户输入(如鼠标或键盘输入)。

注意其中的第二点:如果某个窗口没有用户输入,就不会变为幽灵窗口。因为幽灵窗口只是对用户提供的一种便利,如果用户根本没有注意到某个窗口,系统也不必浪费 CPU 时间去监控其有没有失去响应。

一旦窗口恢复响应,幽灵窗口就会被销毁,旧窗口回到可见状态。这是一种无缝的体验,绝大多数用户根本不会意识到他们在和不同的窗口打交道。

Windows 错误报告(程序挂起时)

最终还是会有用户抓狂到尝试关闭失去响应的窗口。因为和他们交互的是幽灵窗口,角落上的关闭按钮是能够响应的。一旦点下这个按钮,程序就会被中止。

然而,如果这个挂掉的程序的作者对用户的体验毫无了解,又怎么能修复问题呢?这时 Windows 错误报告就派上用场了。这种特殊模式下的 Windows 错误报告又被称为「挂起报告(Hang Reporting)」。挂起报告会收集供开发者诊断挂起原因的信息。

如果开发者对用户的报告能作出响应,那用户就更有可能坐镇不慌了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注