2012-7-8
cswuyg
半年多以前就遇到這個問題,當時由於時間關系沒有做詳細分析,又用其他方式打補丁解決問題,最近又遇到,總算不懶惰,寫個demo看看到底怎么回事。
一、 窗口的Parent、Owner關系
窗口有兩種可能的上下級關系,一種是Owner,一種是parent。
創建窗口時,有WS_POPUP屬性的窗口,它的父窗口其實是Owner窗口。創建之后,可以通過SetParent,為它設置父窗口,這樣子他就有父窗口(子窗口位置限制在父窗口中)又有POPUP屬性。
創建窗口時,有WS_CHILD屬性的窗口,它的父窗口就是父窗口。可以通過SetWindowLongPtr給它加上POPUP屬性。
二、當Owner、parent窗口最小化時,子窗口的反應
Owner關系的窗口。
Owner窗口隱藏,子窗口不會隱藏。
Owner窗口最小化,子窗口隱藏。
Parent關系的窗口。
Parent窗口隱藏,子窗口隱藏。
Parent窗口最小化,子窗口隱藏。
子窗口的隱藏是通過發送WM_SHOWWINDOW消息做的。
所以,當有一個功能是:owner窗口隱藏,子窗口也要隱藏時,就需要自己在owner窗口的隱藏消息處理里去實現子窗口的隱藏。
當父窗口最小化、隱藏時,通過Spy++,可以發現子窗口還有WS_VISIBLE屬性。
當Owner窗口最小化,通過SPY++,可以發現子窗口沒有WS_VISIBLE屬性。
三、Owner和Parent的其它相關知識
GetParent( HWND hWnd ),獲取窗口的父窗口或者owner窗口。如果窗口是child窗口,則返回parent窗口句柄,如果窗口是頂層窗口,則返回owner窗口。如果頂層窗口沒有owner或者函數失敗,則返回0.
頂層窗口:應該是指使用spy++可以抓到的窗口,WS_CHILD屬性的窗口就抓不到。
ShowOwnedPopups();函數,可以讓因最小化而隱藏的owner子窗口顯示。
四、之前遇到的誤解
半年多以前在WTL下做開發的時候,對子窗口的WM_SHOWWINDOW的處理,沒有把bHandled修改為0,導致DefWindowProc函數沒有被調用,按照MSDN的說法,DefWindowProc函數負責了窗口的隱藏、顯示,於是當因為父窗口最小化時,子窗口雖然收到了WM_SHOWWINDOW的隱藏消息但並沒有隱藏。當時在網絡上找到了一篇文章《VC/MFC 程序最小化后不能還原的原因與解決方法》,以為是這個原因導致的,於是在父窗口最小化之前,先把子窗口隱藏,父窗口還原,再讓子窗口顯示。陰錯陽差的以為是這個原因,最近再做測試,才發現,根本不是這么回事,那篇文章說的是另一種情況。
又想到另一個問題,為什么直接調用ShowWindow,可以使得窗口隱藏,而主窗口Mini觸發的子窗口WM_SHOWWINDOW消息,卻會因為消息響應函數里的處理導致窗口不隱藏?有可能是windows系統內部對主動調用ShowWindow的做了處理,而對消息觸發的則通過DefWindowProc做處理。
《VC/MFC 程序最小化后不能還原的原因與解決方法》這篇文章所講的情況,我測試了A擁有B,或者A有子窗口B,在正常操作的情況下都沒出現。所以,我懷疑所說的應該是另一種情況:A擁有(Owner)B,B擁有(Owner)C的情況下,把Activate切換到C窗口,然后對A窗口最小化,這時候C窗口就無法最小化,而且無法從任務欄直接還原,需要使用系統菜單還原,這就是所謂的消息被activate子窗口攔截了。在非正常操作的情況下,這篇文章所講的就很容易出現了,譬如:owner窗口最小化,但產品功能要求popup子窗口依舊顯示,活動切到子窗口,這時候如果不做處理,那么父窗口就無法單擊任務欄還原了。那么如何處理呢?當popup子窗口顯示,而owner窗口最小化到了任務欄,這個時候,你想通過點擊任務欄恢復owner窗口,該如何處理?我覺得是無法處理的,至少我沒想到簡單便捷的邏輯去實現,看下邊五的分析。
五、當popup子窗口顯示而owner最小化在任務欄時,無法點擊任務欄還原owner窗口(2012-7-11補充)
假設通過某些方式,讓popup子窗口顯示,而owner窗口最小化在任務欄,我想通過點擊任務欄還原owner窗口,我認為這是無法解決的問題。用spy++抓到的消息顯示,當點擊任務欄的圖標時,系統只給活動窗口所在線程的每個窗口發送了WM_WINDOWPOSCHANGING、WM_ACTIVATEAPP消息,也就是說,這種情況下點擊任務欄圖標,我的程序並不知道我此時是點擊了任務欄還是拖動了popup子窗口。於是這個問題沒法解決(也許可以通過鼠標位置判斷當前是否在點擊任務欄區域,但打補丁的方式總會容易有陷阱)。那么是否有軟件能做到呢?目前我沒看到能解決這個問題的例子,如果它們有點小聰明,那么會想到“為什么我要解決呢?”,問題本身就不該存在,那么避開就行了。
注意到QQ影音播放器會有一個選項,讓你最小化到托盤區或者最小化到任務欄,當最小化到托盤區時,可以讓播放器設置窗口單獨顯示,而最小化到任務欄就則不可以讓播放器設置窗口單獨顯示,這就避開了這個問題。
曾經的百度ting客戶端,最小化到任務欄,然后從托盤區的“全局設置”項打開“設置”窗口,會發現無法通過點擊任務欄菜單還原ting的主界面。
之前用過的金山毒霸,也存在子窗口顯示,父窗口最小化在托盤區和任務欄,點擊任務欄無法還原主窗口的情況。
暴風影音,可以看到托盤區有兩個打開窗口的選項,一個是“下載管理”,一個是“高級設置”,下載管理是獨立的窗口,跟播放器主窗口沒有關系,所以打開下載窗口的時候,播放器主窗口后續的操作不受影響。“高級設置”窗口的owner窗口是播放器主窗口,它為了避開這個無法解決的問題,就不會讓你單獨打開高級設置窗口,打開設置窗口時,播放器主窗口都會出現。類似的快播播放器,也是讓設置窗口顯示時,主窗口也顯示,避免了這個問題。
再或者,不使用POPUP窗口,使用overlapped窗口。(三種窗口類型的介紹:http://www.cnblogs.com/ziwuge/archive/2010/12/27/1917817.html)
網絡上資料:
《談談父窗口和所有者窗口》
http://blog.vckbase.com/iwaswzq/archive/2006/09/12/22380.html
《VC/MFC 程序最小化后不能還原的原因與解決方法》