В случае аварийного завершения или некорректной работы некоторых приложений, в системном трее могут оставаться "мертвые" иконки, которые уже не принадлежат ни одному запущенному процессу. Глюк хоть и не смертельный, но все равно неприятный. И основная проблема в том, что область трея никак не реагирует на внешние сообщения типа
WM_REPAINT, и функции типа
UpdateWindow и
InvalidateRect. То есть автоматически обновить или перерисовать его, чтобы избавиться от "мертвых" иконок, не получится. Но такие иконки удаляются, если провести курсором мышки над ними. Значит единственный способ перерисовать иконки в трее - это сэмулировать движение мыши над окном трея. Как найти окно трея и его хэндл мы уже знаем, тут ничего нового. В сегменте данных те же значения:
; Сегмент данных
section '.data' data readable writeable
class1 db 'Shell_TrayWnd',0 ; Название класса окна трея
class2 db 'TrayNotifyWnd',0 ; Название класса панели уведомлений
class3 db 'SysPager',0 ; Трей
class4 db 'ToolbarWindow32',0 ; Панель с иконками
ToolbarHandle dd ? ; Хэндл окна трея
ToolbarRect RECT ; Размер окна трея
С поиском окна тоже никаких проблем. Способ универсальный, прекрасно работает на Windows XP и Windows 7.
; Найти окно трея
invoke FindWindow,class1,NULL
or eax,eax
jz exit_process
; Найти панель уведомлений
invoke FindWindowEx,eax,NULL,class2,NULL
or eax,eax
jz exit_process
; Найти трей
invoke FindWindowEx,eax,NULL,class3,NULL
or eax,eax
jz exit_process
; Найти панель иконок в трее
invoke FindWindowEx,eax,NULL,class4,NULL
or eax,eax
jz exit_process
; Сохранить хэндл окна с иконками
mov [ToolbarHandle],eax
Окно трея найдено, осталось сэмулировать движение мышки. Для этого надо просто получить размеры окна трея и в цикле отправить сообщение
WM_MOUSEMOVE. Не обязательно эмулировать движение мышки над каждой точкой окна, достаточно пройтись один раз над каждой иконкой. Так как размер маленькой иконки 16х16 пикселов, то шаг выберем также 16.
И, собственно, сам код. Обратите внимание, что сообщения трею мы отправляем через PostMessage, чтобы не тратить время на ожидание от него ответа.
; Получить размеры окна трея
invoke GetClientRect,[ToolbarHandle],ToolbarRect
; Эмуляция движения мыши над окном трея
xor esi,esi
loc_move_line:
xor edi,edi
loc_move_column:
; y << 16
mov eax,esi
shl eax,16
; x
add eax,edi
; Отправить сообщение движения мыши
invoke PostMessage,[ToolbarHandle],WM_MOUSEMOVE,NULL,eax
; Следующий столбец
add edi,16
cmp edi,[ToolbarRect.right]
jb loc_move_column
; Следующая строка
add esi,16
cmp esi,[ToolbarRect.bottom]
jb loc_move_line
Этот способ, несмотря на то, что он работает, не лишен недостатков. Некоторые приложения, устанавливающие свои иконки в трей, реагируют на перемещение курсора над ними. Например, антивирус DrWeb показывает всплывающее окно с версией своей базы, менеджер USB-устройств Zentimo показывает окно со списком подключенных устройств, какие-нибудь другие программы также могут выполнять свои действия. Так вот, раз мы эмулируем движение мышки над иконками, то и программы срабатывают на это как на реальное движение курсора. Если вы используете трюк с обновлением трея в своей программе, то обязательно оформляйте его как отключаемую опцию с предупреждением о возможной несовместимости. К сожалению, пока что это единственный реальный способ заставить Проводник перерисовать иконки в трее из пользовательского приложения.
В приложении пример программы, обновляющей иконки в трее описанным в статье способом. Для тестов прилагается программа, которая устанавливает свою иконку в трей, и не удаляет ее после выхода.