Обработка нажатия на BUTTON разными кнопками мыши
Обычно элементы управления типа BUTTON в окне приложения реагируют только на обычное нажатие. При клике левой кнопкой мыши на кнопке или при нажатии на клавиатуре клавиши пробел, окну отправляется сообщение WM_COMMAND с параметром BN_CLICKED и идентификатором нажатой кнопки. На этом все. Но иногда надо сделать так, чтобы кнопки реагировали на клик не только левой кнопки мыши, а, например, правой кнопки или колесика. Этого можно добиться через субклассирование обработчика кнопки, ведь по сути она является самостоятельным окном. При инициализации основного окна повесим субклассированный обработчик на нужную нам кнопку:Code (Assembler) : Убрать нумерацию
- ; Субклассировать кнопку
- invoke GetDlgItem,[hwnddlg],ID_BTN
- mov ebx,eax
- ; Установить наш собственный обработчик
- invoke SetWindowLong,ebx,GWL_WNDPROC,ButtonProc
- ; Сохранить хэндл предыдущего обработчика
- invoke SetWindowLong,ebx,GWL_USERDATA,eax
Code (Assembler) : Убрать нумерацию
- ; Пользовательское сообщение от обработчика кнопки
- WM_BUTTON_CLICK = WM_USER + 275
- szMsg1 db 'Left mouse Button',0
- szMsg2 db 'Right mouse Button',0
- szMsg3 db 'Middle mouse Button',0
Для взаимодействия с родительским окном ему отправляется сообщение, что его дочерняя кнопка нажата. В lparam и wparam родительскому окну можно отправлять, например, идентификатор кнопки в окне и тип кнопки, которой по ней нажали. В нашем случае родительскому окну передается указатель на строку с названием нажатой кнопки мыши.
Code (Assembler) : Убрать нумерацию
- ; Отправить наше сообщение родительскому окну кнопки
- invoke GetParent,[hBtn]
- invoke PostMessage,eax,WM_BUTTON_CLICK,0,szMsg1
Чтобы обработать левый клик мышкой и нажатие кнопки с клавиатуры, велик соблазн использовать обычный обработчик кнопок в родительском окне через WM_COMMAND и BN_CLICKED. Но здесь есть одна особенность. При симуляции переключения кнопки в нажатое положение сперва срабатывает обработчик главного окна, как если бы мы нажали на эту кнопку левой кнопкой мыши, и в нашем случае это сообщение отправляется независимо от того, какой кнопкой мыши мы щелкнули. Поэтому для нашей программы обычная связка не годится, все придется пропускать через субклассированный обработчик.
Но раз мы симулировали нажатие, то должны реализовать и отжатие кнопки через некоторое время, сама она в прежнее положение не вернется. Первым приходит в голову решение с задержкой через Sleep, но тут есть большие минусы. Во-первых, на время выполнения команды Sleep поток программы будет остановлен. Во-вторых, если кнопка мыши будет отпущена до момента завершения команды Sleep, то кнопка в окне так и останется залипшей до завершения паузы. Обойти обе проблемы позволяет установка в субклассированном обработчике таймера отжатия и обработка сообщений WM_LBUTTONUP, WM_MBUTTONUP, WM_RBUTTONUP. При симуляции нажатия взводится таймер, по срабатыванию которого кнопка будет отжата. Если отпущена кнопка мыши до срабатывания таймера, то таймер сбрасывается, а кнопка в окне отжимается немедленно. Таймер необходим, чтобы исключить ситуации, когда кнопка в окне была нажата, а затем при зажатой кнопке мыши курсор был выведен за ее пределы. В этом случае при отпускании кнопки мыши, кнопка в окне об этом никак не узнает, и, соответственно, останется в залипшем состоянии.
Code (Assembler) : Убрать нумерацию
- ;--------------------------------------------
- ; Обработка нажатия на кнопку
- ;--------------------------------------------
- proc ButtonProc hBtn:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
- ; Нажата кнопка на клавиатуре
- cmp [uMsg],WM_KEYDOWN
- je .chk_char
- ; Нажата средняя кнопка мыши
- cmp [uMsg],WM_LBUTTONDOWN
- je .click_left
- ; Нажата средняя кнопка мыши
- cmp [uMsg],WM_MBUTTONDOWN
- je .click_middle
- ; Нажата правая кнопка мыши
- cmp [uMsg],WM_RBUTTONDOWN
- je .click_right
- ; Отжата какая-нибудь кнопка мыши
- cmp [uMsg],WM_LBUTTONUP
- je .unclick
- cmp [uMsg],WM_MBUTTONUP
- je .unclick
- cmp [uMsg],WM_RBUTTONUP
- je .unclick
- ; Сработал таймер отжатия
- cmp [uMsg],WM_TIMER
- je .unclick
- .process_ok:
- ; Получить адрес предыдущего обработчика
- invoke GetWindowLong,[hBtn],GWL_USERDATA
- ; Передать управление предыдущему обработчику
- invoke CallWindowProc,eax,[hBtn],[uMsg],[wParam],[lParam]
- ret
- .chk_char:
- ; Получить скан-код нажатой кнопки
- mov eax,[wParam]
- ; Пробел
- cmp al,VK_SPACE
- je .click_left
- ; Кнопка Application
- cmp al,VK_APPS
- je .click_right
- ; Неизвестные кнопки не обрабатываем
- jmp .process_ok
- .click_left:
- ; Установить статус кнопки "нажата"
- invoke SendMessage,[hBtn],BM_SETSTATE,TRUE,0
- ; Отправить сообщение родительскому окну кнопки
- invoke GetParent,[hBtn]
- invoke PostMessage,eax,WM_BUTTON_CLICK,0,szMsg1
- ; Установить таймер нажатия
- invoke SetTimer,[hBtn],1,300,NULL
- jmp .exit_proc
- .click_right:
- ; Установить статус кнопки "нажата"
- invoke SendMessage,[hBtn],BM_SETSTATE,TRUE,0
- ; Отправить сообщение родительскому окну кнопки
- invoke GetParent,[hBtn]
- invoke PostMessage,eax,WM_BUTTON_CLICK,1,szMsg2
- ; Установить таймер нажатия
- invoke SetTimer,[hBtn],1,300,NULL
- jmp .exit_proc
- .click_middle:
- ; Установить статус кнопки "нажата"
- invoke SendMessage,[hBtn],BM_SETSTATE,TRUE,0
- ; Отправить сообщение родительскому окну кнопки
- invoke GetParent,[hBtn]
- invoke PostMessage,eax,WM_BUTTON_CLICK,2,szMsg3
- ; Установить таймер нажатия
- invoke SetTimer,[hBtn],1,300,NULL
- jmp .exit_proc
- .unclick:
- ; Сбросить статус кнопки "нажата"
- invoke SendMessage,[hBtn],BM_SETSTATE,FALSE,0
- invoke KillTimer,[hBtn],1
- .exit_proc:
- xor eax,eax
- ret
- endp
В приложении пример программы с исходным текстом, реализующей кнопку с обработчиком из статьи.
Просмотров: 9040 | Комментариев: 3
Внимание! Статья опубликована больше года назад, информация могла устареть!
Добавить комментарий
Заполните форму для добавления комментария
Сменив её на "более дорогую" 8-кнопочную (программируемую), теперь хочу заставить винду пересылать нажатия:
кнопка6 в DIMOUSE_BUTTON5 или DIMOFS_BUTTON5,
кнопка7 в DIMOUSE_BUTTON6 или DIMOFS_BUTTON6,
кнопка8 в DIMOUSE_BUTTON7 или DIMOFS_BUTTON7.
Поддержка производителя этой мыши что-то "обещала", но ни драйвер, ни программку так и не починили! А в начале/середине того года прекратила поддержку 32-бит, окончательно уйдя в GG для бесятки :(
USB-снифферы ловят приходящие от мыши 9 байт, по которым можно отследить не только номер "отправляемой" (запрограммированной) кнопки самым первым байтом, но и физической (внутренний номер кнопки) предпоследним байтом, и мне не понятно, почему даже «программа (или драйвер?) от производителя этой мышки» в упор игнорирует верхние три разряда самого первого байта! Одним фильтром мыши|hid здесь не обойтись (нужен "свой" hidusb.sys/mouhid.sys)?