Blog. Just Blog

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

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Сегодняшний пример - получение координат иконки в трее. Для чего это надо, я поясню чуть ниже. Сам код основан на уже знакомом вам переборе иконок в трее, поэтому практически все структуры будут такими же. Теоретическая часть работы с треем расписана там же, дублировать ее не буду. Начнем с описания всех необходимых структур и данных:
  1. ; Сегмент данных
  2. section '.data' data readable writeable
  3.  
  4. ; Структура пользовательских данных иконки
  5. struct EXTRADATA
  6.         Wnd dd ?
  7.         uID dd ?
  8. ends
  9.  
  10. class1     db 'Shell_TrayWnd',0    ; Название класса окна трея
  11. class2     db 'TrayNotifyWnd',0    ; Название класса панели уведомлений
  12. class3     db 'SysPager',0         ; Трей
  13. class4     db 'ToolbarWindow32',0  ; Панель с иконками
  14.  
  15. ; Структура для кнопки
  16. button     TBBUTTON
  17. ; Структура для пользовательских данных иконки
  18. extra      EXTRADATA
  19. ; Иконка в трее
  20. node       NOTIFYICONDATA
  21. ; Координаты иконки в трее
  22. rc         RECT
  23.  
  24. ICON_ID = 777
  25.  
  26. hInstance  dd ?    ; Хэндл приложения
  27. hToolbar   dd ?    ; Хэндл окна с иконками
  28. IconsCount dd ?    ; Количество иконок в трее
  29. ProcId     dd ?    ; Id процесса
  30. hProcess   dd ?    ; Хэндл процесса
  31. lpData     dd ?    ; Указатель на блок памяти
  32. BytesRead  dd ?    ; Количество прочитанных символов
Но теории все равно не избежать. В отличие от упомянутого кода, мы будем проверять, чтобы очередная иконка в трее принадлежала нашему процессу и при этом ее идентификатор совпадал с нужным. Если все условия выполнены, то отправляем окну трея сообщение TB_GETITEMRECT и получаем указатель на структуру, в которой содержатся координаты иконки относительно окна трея. С помощью ReadProcessMemory читаем эти данные и преобразуем в абсолютные экранные координаты функцией MapWindowPoints.

Не забываем про особенности чтения памяти 64-битного процесса из 32-битного. Пример реализации этого мощного колдунства также подробно расписан в статье по ссылке из первого абзаца. Тут приведен пример для x86.
  1.         ; Это наша иконка?
  2.         mov     eax,[hwnddlg]
  3.         cmp     eax,[extra.Wnd]
  4.         ; Нет, пропустить
  5.         jnz     .loc_loop
  6.  
  7.         ; Это именно та иконка?
  8.         cmp     [extra.uID],ICON_ID
  9.         ; Нет, пропустить
  10.         jnz     .loc_loop
  11.  
  12.         ; Получить координаты иконки в трее
  13.         invoke  SendMessage,[hToolbar],TB_GETITEMRECT,[IconsCount],[lpData]
  14.         ; Прочитать данные координат иконки
  15.         invoke  ReadProcessMemory,[hProcess],[lpData],rc,\
  16.                 dword sizeof.RECT,BytesRead
  17.         or      eax,eax
  18.         jz      .clear_memory
  19.         ; Прочиталась вся структура?
  20.         cmp     [BytesRead],sizeof.RECT
  21.         jnz     .clear_memory
  22.  
  23.         ; Преобразовать оконные координаты в экранные
  24.         invoke  MapWindowPoints,[hToolbar],HWND_DESKTOP,rc,2
