Иногда приложению требуется получить список иконок, находящихся в трее, а также список приложений, которые их туда разместили. Это может быть нужно для обнаружения некоторых скрывающихся приложений, для эмуляции кликов на иконках, ну или просто для спортивного интереса. В любом случае для получения списка иконок надо сделать следующее: найти в трее панель с иконками, при помощи сообщения
TB_BUTTONCOUNT получить количество иконок, а затем через отправку сообщения
TB_GETBUTTON получить всю необходимую информацию по каждой иконке. Теперь рассмотрим все шаги подробнее.
Работать с вложенными окнами трея мы
уже умеем, здесь практически то же самое, разница только в названиях классов дочерних окон.
; Сегмент данных
section '.data' data readable writeable
...
class1 db 'Shell_TrayWnd',0 ; Название класса окна трея
class2 db 'TrayNotifyWnd',0 ; Название класса панели уведомлений
class3 db 'SysPager',0 ; Трей
class4 db 'ToolbarWindow32',0 ; Панель с иконками
ToolbarHandle dd ? ; Хэндл окна с иконками
...
; Сегмент кода
section '.code' code readable executable
...
; Найти окно трея
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
...
Теперь у нас есть хэндл окна панели инструментов с иконками в трее. Получим количество иконок в панели.
; Получить количество иконок в трее
invoke SendMessage,eax,TB_BUTTONCOUNT,0,0
or eax,eax
jz exit_process
; Сохранить количество иконок в трее
mov [IconsCount],eax
Количество иконок тоже есть. Осталось перебрать их в цикле и получить всю необходимую информацию. Для этого используется сообщение TB_GETBUTTON и структура
TBBUTTON для получения результата. Однако, если сейчас попробовать послать окну панели сообщение TB_GETBUTTON, то в результате не получим ничего. Почему? Потому что память, в которую будут записываться данные, обязательно должна принадлежать процессу, который является владельцем окна трея (обычно это explorer.exe).
Ненадолго отвлечемся от трея и выделим блок памяти нужного размера в контексте процесса-владельца трея. Размер блока равен размеру структуры TBBUTTON.
; Получить ID процесса-владельца трея
invoke GetWindowThreadProcessId,[ToolbarHandle],ProcId
; Открыть процесс с полным доступом
invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,[ProcId]
or eax,eax
; Фокус не удался
jz exit_process
; Сохранить хэндл процесса-владельца трея
mov [hProcess],eax
; Выделить блок памяти в контексте процесса
invoke VirtualAllocEx,[hProcess],NULL,dword sizeof.TBBUTTON,\
MEM_COMMIT,PAGE_READWRITE
or eax,eax
jz exit_process
; Сохранить указатель на блок памяти
mov [lpData],eax
Теперь все готово для приема данных, можно приступать к перебору иконок в трее. В структуре TBBUTTON двойное слово dwData - указатель на блок расширенных данных, которые определяются приложением. В нашем случае по этому адресу лежит структура EXTRADATA, не описанная в FASM:
; Структура пользовательских данных иконки
struct EXTRADATA
Wnd dd ? ; Хэндл родительского окна иконки
uID dd ? ; Стиль отображения иконки
ends
Поскольку все нужные данные находятся в другом процессе, читать их будем через функцию
ReadProcessMemory: сперва структуру TBBUTTON, а затем соответствующую ей структуру EXTRADATA. Зная хэндл окна-владельца каждой иконки, можно получить идентификатор процесса, которому принадлежит окно, а по нему, в свою очередь, можно узнать имя исполняемого файла. Для получения имени есть несколько методов, в этом примере я буду использовать функцию
CreateToolhelp32Snapshot.
; Перебрать все иконки в трее
loc_loop:
dec [IconsCount]
; Получить иконку из трея с индексом IconsCount
invoke SendMessage,[ToolbarHandle],TB_GETBUTTON,[IconsCount],[lpData]
; Прочитать структуру иконки
invoke ReadProcessMemory,[hProcess],[lpData],button,\
dword sizeof.TBBUTTON,BytesRead
or eax,eax
jz exit_process
; Прочиталась вся структура?
cmp [BytesRead],sizeof.TBBUTTON
jnz exit_process
; Прочитать пользовательские данные иконки
invoke ReadProcessMemory,[hProcess],[button.dwData],extra,\
dword sizeof.EXTRADATA,BytesRead
or eax,eax
jz exit_process
; Прочиталась вся структура?
cmp [BytesRead],sizeof.EXTRADATA
jnz exit_process
; Это скрытая иконка?
mov eax,[extra.uID]
and eax,80000000h
or eax,eax
; Да, пропустить
jnz loc_loop
; Окно процесса существует?
invoke IsWindow,[extra.Wnd]
or eax,eax
jz loc_loop
; Получить Id процесса, чья иконка находится в трее
invoke GetWindowThreadProcessId,[extra.Wnd],ProcTrayId
; Снимок процессов системы
invoke CreateToolhelp32Snapshot,TH32CS_SNAPPROCESS,0
mov ebx,eax
; Перебрать в цикле все процессы
mov eax,sizeof.PROCESSENTRY32
mov [ProcEntry.dwSize],eax
invoke Process32First,ebx,ProcEntry
@@:
cmp eax,FALSE
je @f
; Это нужный нам процесс?
mov eax,[ProcEntry.th32ProcessID]
cmp eax,[ProcTrayId]
je @f
; Следующий процесс
invoke Process32Next,ebx,ProcEntry
or eax,eax
jz loc_loop
jmp @b
@@:
push eax
; Закрыть хэндл
invoke CloseHandle,ebx
pop eax
; Имя файла определить не удалось
or eax,eax
jz @f
invoke wsprintf,buff,mask,ProcEntry.szExeFile
add esp,12
; Записать имя файла в консоль
invoke lstrlen,buff
invoke WriteConsole,[stdout],buff,eax,BytesRead,NULL
@@:
; Все иконки обработали?
cmp [IconsCount],0
ja loc_loop
; Очистить память и ресурсы
invoke VirtualFreeEx,[ProcId],[lpData],0,MEM_RELEASE
invoke CloseHandle,[ProcId]
Описанный метод протестирован и гарантированно работает в Windows XP и Windows 7, но не работает в альтернативных оболочках типа Aston Desktop, потому что в них используются другие названия классов окон и их иерархия.
В приложении консольная программа с исходником, выводящая на экран список исполняемых файлов всех приложений, иконки которых видны в трее.