点击任务栏图标,对应的窗口会激活带到前台,笔者在项目中遇到点击任务栏图标一直不起作用,大家未必会遇到该问题,原因不重要,排查过程的思路、工具更重要。
为什么我的程序不行,其它程序可以,它们接收到的消息有什么区别?用SPY 抓取该窗口(应用窗口),观察该窗口在点击任务栏图标时收到的消息,然后抓取其它正常的窗口,也观察其在同样操作下接收到的消息,然后比较。SPY 记录窗口消息的方法:右击窗口,选择菜单Messages,然后点击主菜单Messages->Logging Options,选择Messages标签页,点击Select All按钮,点击OK。
对比正常和不正常的消息后发现,不正常时窗口未收到WM_ACTIVATE消息。WM_ACTIVATE消息的默认处理是将窗口激活带到前台,未接收到WM_ACTIVATE消息就是问题所在。
未接收到WM_ACTIVATE消息,必然是发送给其它窗口,而且这个窗口很有可能是本进程,于是继续观察本进程窗口接收到的WM_ACTIVATE消息,方法:点击主菜单Messages->Logging Options,选择Windows标签页,勾选Windows of Same Process选项,然后选择Messages标签页,勾选WM_ACTIVATE消息,这样消息日志比较少。
观察发现果然是本进程的另外一个窗口接收到WM_ACTIVATE消息,右击WM_ACTIVATE消息查看消息详细内容,里面可以看到哪个窗口接收。SPY 抓取该窗口,发现该窗口是应用窗口的子窗口,右击窗口选择Properties菜单,查看该窗口详细信息,发现窗口的Windows Styles有WS_POPUP,正常应该是WS_CHILDWINDOW。
明明是子窗口为什么会有WS_POPUP呢?这个窗口实现上很可能有问题,走读该窗口相关代码,发现该窗口创建时指定WS_POPUP,然后使用SetParent() API设置它为子窗口。
为什么SetParent() API没有将WS_POPUP改成WS_CHILD呢?仔细阅读MSDN关于SetParent() API的介绍,里面明确提到为了兼容考虑,SetParent() API不会将WS_POPUP改成WS_CHILD,调用方需要自行修改。
显示调用SetWindowLong() API去除WS_POPUP增加WS_CHILD,问题解决。