Подтвердить что ты не робот

Понимание низкоуровневого мыши и клавиатуры (win32)

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

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).

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

Я спросил вас, размещаете ли вы место 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. Чтобы быть точным, если вы хотите поддерживать как 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

Вместо этого:

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);

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

Ответ 4

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
}