Обработка сообщений от клавиатуры в DialogBox
При разработке софта я столкнулся с таким неприятным явлением, что диалоговые окна, созданные функциями типа DialogBoxParam, не обрабатывают сообщения от клавиатуры. К таким сообщениям относятся, например, WM_KEYDOWN, WM_CHAR и WM_SYSKEYDOWN. При этом, если создать диалоговое окно функцией типа CreateWindowEx, то к нему сообщения проходят нормально. Это странное поведение связано с тем, что клавиатурные сообщения передаются напрямую элементам управления, находящимся в диалоговом окне, но не передаются самому окну. Чтобы использовать горячие клавиши, можно регистрировать их через RegisterHotKey, а затем обрабатывать сообщение WM_HOTKEY, но это очень плохое решение. Во-первых, комбинация клавиш уже может быть зарегистрирована другой программой, а во-вторых, использовать глобальные горячие клавиши для нужд локального окна - дурной тон.Есть более гибкое и универсальное решение, основанное на установке в своем процессе хука на сообщения (функция SetWindowsHookEx с параметром WH_GETMESSAGE), который будет дублировать все сообщения от клавиатуры на диалоговое окно. Перехватчик можно устанавливать как до открытия диалогового окна, так и при его инициализации, все зависит от поставленной задачи. Мне больше нравится второй вариант.
Code (Assembler) : Убрать нумерацию
- ; Обработчик сообщений диалогового окна
- cmp [msg],WM_INITDIALOG
- je .wminitdialog
- cmp [msg],WM_CLOSE
- je .wmclose
- ...
- .wminitdialog:
- ...
- ; Сохранить хэндл диалогового окна
- mov eax,[hwnddlg]
- mov [hwmain],eax
- ; Установить хук на обработку сообщений
- invoke GetCurrentThreadId
- invoke SetWindowsHookEx,WH_GETMESSAGE,GetMessageProc,NULL,eax
- ; Сохранить хэндл хука
- mov [hook],eax
- ...
- .wmclose:
- ...
- ; Снять хук с обработки сообщений
- invoke UnhookWindowsHookEx,[hook]
- ...
Осталось дописать процедуру обработки хука. У нас есть хэндл диалогового окна hwmain, который мы сохранили при инициализации, ему и будут ретранслироваться все поступающие сообщения от клавиатуры. Все прочие сообщения будут обрабатываться системой в обычном порядке.
Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------
- ; Ретранслятор сообщений диалоговому окну
- ;------------------------------------------------------------
- proc GetMessageProc nCode:DWORD,wparam:DWORD,lparam:DWORD
- pusha
- ; В lParam находится указатель на MSG
- mov eax,[lparam]
- ; Получить сообщение
- mov ebx,[eax+MSG.message]
- ; Это клавиатурное сообщение?
- cmp ebx,WM_KEYDOWN
- je @f
- cmp ebx,WM_CHAR
- je @f
- cmp ebx,WM_SYSKEYDOWN
- je @f
- ; Нет, просто пропустить сообщение дальше по цепочке
- popa
- invoke CallNextHookEx,[hook],[nCode],[wparam],[lparam]
- ret
- @@:
- ; Ретранслировать сообщение главному окну
- invoke SendMessage,[hwmain],[eax+MSG.message],\
- [eax+MSG.wParam],[eax+MSG.lParam]
- popa
- xor eax,eax
- ret
- endp
В приложении примеры программ с исходными текстами. Первая использует хук для ретрансляции клавиатурных сообщений и показывает в логе обработанные сообщения и их параметры. Вторая предназначена для сравнения, чтобы убедиться, что в обычном режиме клавиатурные сообщения диалоговым окном не обрабатываются.
Просмотров: 5489 | Комментариев: 11
Метки: Assembler
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(16.08.2017 в 09:07):
Немного поправил GetMessageProc, чтобы работало на всех системах.
user
(17.01.2016 в 01:50):
Еще такой вопрос возник по этой переключалке клавиатуры и хукам.
Выяснилось, что работает она нормально со всеми программами,
КРОМЕ ПРОНРАММ, входящих в MSOFFICE.
Причём если хучить WM_GETMESSAGE, то HOOK.DLL в памяти WINWORD.EXE присутствует, но сообщения в HOOK.DLL.MsgHookProc не попадают.
А если хучить WM_KEYBOARD_LL, тогда наоборот - в HOOK.DLL.KeyEventProc приходят все сообщения о нажатых кнопках, включая и искомые комбинации для переключения раскоадок, но HOOK.DLL отсутствует в памяти WINWORD.EXE.
Соответственно, в WINWORDe никакого переключения раскладок не происходит.
Не мудрствуя, заказал хучение И WM_GETMESSAGE И WM_KEYBOARD_LL в одной и той же HOOK.DLL - тогда она успешно инжектируется в WINWORD.EXE и до процедуры HOOK.DLL.KeyEventProc доходят все нажатия кнопок в WINWORD'е (пишется лог-файл) - но снова-таки переключения раскладок не происходит..
Лайоуты переключать пытаюсь вызовом функции
LoadKeyboardLayout("00000419",KLF_ACTIVATE); (и т.д.)
Это работает абсолютно везде, кроме MSOFFICE (любой версии от 97 до 2007 года).
И тем не менее, PUNTOSWITCHER справляется с этой задачей.
Склоняюсь к мысли, что нужно лайоуты переключать как-то по-другому, чтобы работало и с MSOFFICE'ом.
Может, у тебя есть какие-нибудь соображения по этому поводу?
А то, конечно, придётся ковырять PUNTOSWITCHER..
Выяснилось, что работает она нормально со всеми программами,
КРОМЕ ПРОНРАММ, входящих в MSOFFICE.
Причём если хучить WM_GETMESSAGE, то HOOK.DLL в памяти WINWORD.EXE присутствует, но сообщения в HOOK.DLL.MsgHookProc не попадают.
А если хучить WM_KEYBOARD_LL, тогда наоборот - в HOOK.DLL.KeyEventProc приходят все сообщения о нажатых кнопках, включая и искомые комбинации для переключения раскоадок, но HOOK.DLL отсутствует в памяти WINWORD.EXE.
Соответственно, в WINWORDe никакого переключения раскладок не происходит.
Не мудрствуя, заказал хучение И WM_GETMESSAGE И WM_KEYBOARD_LL в одной и той же HOOK.DLL - тогда она успешно инжектируется в WINWORD.EXE и до процедуры HOOK.DLL.KeyEventProc доходят все нажатия кнопок в WINWORD'е (пишется лог-файл) - но снова-таки переключения раскладок не происходит..
Лайоуты переключать пытаюсь вызовом функции
LoadKeyboardLayout("00000419",KLF_ACTIVATE); (и т.д.)
Это работает абсолютно везде, кроме MSOFFICE (любой версии от 97 до 2007 года).
И тем не менее, PUNTOSWITCHER справляется с этой задачей.
Склоняюсь к мысли, что нужно лайоуты переключать как-то по-другому, чтобы работало и с MSOFFICE'ом.
Может, у тебя есть какие-нибудь соображения по этому поводу?
А то, конечно, придётся ковырять PUNTOSWITCHER..
user
(22.12.2015 в 13:43):
[offtop]
Вообще, в порядке оффтопа, скажу, что программы, занимающиеся клавиатурой, да и вообще имеющие интерфейс для человека, должны быть очень эргономичны (вроде бы очевидная вещь..). Больше всего мне не нравятся программы, которые дрессируют юзера на выполнение каких-то ненужных и мудрёных действий.
Работу с клавиатурой в Windows при установленных трёх и более языках ввода иначе как дрессурой не назовёшь - сейчас, когда вроде уже всё нормально, иногда машинально пытаюсь переключить ENG<->RUS двумя нажатиями Ctrl+Shift, по устоявшейся многолетней привычке. Это посчитать, сколько было сделано ненужных дополнительных нажатий - выйдет внушительная цифра. А сколько было опечаток из-за того, что клавиатура всёже нормально не переключилась.. - тут в одном из моих недавних комментов была такая лажа - вместо "Ы" везде набрано "i", как в украинской раскладке. Учитывая, что в данном случае комментарии поредактировать нельзя - то эта лажа так и остаётся висеть.
[/offtop]
Вообще, в порядке оффтопа, скажу, что программы, занимающиеся клавиатурой, да и вообще имеющие интерфейс для человека, должны быть очень эргономичны (вроде бы очевидная вещь..). Больше всего мне не нравятся программы, которые дрессируют юзера на выполнение каких-то ненужных и мудрёных действий.
Работу с клавиатурой в Windows при установленных трёх и более языках ввода иначе как дрессурой не назовёшь - сейчас, когда вроде уже всё нормально, иногда машинально пытаюсь переключить ENG<->RUS двумя нажатиями Ctrl+Shift, по устоявшейся многолетней привычке. Это посчитать, сколько было сделано ненужных дополнительных нажатий - выйдет внушительная цифра. А сколько было опечаток из-за того, что клавиатура всёже нормально не переключилась.. - тут в одном из моих недавних комментов была такая лажа - вместо "Ы" везде набрано "i", как в украинской раскладке. Учитывая, что в данном случае комментарии поредактировать нельзя - то эта лажа так и остаётся висеть.
[/offtop]
ManHunter
(22.12.2015 в 09:51):
Мудрено как-то, но раз надо, значит надо.
user
(22.12.2015 в 02:01):
) получилась нормальная переключалка для трёх раскладок клавиатуры винды, замена стандартному механизму Windows. Переключестся ENG<->RUS по Ctrl+Shift, при необходимости переключить на Украинский жмём Shift+Ctrl, и тогда снова используем Ctrl+Shift, но уже для переключения между ENG<->UKR. Обратно на пару ENG<->RUS снова переключем по Shift+Ctrl и так по кругу)).
А то стандартно для переключения ENG<->RUS приходилось жать на комбинацию лишний раз (т.е. дважды), чтобы "перескочить" установленный лайоут дополнительного третьего языка (украинского).
Такой штуки сильно не хватало. Доволен.
Спасибо за идею с хуками. То, что надо.
old-dos.ru/dl.php?id=12947
А то стандартно для переключения ENG<->RUS приходилось жать на комбинацию лишний раз (т.е. дважды), чтобы "перескочить" установленный лайоут дополнительного третьего языка (украинского).
Такой штуки сильно не хватало. Доволен.
Спасибо за идею с хуками. То, что надо.
old-dos.ru/dl.php?id=12947
user
(20.12.2015 в 13:17):
Да, с глобальным хуком всё работает, спасибо.
ManHunter
(17.12.2015 в 22:54):
Ставишь глобальный системный хук на функцию в своей dll и она сама инжектится во все процессы без лишних движений с твоей стороны.
user
(17.12.2015 в 22:29):
.. в принципе, подумаю насчёт инжекта.
Заинжектировать DLL во все процессы не проблема - но тогда нужно по мере запуска новых процессов повторять эту процедуру для каждого вновь запущенного..
Заинжектировать DLL во все процессы не проблема - но тогда нужно по мере запуска новых процессов повторять эту процедуру для каждого вновь запущенного..
user
(17.12.2015 в 12:05):
Это не выход. Слишком громоздко.
Жаль.
ManHunter
(17.12.2015 в 08:30):
Пунто это делает через инжект своей dll во все процессы.
user
(17.12.2015 в 00:09):
C помощью хука клавиатуры можно сделать неплохой кейлоггер. Тут рабочий вариант, проверял:
replace.org.ua/topic/4351/
Есть ещё вариант - по таймеру делать опрос состояния интересующих клавиш. Этот вариант хоть и костыльный, но работает.
---------------------------------
Тут возникла такая проблема - переключал программно раскладки клавиатуры в одной программе (year.exe, задавал давеча вопрос по поводу глючения копирования кириллицы в клипбоард, если помнишь) - в общем, там нормально всё решилось, только по ходу дела задумал сделать удобную переключалку раскладок клавиатуры, давно такую хотел - но упирается всё в то, что С помощью ??Layout??(..) можно работать только со своим процессом, а нужно, чтобы работало для текущего процесса в системе. В принципе, пунто-свитчер это ведь как-то делает?
Никаких соображений нет по поводу реализации этой вещи? Конечно, можно помучить пунто-свитчер, но мало ли..
Готовых решений не нашёл, хотя народ интересуется этой темой.
Вот в общих чертах задавали похожий вопрос:
sql.ru/forum/447968/perekluchenie-raskladki-klaviatury
replace.org.ua/topic/4351/
Есть ещё вариант - по таймеру делать опрос состояния интересующих клавиш. Этот вариант хоть и костыльный, но работает.
---------------------------------
Тут возникла такая проблема - переключал программно раскладки клавиатуры в одной программе (year.exe, задавал давеча вопрос по поводу глючения копирования кириллицы в клипбоард, если помнишь) - в общем, там нормально всё решилось, только по ходу дела задумал сделать удобную переключалку раскладок клавиатуры, давно такую хотел - но упирается всё в то, что С помощью ??Layout??(..) можно работать только со своим процессом, а нужно, чтобы работало для текущего процесса в системе. В принципе, пунто-свитчер это ведь как-то делает?
Никаких соображений нет по поводу реализации этой вещи? Конечно, можно помучить пунто-свитчер, но мало ли..
Готовых решений не нашёл, хотя народ интересуется этой темой.
Вот в общих чертах задавали похожий вопрос:
sql.ru/forum/447968/perekluchenie-raskladki-klaviatury
Добавить комментарий
Заполните форму для добавления комментария