2010-06-28 12 views
12

Estoy intentando capturar la entrada global de mouse y teclado.Comprensión del gancho de mouse y teclado de bajo nivel (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 
} 

Así que todo funciona aquí, excepto si #define TEST a poner en el Sleep, el ratón se vuelve increíblemente lento, como era de esperar si de repente solo permiten el ratón para actualizar 20 veces por segundo. Y sin el sueño, estoy vinculando la CPU al 100%. Pero está bien por ahora (eso desaparece si uso GetMessage).

Ahora, tal como lo entiendo, los ganchos de bajo nivel funcionan cambiando el contexto al proceso que lo instaló, y luego enviando al proceso algún tipo de mensaje para que ejecute la devolución de llamada del enlace. Lo que me confunde un poco, sin embargo, es por qué mi programa nunca imprimirá "msg recvd", pero imprime "el botón derecho del mouse hacia arriba/arriba" cada vez que hago clic con el botón derecho del mouse. Esto me lleva a concluir que mi MouseHookProc se invoca durante la llamada PeekMessage. Simplemente sucede que es un tipo de mensaje especial y PeekMessage devuelve 0. Pero aún necesito llamar al PeekMessage o un equivalente.

Dado que mi programa necesita hacer un montón de cosas, claramente no puedo pesar mi ciclo de bombeo de mensajes (el que llama al PeekMessage) llamando a otra función que tarda, digamos 50ms en regresar. ¿Cómo puedo leer varias veces mi programa para mantener la capacidad de respuesta del mouse y al mismo tiempo hacer un poco de trabajo pesado? En un programa de Win32 multiproceso, todavía hay una sola cola de mensajes, ¿verdad?

Actualización: Después de leer la documentación de MS, creo que sé lo que es correcto para mí. Debería generar un hilo en mi aplicación que llame al SetWindowsHookEx para registrar el gancho del mouse, y luego sentarme en su propio bucle de mensajes, y el sistema se encargará de enviar las actualizaciones del mouse a este hilo. Será libre de hacer lo que quiera dentro del MouseHookProc, y el resto de mi aplicación se ejecutará de forma independiente.

+0

¿Tiene 'MouseHookProc' dentro de un archivo DLL y' SetWindowsHookEx' y 'PeekMessage' dentro de un archivo EXE? – Oleg

+0

Quería evitar tener que inyectar una DLL. Parece más adecuado para mis necesidades para que el sistema cambie a mi programa para llamarlo 'MouseHookProc', de ahí que quiera usar el gancho de bajo nivel del mouse. Según lo entiendo, un gancho de mouse normal, si quiero que sea global, debe usar el método DLL. –

Respuesta

9

El problema es que su bucle de mensajes , quema los ciclos de CPU al 100% porque usa PeekMessage(). Windows sabe cómo mantener vivo el anzuelo, incluso si no busca mensajes, use GetMessage() para resolver su problema. Usar Sleep (1) también resolverá su problema, pero no es necesario aquí.

Why must SetWindowsHookEx be used with a windows message queue

3

En vez de hacer:

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

Interruptor esto:

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

Esto permitirá que su aplicación pueda seguir efectuando todos los mensajes en la cola hasta que esté vacía (como no tener sueño), luego renunciar a un poco de tiempo de CPU cuando la aplicación está "inactiva".

3

MouseHookProc debe residir en la DLL, de lo contrario no se puede capturar la entrada "global" (http://msdn.microsoft.com/en-us/library/ms997537.aspx)

Sobre el bucle - se puede modificar de esta manera:

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

Un gancho de mouse de bajo nivel * no * requiere una DLL, no es un gancho global como WH_MOUSE. –

6

te entres si se coloca el lugar MouseHookProc en la DLL, porque los intentos de colocarlo dentro de un archivo EXE es un error típico. Lo hice también hace muchos años.

En primer lugar, ¿cómo se puede leer en http://msdn.microsoft.com/en-us/library/ms644990.aspx:

SetWindowsHookEx se puede utilizar para inyectar una DLL en otro proceso. No se puede inyectar un DLL de 32 bits en un proceso de 64 bits, y un DLL de 64 bits no puede ser inyectado en un proceso de 32 bits. Si una aplicación requiere el uso de ganchos en otros procesos, se requiere que una llamada aplicación de 32 bits SetWindowsHookEx para inyectar una DLL de 32 bits en los procesos de 32 bits, y una llamada de aplicación 64 bits SetWindowsHookEx para inyectar una DLL de 64 bits en procesos de 64 bits. Los archivos DLL de 32 bits y 64 bits deben tener nombres diferentes.

Debe colocarlo en una DLL. Para ser exactamente si desea admitir plataformas de 32 bits y de 64 bits, debe implementar dos dlls: una DLL de 32 y 64 bits. ¿Pero por qué? ¿Y cómo funciona SetWindowsHookEx?

Si se ejecuta en un archivo EXE el código como el siguiente

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

das user32.dll solicitud para inyectar su syshook.dll en todos los demás procesos en el mismo lugar de ventanas (DLL no lo hará ser inyectado a servicios y procesos de otros usuarios registrados mediante el cambio rápido de usuario). A continuación, user32.dll llamada LoadLibrary al syshook.dll en diferentes procesos. Luego, si se llamará a la función MouseHookProc, in se llamará en el contexto del proceso que procesa el mensaje del mouse. Si el proceso no es una aplicación de consola, el código como

printf("right mouse down\n"); 

no funciona.

Así que espero que ahora no entienda por qué debe colocar MouseHookProc en una DLL.

+2

Creo que puede haber algo de confusión aquí. Por el momento, he descubierto más o menos todo lo que necesito de este tema: http://stackoverflow.com/questions/2060345/in-what-thread-does-a-low-level-mouse-and-keyboard- ¡Gracias por tomarse el tiempo de ayudarnos, sin embargo! ¡ ! –

+2

¡Es información muy interesante! Es muy interesante que 'WH_MOUSE_LL' es una exepción de la regla de inyección de DLL generel. ¡Gracias! Le recomendaría que utilice "Enviar comentarios sobre este tema a Microsoft" en http://msdn.microsoft.com/en-us/library/ms644990.aspx para sugerirle a Microsoft que cambie una pequeña documentación de 'SetWindowsHookEx' que corresponde a la información de http://msdn.microsoft.com/en-us/library/ms644986.aspx. – Oleg

+0

, ¿está diciendo que con WH_MOUSE_LL funciona sin estar en una DLL, pero para otros tipos de mensajes debe estar dentro de una DLL? – rogerdpack

Cuestiones relacionadas