Blog. Just Blog

Перехват и обработка Alt+Tab

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Сегодня поэкспериментируем с системными событиями. Это очень мощный и интересный инструмент, с помощью которого можно оперативно реагировать на события до того момента, как они дойдут до приложения. В качестве примера будем перехватывать окно переключения процессов, которое появляется при нажатии комбинации Alt+Tab, а также обрабатывать выбор элементов в этом окне. Начнем с описания нужных нам констант.
  1. EVENT_SYSTEM_SWITCHSTART = 0x0014
  2. EVENT_SYSTEM_SWITCHEND   = 0x0015
  3. EVENT_OBJECT_FOCUS       = 0x8005
  4.  
  5. WINEVENT_OUTOFCONTEXT    = 0x0000
  6. WINEVENT_SKIPOWNPROCESS  = 0x0002
Затем при инициализации окна или где-нибудь в начале приложения при помощи функции SetWinEventHook подписываемся на системные события EVENT_SYSTEM_SWITCHSTART и EVENT_SYSTEM_SWITCHEND. Правильнее будет сказать, что на интервал всех событий с идентификаторами, расположенными между ними, включая границы. Но, поскольку они идут друг за другом, то получается, что только на эти два события.
  1. wminitdialog:
  2.         ; Обнулить хэндл окна Alt+Tab
  3.         mov     [hwndat],0
  4.         invoke  SetWinEventHook,EVENT_SYSTEM_SWITCHSTART,EVENT_SYSTEM_SWITCHEND,\
  5.                 NULL,WinEventProc,0,0,WINEVENT_OUTOFCONTEXT+WINEVENT_SKIPOWNPROCESS
  6.         mov     [hook],eax
Останется только оформить обработчик событий WinEventProc. Для большей универсальности он будет также отвечать за обработку выбора элементов в окне Alt+Tab.

Логика обработчика следующая. При наступлении события EVENT_SYSTEM_SWITCHSTART (открытие окна Alt+Tab) устанавливается обработчик события EVENT_OBJECT_FOCUS, то есть переключение между элементами окна. Это событие срабатывает при перемещении переключателя между запущенными процессами, но обрабатываться будет только когда окно Alt+Tab активно. При наступлении события EVENT_SYSTEM_SWITCHEND (окончание выбора) обработчик переключения сбрасывается. Соответственно, на каждое из этих трех событий ваше приложение может каким-то образом реагировать.
  1. proc WinEventProc hWinEventHook:DWORD, event:DWORD, hwnd:DWORD,\
  2.                   idObject:DWORD, idChild:DWORD, idEventThread:DWORD,\
  3.                   dwmsEventTime:DWORD
  4.         pusha
  5.         ; Начало переключения
  6.         cmp     [event],EVENT_SYSTEM_SWITCHSTART
  7.         jne     @f
  8.         ; Окно Alt-Tab уже открыто?
  9.         cmp     [hwndat],0
  10.         jne     .loc_ret
  11.         mov     eax,[hwnd]
  12.         mov     [hwndat],eax
  13.         ; Установить обработчик переключения элементов
  14.         invoke  SetWinEventHook,EVENT_OBJECT_FOCUS,EVENT_OBJECT_FOCUS,NULL,\
  15.                 WinEventProc,0,0,WINEVENT_OUTOFCONTEXT+WINEVENT_SKIPOWNPROCESS
  16.         mov     [focus],eax
  17.         ; Действие при начале переключения
  18.         ...
  19.         ...
  20.         jmp     .loc_ret
  21. @@:
  22.         ; Смена фокуса на элементе
  23.         cmp     [event],EVENT_OBJECT_FOCUS
  24.         jne     @f
  25.         ; Событие произошло в окне Alt+Tab?
  26.         mov     eax,[hwnd]
  27.         cmp     [hwndat],eax
  28.         jne     .loc_ret
  29.         ; Действие при переключении
  30.         ...
  31.         ...
  32.         jmp     .loc_ret
  33. @@:
  34.         ; Завершение переключения
  35.         cmp     [event],EVENT_SYSTEM_SWITCHEND
  36.         jne     .loc_ret
  37.         ; Окно Alt-Tab еще открыто?
  38.         cmp     [hwndat],0
  39.         je      .loc_ret
  40.         ; Снять обработчик переключения элементов
  41.         invoke  UnhookWinEvent,[focus]
  42.         mov     [hwndat],0
  43.         mov     [focus],0
  44.         ; Действие при завершении
  45.         ...
  46.         ...
  47. .loc_ret:
  48.         popa
  49.         ret
  50. endp
