Window Ghosting

最近工做中遇到Window Ghosting這個問題, 感受挺有意思,這裏簡單記錄下。



在XP時代咱們的程序沒有響應後只能經過任務管理器強制殺掉,可是Vista以後狀況變了, 咱們仍然能夠拖動失去響應的窗口,甚至能夠嘗試最小化和關閉窗口, 咱們把這個特性叫住Window Ghosting。

首先咱們考慮下怎樣判斷一個窗口是否已經失去響應?
 
通常咱們想到的是SendMessageTimeout,給窗口發送WM_NULL消息,判斷返回是否超時。這固然也是一種方法,可是系統有更方便的API  IsHungAppWindow, 該API是判斷窗口是否失去響應的標準方法。咱們猜想IsHungAppWindow內部是否經過SendMessageTimeout來實現的, 跟蹤下咱們會發現不是咱們想象的那樣, IsHungAppWindow內部掉用了未公開的API NtUserQueryWindow。

接下來考慮下  IsHungAppWindow 是如何鑑定一個窗口是否在失去響應狀態?
這是MSDN中的原話: 
Determines whether the system considers that a specified application is not responding. An application is considered to be not responding if it is not waiting for input, is not in startup processing, and has not called PeekMessage  within the internal timeout period of 5 seconds.
簡單來講就是程序在非等待輸入狀態 ,不是在程序啓動階段, 而且5秒內沒有從消息隊列中取消息。 

下面咱們思考系統是如何實現Window Ghosting的?
咱們知道失去響應的窗口通常來講是由於UI線程正在作一些繁忙的工做, 或是UI線程死鎖而沒有在繼續運行了。 那這裏就很奇怪了, UI線程都失去響應了, 窗口怎麼還能響應咱們的鼠標拖動消息?咱們的鼠標拖動事件須要運行在UI線程中才行 ,該實現有些顛覆咱們現有的計算機知識。

這裏的關鍵就是咱們看到的失去響應的窗口是否是仍是咱們原來的窗口? 實際上咱們真正的窗口已經讓系統用Ghosting窗口替代了。

完整過程是這樣的, 當系統檢測到咱們程序窗口失去響應了, 系統進程(dwm.exe)會以相同的Z-order, 位置,大小和Style建立一個ghosting窗口(能夠經過SPY查看 ,類名是Ghost), 我 們看到的失去響應的窗口就是這個窗口, 該窗口的客戶區內容是從老窗口中拷貝過來的。而咱們原來真正窗口依舊在那裏(style, 位置,大小和z-order都沒有變 ), 可是dwm.exe合成屏幕內容是並不會把這個窗口畫出來, 因此咱們看起來就是原來的窗口給hide了。
 
這就是Window Ghosting的奧祕, 咱們能夠在程序中調用 API DisableProcessWindowsGhosting 來禁止系統對咱們的程序使用 Window Ghosting.

Window Ghosting這個特性很不錯, 讓失去響應的程序也有很好的用戶體驗, 可是它也帶來了一些問題。

我遇到的問題是咱們在枚舉窗口的過程當中,咱們經過GetWindowRect查詢一個失去響應的程序窗口的位置,可是返回結果卻和咱們屏幕上看到的不一致, 由於咱們看到的是被咱們拖動過的Ghosting window,可是API返回的確是被hide的原窗口的位置。 這種狀況下咱們須要原窗口和Ghosting窗口的一張映射表, 可是我尚未找到他們對應關係的方法, 不知道系統又沒有相關API提供?一種方法是經過查找類名是"Ghost"的窗口,判斷進程是否是dwm.exe, 再經過標題匹配。可是該方法效率低,也不可靠。
相關文章
相關標籤/搜索