Обробкаповідомлень від клавіатури у DialogBox
При розробці софту я зіткнувся з таким неприємним явищем, що діалогові вікна, створені функціями DialogBoxParam, не обробляють повідомлення від клавіатури. До таких повідомлень відносяться, наприклад, WM_KEYDOWN, WM_CHAR та WM_SYSKEYDOWN. При цьому, якщо створити діалогове вікно функцією типу CreateWindowEx, то повідомлення проходять нормально. Ця дивна поведінка пов'язана з тим, що клавіатурні повідомлення передаються безпосередньо елементам керування, що знаходяться в діалоговому вікні, але не передаються вікну. Щоб використовувати гарячі клавіші, можна реєструвати їх через RegisterHotKey, а потім обробляти повідомлення WM_HOTKEY, але це дуже погане рішення. По-перше, комбінація клавіш вже може бути зареєстрована іншою програмою, а по-друге, використовувати глобальні гарячі клавіші потреб локального вікна - поганий тон.
Є більш гнучке та універсальне рішення, засноване на установці у своєму процесі хука на повідомлення (функція SetWindowsHookEx з параметром WH_GETMESSAGE), який дублюватиме всі повідомлення від клавіатури на діалогове вікно. Перехоплювач можна встановлювати як до відкриття діалогового вікна, так і за його ініціалізації, все залежить від поставленого завдання. Мені більше подобається другий варіант.
; Обробник повідомлень діалогового вікна 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, який ми зберегли при ініціалізації, йому і будуть ретранслюватися всі повідомлення від клавіатури. Всі інші повідомлення будуть оброблятися системою у звичайному порядку.
;------------------------------------------------- ----------- ; Ретранслятор повідомлень діалогового вікна ;------------------------------------------ ------------------ 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
Тепер у діалоговому вікні можна обробляти клавіатурні повідомлення поряд з іншими. За таким же принципом можна ретранслювати в діалогове вікно та інші повідомлення, що не обробляються, якщо такі раптом з'являться.
У програмі приклади програм з вихідними текстами. Перша використовує хук для ретрансляції клавіатурних повідомлень та показує у лозі оброблені повідомлення та їх параметри. Друга призначена для порівняння, щоб переконатися, що у звичайному режимі клавіатурні повідомлення не обробляються діалоговим вікном.