Зная хэндл окна Alt+Tab, после создания можно творить с ним всякие разные штуки, например, изменить визуальный стиль и размер, убрать Aero-эффекты, добавить иконку, поменять текст в заголовке или еще что-нибудь. Но главная прелесть, что при выборе элемента мы можем получить информацию о нем. Делается это при помощи COM-объекта IAccessible. Для этого придется описать его интерфейс и еще несколько структур.
  1. struct DECIMAL
  2.     wReserved dw ?
  3.     union
  4.         struct
  5.             scale db ?
  6.             sign db ?
  7.         ends
  8.         signscale dw ?
  9.     ends
  10.     Hi32 dd ?
  11.     union
  12.         struct
  13.             Lo32 dd ?
  14.             Mid32 dd ?
  15.         ends
  16.         Lo64 dq ?
  17.     ends
  18. ends
  19.  
  20. struct VARIANT
  21.     union
  22.         struct
  23.             vt dw ?
  24.             wReserved rw 3
  25.             union
  26.                 llVal dq ?
  27.                 lVal  dd ?
  28.                 iVal  dw ?
  29.                 bVal  db ?
  30.             ends
  31.         ends
  32.         decVal DECIMAL
  33.     ends
  34. ends
  35.  
  36. ; Интерфейс IAccessible
  37. struct IAccessible
  38.         QueryInterface          dd ?
  39.         AddRef                  dd ?
  40.         Release                 dd ?
  41.  
  42.         GetTypeInfoCount        dd ?
  43.         GetTypeInfo             dd ?
  44.         GetIDsOfNames           dd ?
  45.         _Invoke                 dd ?
  46.  
  47.         get_accParent           dd ?
  48.         get_accChildCount       dd ?
  49.         get_accChild            dd ?
  50.         get_accName             dd ?
  51.         get_accValue            dd ?
  52.         get_accDescription      dd ?
  53.         get_accRole             dd ?
  54.         get_accState            dd ?
  55.         get_accHelp             dd ?
  56.         get_accHelpTopic        dd ?
  57.         get_accKeyboardShortcut dd ?
  58.         get_accFocus            dd ?
  59.         get_accSelection        dd ?
  60.         get_accDefaultAction    dd ?
  61.         accSelect               dd ?
  62.         accLocation             dd ?
  63.         accNavigate             dd ?
  64.         accHitTest              dd ?
  65.         accDoDefaultAction      dd ?
  66.         put_accName             dd ?
  67.         put_accValue            dd ?
  68. ends
При наступлении события появления окна Alt+Tab или переключения элемента, с помощью функции AccessibleObjectFromEvent получаем адрес интерфейса, через методы которого можно узнать все необходимые нам данные. Вот пример, как можно узнать общее количество элементов в окне переключения процессов, индекс выбранного элемента и его название, то есть название процесса, на котором остановился выбор.
  1.         ; Получить информацию о выбранном элементе
  2.         invoke  AccessibleObjectFromEvent,[hwnd],[idObject],[idChild],\
  3.                 pAcc,varChild
  4.  
  5.         ; Количество элементов в окне
  6.         mov     eax,[pAcc]
  7.         mov     eax,[eax]
  8.         stdcall dword [eax+IAccessible.get_accChildCount],[pAcc],tmp
  9.         ; [tmp] --> всего элементов в окне
  10.  
  11.         ; Выбранный элемент
  12.         mov     eax,[pAcc]
  13.         mov     eax,[eax]
  14.         stdcall dword [eax+IAccessible.get_accFocus],[pAcc],varChild
  15.         ; [varChild.lVal] --> индекс выбранного элемента
  16.  
  17.         ; Получить название выбранного элемента
  18.         push    [bstrName]
  19.         mov     eax,varChild
  20.         push    dword [eax+0Ch]
  21.         push    dword [eax+08h]
  22.         push    dword [eax+04h]
  23.         push    dword [eax]
  24.         mov     eax,[pAcc]
  25.         mov     eax,[eax]
  26.         stdcall dword [eax+IAccessible.get_accName],[pAcc]
  27.         ; [bstrName] -> указатель на указатель на строку
  28.  
  29.         ; Прибраться за собой
  30.         mov     eax,[pAcc]
  31.         mov     eax,[eax]
  32.         stdcall dword [eax+IAccessible.Release],[pAcc]
В приложении примеры программы с исходными текстами, которые обрабатывают события окна Alt+Tab, а также при появлении этого окна меняют его стиль и заголовок. Первая программа просто реагирует на события, а вторая показывает информацию о элементах окна.

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

Alt.Tab.Hook.Demo.zip (7,697 bytes)


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

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

Комментарии

Отзывы посетителей сайта о статье
ManHunter (08.03.2021 в 21:31):
На Win7x64 все работает как часы. Проблемы бесятки любой разрядности меня не волнуют.
Biginer (08.03.2021 в 12:09):
Что-то в Windows 10 не работает :(.

ООО, извините, 64 битная винда, может быть поэтому?
ManHunter (27.07.2020 в 16:19):
Угрохал несколько часов в отладчике, зато добавил описание работы с COM-объектами и еще один пример, который получает информацию о выбранном элементе в окне Alt+Tab.
ManHunter (23.07.2020 в 06:57):
Потому что в более серьезных программах одного только факта возникновения события обычно недостаточно, нужны подробности что и как. Их можно получить через AccessibleObjectFromEvent, которая возвращает COM-объект, через который, в свою очередь, вся необходимая мякотка и извлекается. Вот для работы с этими COM-объектами как раз и вызывается CoInitialize. В моем примере этого не требуется.
morgot (23.07.2020 в 01:56):
Спасибо, пригодится.
Почему в мсдн в примере перед этой апи идет вызов CoInitialize ? Что-то не пойму, вроде можно без СОМ обойтись, или некоторые события его требуют? Как то не юзал эту функцию ни разу.

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

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

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