Понимание низкоуровневого подключения мыши и клавиатуры (win32) - PullRequest
13 голосов
/ 28 июня 2010

Я пытаюсь захватить глобальный ввод с помощью мыши и клавиатуры.

LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
  if (nCode >= 0) {
    if (wParam == WM_RBUTTONDOWN) printf("right mouse down\n");
    if (wParam == WM_RBUTTONUP) printf("right mouse up\n");
  }
  return CallNextHookEx(0, nCode, wParam, lParam);
}

HHOOK mousehook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, NULL, 0);
while(true) {
  MSG msg;
  if (PeekMessage(&msg,0,0,0,PM_REMOVE)) {
    printf("msg recvd\n");
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
#ifdef TEST
  Sleep(50);
#endif
}

Так что все работает здесь, кроме того, что если я #define TEST вставлю Sleep, мышь станет невероятно вялой, как и следовало ожидать, если я вдруг позволю мыши обновляться только 20 раз в секунду. А без сна привязываю процессор на 100%. Но пока все в порядке (это исчезнет, ​​если я использую GetMessage).

Теперь, насколько я понимаю, низкоуровневые перехватчики работают путем переключения контекста на процесс, который его установил, и затем посылают процессу какое-то сообщение, чтобы позволить ему выполнить обратный вызов перехвата. Что меня немного смущает, так это то, что моя программа никогда не будет печатать «msg recvd», а печатает «правой / левой кнопкой мыши вниз / вверх» всякий раз, когда я нажимаю правую кнопку мыши. Это приводит меня к выводу, что мой MouseHookProc вызывается во время вызова PeekMessage. Просто случается, что это какое-то специальное сообщение, и PeekMessage возвращает 0. Но мне все еще нужно позвонить PeekMessage или какому-то эквивалентному.

Так как моей программе нужно делать кучу вещей, я явно не могу взвесить мой цикл прокачки сообщений (тот, который вызывает PeekMessage), вызывая другую функцию, которая требует, скажем, 50 мс для возврата. Как я могу использовать многопоточность своей программы, чтобы поддерживать отзывчивость мыши при одновременной небольшой нагрузке? В многопоточной программе win32 есть только одна очередь сообщений, верно?

Обновление: после прочтения документации MS я думаю, что знаю, что мне нужно сделать. Я должен просто создать поток в моем приложении, который вызывает SetWindowsHookEx, чтобы зарегистрировать ловушку мыши, а затем сидеть в своем собственном цикле сообщений, и система позаботится об отправке обновлений мыши в этот поток. В MouseHookProc он будет свободен делать все, что захочет, а остальная часть моего приложения будет работать независимо.

Ответы [ 4 ]

11 голосов
/ 28 июня 2010

Проблема в вашем цикле сообщений, он сжигает 100% циклов процессора, потому что вы используете PeekMessage (). Windows знает, как поддерживать ловушку, даже если вы не запрашиваете сообщения, используйте GetMessage () для решения вашей проблемы. Использование Sleep (1) также решит вашу проблему, но в этом нет необходимости.

Почему SetWindowsHookEx должен использоваться с очередью сообщений Windows

6 голосов
/ 28 июня 2010

Я уточнил, помещаете ли вы место MouseHookProc в DLL, потому что попытка поместить его в EXE это типичная ошибка. Я сделал это также много лет назад.

Прежде всего, как вы можете читать в http://msdn.microsoft.com/en-us/library/ms644990.aspx:

SetWindowsHookEx может использоваться для введения DLL в другой процесс. 32-битный DLL не может быть введена в 64-битную процесс, а 64-битная DLL не может быть впрыскивается в 32-битный процесс. Если приложение требует использования крючков в других процессах требуется что вызов 32-битного приложения SetWindowsHookEx для внедрения 32-разрядного DLL в 32-битные процессы, и 64-битный вызов приложения SetWindowsHookEx для внедрения 64-разрядных DLL в 64-битных процессах. 32-разрядный и 64-битные библиотеки DLL должны иметь разные имена.

Так что вы должны поместить в DLL. Точнее, если вы хотите поддерживать как 32-битные, так и 64-битные платформы, вам нужно реализовать две библиотеки DLL: одну 32-битную и 64-битную DLL. Но почему? А как SetWindowsHookEx работает?

Если вы выполняете в EXE код, подобный следующему

HINSTANCE hinstDLL = LoadLibrary(TEXT("c:\\myapp\\syshook.dll"));
HOOKPROC hkprcMouse = (HOOKPROC)GetProcAddress(hinstDLL, "MouseHookProc");
HHOOK hhookMouse = SetWindowsHookEx( 
                    WH_MOUSE_LL,
                    hkprcMouse,
                    hinstDLL,
                    0); 

вы даете user32.dll запрос на добавление вашего syshook.dll во все другие процессы на той же станции Windows (DLL не будут внедрены в службы и процессы других пользователей, зарегистрированных через быстрое переключение пользователей). Затем user32.dll вызов LoadLibrary на syshook.dll в разных процессах. Затем, если будет вызвана функция MouseHookProc, она будет вызвана в контексте процесса, который обрабатывает сообщение мыши. Если процесс не является консольным приложением, код типа

printf("right mouse down\n");

не может работать.

Так что я надеюсь, теперь вы поймете, почему вы должны поместить MouseHookProc в DLL.

3 голосов
/ 28 июня 2010

MouseHookProc должен находиться в dll, иначе вы не можете захватить «глобальный» ввод (http://msdn.microsoft.com/en-us/library/ms997537.aspx)

О цикле - вы можете изменить его так:

while(true) {
  MSG msg;
  while (PeekMessage(&msg,0,0,0,PM_REMOVE)) {
    printf("msg recvd\n");
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
#ifdef TEST
  DoStuff();
  Sleep(50);
#endif
}
3 голосов
/ 28 июня 2010

Вместо выполнения:

if (PeekMessage(&msg,0,0,0,PM_REMOVE)) {
    printf("msg recvd\n");
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}
Sleep(50);

Переключите это на:

while (PeekMessage(&msg,0,0,0,PM_REMOVE)) {
    // Add this potentially...
    if (msg.message == WM_QUIT)
        break;
    printf("msg recvd\n");
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}
Sleep(10);

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...