Перехват и обработка Alt+Tab
Сегодня поэкспериментируем с системными событиями. Это очень мощный и интересный инструмент, с помощью которого можно оперативно реагировать на события до того момента, как они дойдут до приложения. В качестве примера будем перехватывать окно переключения процессов, которое появляется при нажатии комбинации Alt+Tab, а также обрабатывать выбор элементов в этом окне. Начнем с описания нужных нам констант.Code (Assembler) : Убрать нумерацию
- EVENT_SYSTEM_SWITCHSTART = 0x0014
- EVENT_SYSTEM_SWITCHEND = 0x0015
- EVENT_OBJECT_FOCUS = 0x8005
- WINEVENT_OUTOFCONTEXT = 0x0000
- WINEVENT_SKIPOWNPROCESS = 0x0002
Code (Assembler) : Убрать нумерацию
- wminitdialog:
- ; Обнулить хэндл окна Alt+Tab
- mov [hwndat],0
- invoke SetWinEventHook,EVENT_SYSTEM_SWITCHSTART,EVENT_SYSTEM_SWITCHEND,\
- NULL,WinEventProc,0,0,WINEVENT_OUTOFCONTEXT+WINEVENT_SKIPOWNPROCESS
- mov [hook],eax
Логика обработчика следующая. При наступлении события EVENT_SYSTEM_SWITCHSTART (открытие окна Alt+Tab) устанавливается обработчик события EVENT_OBJECT_FOCUS, то есть переключение между элементами окна. Это событие срабатывает при перемещении переключателя между запущенными процессами, но обрабатываться будет только когда окно Alt+Tab активно. При наступлении события EVENT_SYSTEM_SWITCHEND (окончание выбора) обработчик переключения сбрасывается. Соответственно, на каждое из этих трех событий ваше приложение может каким-то образом реагировать.
Code (Assembler) : Убрать нумерацию
- proc WinEventProc hWinEventHook:DWORD, event:DWORD, hwnd:DWORD,\
- idObject:DWORD, idChild:DWORD, idEventThread:DWORD,\
- dwmsEventTime:DWORD
- pusha
- ; Начало переключения
- cmp [event],EVENT_SYSTEM_SWITCHSTART
- jne @f
- ; Окно Alt-Tab уже открыто?
- cmp [hwndat],0
- jne .loc_ret
- mov eax,[hwnd]
- mov [hwndat],eax
- ; Установить обработчик переключения элементов
- invoke SetWinEventHook,EVENT_OBJECT_FOCUS,EVENT_OBJECT_FOCUS,NULL,\
- WinEventProc,0,0,WINEVENT_OUTOFCONTEXT+WINEVENT_SKIPOWNPROCESS
- mov [focus],eax
- ; Действие при начале переключения
- ...
- ...
- jmp .loc_ret
- @@:
- ; Смена фокуса на элементе
- cmp [event],EVENT_OBJECT_FOCUS
- jne @f
- ; Событие произошло в окне Alt+Tab?
- mov eax,[hwnd]
- cmp [hwndat],eax
- jne .loc_ret
- ; Действие при переключении
- ...
- ...
- jmp .loc_ret
- @@:
- ; Завершение переключения
- cmp [event],EVENT_SYSTEM_SWITCHEND
- jne .loc_ret
- ; Окно Alt-Tab еще открыто?
- cmp [hwndat],0
- je .loc_ret
- ; Снять обработчик переключения элементов
- invoke UnhookWinEvent,[focus]
- mov [hwndat],0
- mov [focus],0
- ; Действие при завершении
- ...
- ...
- .loc_ret:
- popa
- ret
- endp
Code (Assembler) : Убрать нумерацию
- struct DECIMAL
- wReserved dw ?
- union
- struct
- scale db ?
- sign db ?
- ends
- signscale dw ?
- ends
- Hi32 dd ?
- union
- struct
- Lo32 dd ?
- Mid32 dd ?
- ends
- Lo64 dq ?
- ends
- ends
- struct VARIANT
- union
- struct
- vt dw ?
- wReserved rw 3
- union
- llVal dq ?
- lVal dd ?
- iVal dw ?
- bVal db ?
- ends
- ends
- decVal DECIMAL
- ends
- ends
- ; Интерфейс IAccessible
- struct IAccessible
- QueryInterface dd ?
- AddRef dd ?
- Release dd ?
- GetTypeInfoCount dd ?
- GetTypeInfo dd ?
- GetIDsOfNames dd ?
- _Invoke dd ?
- get_accParent dd ?
- get_accChildCount dd ?
- get_accChild dd ?
- get_accName dd ?
- get_accValue dd ?
- get_accDescription dd ?
- get_accRole dd ?
- get_accState dd ?
- get_accHelp dd ?
- get_accHelpTopic dd ?
- get_accKeyboardShortcut dd ?
- get_accFocus dd ?
- get_accSelection dd ?
- get_accDefaultAction dd ?
- accSelect dd ?
- accLocation dd ?
- accNavigate dd ?
- accHitTest dd ?
- accDoDefaultAction dd ?
- put_accName dd ?
- put_accValue dd ?
- ends
Code (Assembler) : Убрать нумерацию
- ; Получить информацию о выбранном элементе
- invoke AccessibleObjectFromEvent,[hwnd],[idObject],[idChild],\
- pAcc,varChild
- ; Количество элементов в окне
- mov eax,[pAcc]
- mov eax,[eax]
- stdcall dword [eax+IAccessible.get_accChildCount],[pAcc],tmp
- ; [tmp] --> всего элементов в окне
- ; Выбранный элемент
- mov eax,[pAcc]
- mov eax,[eax]
- stdcall dword [eax+IAccessible.get_accFocus],[pAcc],varChild
- ; [varChild.lVal] --> индекс выбранного элемента
- ; Получить название выбранного элемента
- push [bstrName]
- mov eax,varChild
- push dword [eax+0Ch]
- push dword [eax+08h]
- push dword [eax+04h]
- push dword [eax]
- mov eax,[pAcc]
- mov eax,[eax]
- stdcall dword [eax+IAccessible.get_accName],[pAcc]
- ; [bstrName] -> указатель на указатель на строку
- ; Прибраться за собой
- mov eax,[pAcc]
- mov eax,[eax]
- stdcall dword [eax+IAccessible.Release],[pAcc]
Просмотров: 1423 | Комментариев: 5
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(08.03.2021 в 21:31):
На Win7x64 все работает как часы. Проблемы бесятки любой разрядности меня не волнуют.
Biginer
(08.03.2021 в 12:09):
Что-то в Windows 10 не работает :(.
ООО, извините, 64 битная винда, может быть поэтому?
ООО, извините, 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 ? Что-то не пойму, вроде можно без СОМ обойтись, или некоторые события его требуют? Как то не юзал эту функцию ни разу.
Почему в мсдн в примере перед этой апи идет вызов CoInitialize ? Что-то не пойму, вроде можно без СОМ обойтись, или некоторые события его требуют? Как то не юзал эту функцию ни разу.
Добавить комментарий
Заполните форму для добавления комментария