geelaw
2022-01-29 07:45:09 +08:00
首先,那个东西叫“通知区域”,不叫 tray 。即使你认为 tray 来自于早期代码里的命名,tray 也是指整个任务栏。
这个问题的成因是通知区域图标是程序通知任务栏创建的,崩溃时程序没有通知删除,所以不会立刻消失。
鼠标放上去会消失是因为 Explorer 会给图标对应的窗口发消息,如果窗口句柄已经无效,则会删除图标——注意窗口句柄是 USER 句柄,因此既没有计数也会复用,在极限情况下可能新程序被分配到已经崩溃程序使用过的句柄,导致新程序莫名其妙收到消息。
Explorer 崩溃重启后程序需要重新添加图标,获取这一信息的方法是注册“任务栏创建好了”消息。
为什么不能轮询——因为不存在一个“心跳”消息,也不应该随便给窗口发送消息(虽然理论上可以注册一个新名字,这样任意窗口都不会对该消息有特殊反应才对),也不应该轮询(性能问题)。
这个问题显然是可以解决的,比如任务栏上列举窗口的按钮可以对崩溃立刻作出反应,这个变化是 USER 监控,并发送 WM_SHELL_* 消息给任务栏的。然而通知区域图标并不是 USER 概念而是 shell 概念(只存在于 explorer.exe 进程中),虽然可以提升到 USER 概念实现之,估计是不值得吧( USER 是 per-session 级别的,可以容纳的句柄数量也相当有限)。
更好的解决方法是——不要崩溃。
类似的问题也存在于 Application Desktop Toolbar 。还有更多设计 bug 导致 shell 的一些部分需要两次操作才能刷新( COM categories 、shellnew 等)。