В современных системах, начиная с Windows 7 и Windows Server 2008, появился более удобный способ для получения координат иконки в трее - функция Shell_NotifyIconGetRect. В большинстве случаев для нее не надо искать окно трея, не надо лезть в память процесса, не надо играться с разрядностью системы. Список необходимых данных тоже заметно сократится:
  1. ; Сегмент данных
  2. section '.data' data readable writeable
  3.  
  4. ; Структура для идентификации иконки
  5. struct NOTIFYICONIDENTIFIER
  6.         cbSize   dd ?
  7.         hWnd     dd ?
  8.         uID      dd ?
  9.         guidItem rb 16
  10. ends
  11.  
  12. ; Иконка в трее
  13. node       NOTIFYICONDATA
  14. ; Идентификатор иконки
  15. notify     NOTIFYICONIDENTIFIER
  16. ; Координаты иконки в трее
  17. rc         RECT
Если не требуется проверять видимость иконки, то весь код получения координат иконки в трее сократится до нескольких строчек. Главное только перед вызовом функции правильно заполнить структуру для идентификации иконки:
  1.         ; Заполнить структуру для идентификации иконки
  2.         mov     [notify.cbSize],sizeof.NOTIFYICONIDENTIFIER
  3.         mov     eax,[hwnddlg]
  4.         mov     [notify.hWnd],eax
  5.         mov     [notify.uID],ICON_ID
  6.  
  7.         ; Получить координаты иконки в трее
  8.         invoke  Shell_NotifyIconGetRect,notify,rc
Позиция иконки возвращается сразу же в экранных координатах, что тоже хорошо, дополнительно ничего преобразовывать не надо.

Важно помнить, что координаты иконки в трее не являются неизменными значениями. Пользователь может перетащить иконку на другую позицию или вообще скрыть иконку через настройки, вновь запущенное приложение может подвинуть иконку на другую позицию, Проводник может аварийно завершить работу и перезапуститься, в результате чего трей будет перерисован. Панель задач может изменить размер или положение на экране. Да мало ли чего может произойти. Поэтому обязательно надо заново получать координаты иконки перед тем, как выполнять связанные с этим действия.

Вот так мы плавно подошли к ответу на вопрос для чего это вообще надо. Дело в том, что система не предоставляет средств для полноценной обработки действий с мышью над иконкой в трее. Например, с помощью обычного обработчика сообщений совершенно невозможно отследить сообщения типа WM_MOUSEHOVER, WM_MOUSELEAVE и WM_MOUSEWHEEL. Но, зная координаты иконки, а затем сопоставив их с координатами события, это можно реализовать. Например, в следующем примере по таймеру можно опрашивать координаты иконки в трее и позицию курсора:
  1.         ; Получить позицию курсора
  2.         invoke  GetCursorPos,pt
  3.         ; Курсор находится внутри прямоугольника иконки?
  4.         invoke  PtInRect,rc,[pt.x],[pt.y]
  5.         or      eax,eax
  6.         ; Да
  7.         jnz     cursor_over_icon
Колесико мыши обрабатывается примерно так же, только не по таймеру, а через установку глобального хука. Но об этом я расскажу в одной из следующих статей.

В приложении примеры программ с исходными текстами, которые отслеживают изменение положения иконки в трее и перемещение курсора над этой иконкой.

Примеры программ с исходными текстами (FASM)Примеры программ с исходными текстами (FASM)

Get.Tray.Icon.Position.Demo.zip (10,137 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 866 | Комментариев: 3

Метки: Assembler, иконки
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (12.11.2021 в 11:18):
Добавил пример с Shell_NotifyIconGetRect, архив обновлен.
ManHunter (11.11.2021 в 21:51):
Наверное, вот это
ЦитатаMinimum supported client Windows 7 [desktop apps only]
Minimum supported server Windows Server 2008 R2 [desktop apps only]

и то, что один фиг надо определять видимость иконки через все эти чтения памяти Проводника. Так что особого выигрыша оно не дает.

Но в остальном мысль правильная, обязательно добавлю в статью. Спасибо!
DRON (11.11.2021 в 20:39):
А что не так с Shell_NotifyIconGetRect?

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2025
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0057 сек.) / Память: 4.5 Mb
Наверх