Получение списка иконок в трее

Иногда приложению требуется получить список иконок, находящихся в трее, а также список приложений, которые их туда разместили. Это может быть нужно для обнаружения некоторых скрывающихся приложений, для эмуляции кликов на иконках, ну или просто для спортивного интереса. В любом случае для получения списка иконок надо сделать следующее: найти в трее панель с иконками, при помощи сообщения 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, потому что в них используются другие названия классов окон и их иерархия.

В приложении консольная программа с исходником, выводящая на экран список исполняемых файлов всех приложений, иконки которых видны в трее.
Теги:
иконки, трей
Добавлено: 10 Апреля 2018 20:02:48 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...