Вступление
Этот проект демонстрирует методику использования программирования хуков для эмуляции мыши с помощью клавиатуры. Программа эмулятора мыши полезна в тех случаях, когда «реальная» мышь не работает (или же отсутствует). Данная статья представляет собой быстрый старт для перехвата разного рода сообщений системы.
Однажды, во время работы на старом компьютере, я столкнулся с проблемой. USB порт был сломан, а мышь PS/2 отсутствовала. Я решил написать простую программу, которая могла бы заменить мне отсутствующее устройство.
Переходим к делу
Разработка «виртуальной мыши» заключается в следующем. Прежде всего, необходимо выбрать и назначить клавиши для соответствующих действий мыши. После длительных раздумий были избраны следующие команды-клавиши:
- перемещение указатель влево (NUM4);
- переместить указатель вправо (NUM6);
- переместить указатель вверх (NUM8);
- переместить указатель вниз (NUM5);
- щелкнуть левой кнопкой мыши (NUM7);
- щелкнуть правой кнопкой мыши (NUM9).
Исходный код для этих назначений выглядит следующим образом:
1 2 3 4 5 6 7 8 9 |
// keys to control cursor #define KeyMoveLeft VK_NUMPAD4 #define KeyMoveRight VK_NUMPAD6 #define KeyMoveUP VK_NUMPAD8 #define KeyMoveDown VK_NUMPAD5 // keys to emulate mouse buttons #define KeyClickLeft VK_NUMPAD7 #define KeyClickRight VK_NUMPAD9 |
Дальше нам необходимо назначить клавишу (это может быть и комбинация клавиш), которой можно контролировать состояние включения или же отключения «виртуальной мыши». В коде мы прописываем следующее:
1 2 |
// key to switch on/off MouseEmulate software #define KeyMouseEmulateEnDis VK_F7 |
Следующими действиями является:
- создание функции для обработки нажатых клавиш;
- запуск «хука» из «главной» функции main();
Что же это такое этот хук? Хук – это механизм, с помощью которого приложения могут перехватывать события, такие как сообщения, действия мыши и нажатия клавиш. Функции, которые перехватывают события определенного типа, известны как «хуковые» процедуры. Подключаемая процедура может обрабатывать событие, которое она получает, а затем изменять или отменять его [1]. Для старта клавиатурного хука необходимо написать функцию обратного вызова (callback), которая будет вызываться и обрабатываться при нажатии на кнопку. Функция обратного вызова:
1 2 3 4 5 6 7 8 |
LRESULT CALLBACK KeyboardHook (int nCode, WPARAM wParam, LPARAM lParam) { if (nCode == HC_ACTION) if (wParam == WM_SYSKEYDOWN || wParam == WM_KEYDOWN || wParam == WM_SYSKEYUP || wParam == WM_KEYUP) CheckKey (nCode, wParam, lParam); return CallNextHookEx(hHook, nCode, wParam, lParam); } |
Здесь функция CheckKey(…)
является обработчиком команд поступивших из клавиатуры. Содержание (скелет) этой функции представлен ниже:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
void CheckKey( int nCode, WPARAM wParam, LPARAM lParam ) { … // switch on/off the MouseEmulate program if (wParam == WM_SYSKEYUP || wParam == WM_KEYUP) { if( hookStruct->vkCode == KeyMouseEmulateEnDis ) { bEmulEn = bEmulEn ? false: true; } } if(bEmulEn) { switch ( hookStruct->vkCode ) { case KeyClickLeft: { // emulating left mouse button click } break; case KeyClickRight: { // emulating right mouse button click } break; case KeyMoveLeft: { // move cursor left } break; case KeyMoveUP: { // move cursor up } break; case KeyMoveRight: { // move cursor right } break; case KeyMoveDown: { // move cursor down } break; } } } |
Можно запустить хук из «главной» функции:
1 2 3 4 5 6 7 |
int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHook, hInstance , 0); while (GetMessage(NULL,NULL,0,0)) ; // NOP while not WM_QUIT return UnhookWindowsHookEx(hHook); } |
Более подробную информацию о функции SetWindowsHookEx(…) можно получить из [2].
Предпочтительно использовать методику определения шага для перемещения курсора в зависимости от того длительно или же коротко нажата кнопка на клавиатуре. Следующая функция отвечает за эту идею:
1 2 3 4 5 6 7 |
void StepCalculate() { unsigned short speedMax = 40; unsigned short dt = tNow-tBefore; float dtMax = 30.0f; step = (int)(speedMax * dtMax/(tNow-tBefore)); } |
Здесь, speedMax
является переменной определяющей наибольший шаг (40 пикселей) перемещения курсора при длительном нажатии клавиатурной клавиши. Значение dt
характеризует разницу во времени между двумя нажатиями одной и той же клавиши (длительное нажатие клавиши рассматривается как многоразовое).
Загрузка
Скачать демонстрационный проект
Скачать исходный код проекта
Ссылки
- http://msdn.microsoft.com/en-us/library/ms644959(v=vs.85).aspx
- http://msdn.microsoft.com/en-us/library/ms644990(v=vs.85).aspx