2010-06-28 3 views
12

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

+0

У вас есть 'MouseHookProc' внутри цикла DLL и' SetWindowsHookEx' и 'PeekMessage' внутри EXE? – Oleg

+0

Мне не хотелось вводить DLL. Кажется более подходящим для моих потребностей в том, чтобы система переключилась на мою программу, чтобы вызвать ее «MouseHookProc», поэтому я хочу использовать крючок с низким уровнем мыши. Насколько я понимаю, обычный крючок мыши, если я хочу, чтобы он был глобальным, должен использовать метод DLL. –

ответ

9

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

Why must SetWindowsHookEx be used with a windows message queue

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

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

3

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 
} 
+4

Низкоуровневый мышиный крючок * не требует DLL, это не глобальный крючок, как WH_MOUSE. –

6

Я aksed вас ли вы поместить место 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-разрядной библиотеки в 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 во всех других процессов на одной и той же станции окна (длл не будет быть введены в службы и процессы других пользователей, зарегистрированных путем быстрого переключения пользователей). Затем user32.dll позвонить LoadLibrary в syshook.dll в разных процессах. Затем, если будет вызвана функция MouseHookProc, она будет вызываться в контексте процесса, который обрабатывает сообщение мыши. Если процесс не является консольным приложением, такой код, как

printf("right mouse down\n"); 

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

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

+2

Думаю, здесь может быть какая-то путаница. На данный момент я более или менее понял все, что мне нужно, из этой темы: http://stackoverflow.com/questions/2060345/in-what-thread-does-a-low-level-mouse-and-keyboard- hook-callback-run Спасибо, что нашли время, чтобы помочь! –

+2

Очень интересная информация! Очень интересно, что 'WH_MOUSE_LL' является исключением из правила генерации DLL. Благодаря! Я бы рекомендовал вам использовать «Отправить комментарии по этой теме в Microsoft» по адресу http://msdn.microsoft.com/en-us/library/ms644990.aspx, чтобы предложить Microsoft изменить небольшую документацию «SetWindowsHookEx» на соответствие информацию из http://msdn.microsoft.com/en-us/library/ms644986.aspx. – Oleg

+0

, так что вы говорите с WH_MOUSE_LL, он работает, не будучи в DLL, но для других типов сообщений он должен находиться внутри dll? – rogerdpack

Смежные вопросы