Вступление

Этот проект демонстрирует методику использования программирования хуков для эмуляции мыши с помощью клавиатуры. Программа эмулятора мыши полезна в тех случаях, когда «реальная» мышь не работает (или же отсутствует). Данная статья представляет собой быстрый старт для перехвата разного рода сообщений системы.

Однажды, во время работы на старом компьютере, я столкнулся с проблемой. 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 характеризует разницу во времени между двумя нажатиями одной и той же клавиши (длительное нажатие клавиши рассматривается как многоразовое).

 

Загрузка

Скачать демонстрационный проект

Скачать исходный код проекта

 

Ссылки

  1. http://msdn.microsoft.com/en-us/library/ms644959(v=vs.85).aspx
  2. http://msdn.microsoft.com/en-us/library/ms644990(v=vs.85).aspx