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 程序最小化后不能还原的原因与解决方法》
http://blog.sina.com.cn/s/blog_4b44e1c00100mdkl.html