2010-06-28 4 views
12

J'essaye de capturer l'entrée globale de la souris et du clavier.Comprendre le hook de la souris et du clavier de bas niveau (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 
} 

que tout fonctionne ici, sauf si je #define TEST à mettre dans le Sleep, la souris devient incroyablement lent, comme on pouvait s'y attendre si je laisse tout à coup que la souris pour mettre à jour 20 fois par seconde. Et sans le sommeil, je suis en train de fixer le CPU à 100%. Mais ça va pour l'instant (ça s'en va si j'utilise GetMessage). Maintenant, si je comprends bien, les hooks de bas niveau fonctionnent en basculant le contexte vers le processus qui l'a installé, puis en envoyant au processus une sorte de message pour le laisser exécuter le rappel de hook. Ce qui m'embarrasse un peu, c'est pourquoi mon programme n'imprimera jamais "msg recvd", mais il imprime "right mouse down/up" chaque fois que je clique sur le bouton droit de la souris. Cela m'amène à conclure que mon MouseHookProc est appelé pendant l'appel PeekMessage. Il arrive juste d'être une sorte de message spécial et PeekMessage renvoie 0. Mais je dois encore appeler PeekMessage ou un équivalent. Comme mon programme doit faire un tas de choses, je ne peux clairement pas alourdir ma boucle de pompage de messages (celle qui appelle PeekMessage) en appelant une autre fonction qui prend, disons 50ms à retourner. Comment pourrais-je multithread mon programme pour maintenir la réactivité de la souris tout en faisant un peu de levage lourd? Dans un programme Win32 multithread, il n'y a toujours qu'une seule file d'attente de messages, n'est-ce pas? Mise à jour: Après avoir lu la documentation de MS, je pense savoir ce que je dois faire. Je devrais juste engendrer un fil dans mon application qui appelle SetWindowsHookEx pour enregistrer le crochet de souris, et puis asseoir autour dans sa propre boucle de message, et le système prendra soin d'envoyer les mises à jour de souris à ce fil. Il sera libre de faire tout ce qu'il veut dans le MouseHookProc, et le reste de mon application fonctionnera indépendamment.

+0

Avez-vous 'MouseHookProc' dans une DLL et' SetWindowsHookEx' et 'PeekMessage' dans un fichier EXE? – Oleg

+0

Je voulais éviter d'avoir à injecter une DLL. Il semble plus adapté à mes besoins pour que le système bascule vers mon programme pour appeler son 'MouseHookProc', d'où la raison pour laquelle je veux utiliser le hook bas de la souris. Si je comprends bien, un hook de souris régulier, si je veux qu'il soit global, doit utiliser la méthode DLL. –

Répondre

9

Le problème est votre boucle de message , il brûle 100% des cycles CPU parce que vous utilisez PeekMessage(). Windows sait comment conserver le hook en vie même si vous n'interrogez pas les messages, utilisez GetMessage() pour résoudre votre problème. Utiliser Sleep (1) résoudra également votre problème mais n'est pas nécessaire ici.

Why must SetWindowsHookEx be used with a windows message queue

3

Au lieu de faire:

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

Passer ceci:

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

Cela permettra à votre application de continuer à traiter tous les messages dans la file d'attente jusqu'à ce qu'il soit vide (comme ne pas avoir le sommeil), puis abandonner un peu de temps CPU lorsque l'application est "inactive".

3

MouseHookProc doit se trouver dans dll, sinon vous ne pouvez pas capturer l'entrée « globale » (http://msdn.microsoft.com/en-us/library/ms997537.aspx)

A propos de la boucle - vous pouvez le modifier comme ceci:

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 hook de souris de bas niveau ne nécessite * pas * de DLL, ce n'est pas un hook global comme WH_MOUSE. –

6

Je vous aksed si vous placez le lieu MouseHookProc dans DLL, parce que les tentatives de le placer dans un fichier EXE est une erreur typique. Je l'ai fait aussi il y a plusieurs années.

Tout d'abord, comment vous pouvez lire dans http://msdn.microsoft.com/en-us/library/ms644990.aspx:

SetWindowsHookEx peut être utilisé pour injecter une DLL dans un autre processus. Une DLL 32 bits ne peut pas être injectée dans un processus 64 bits et une DLL 64 bits ne peut pas être injectée dans un processus 32 bits. Si une application nécessite l'utilisation de crochets dans d'autres processus, il est nécessaire qu'un appel SetWindowsHookEx application 32 bits pour injecter 32 bits DLL dans les processus 32 bits et une application 64 bits appel SetWindowsHookEx pour injecter une DLL 64 bits dans des processus 64 bits. Les DLL 32 bits et 64 bits doivent avoir des noms différents.

Vous devez donc placer dans une DLL. Pour être exactement si vous voulez supporter les plates-formes 32 bits et 64 bits, vous devez implémenter deux DLL: une DLL 32 bits et 64 bits. Mais pourquoi? Et comment fonctionne SetWindowsHookEx?

Si vous exécutez dans un EXE comme le code suivant

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

vous donnez user32.dll demande d'injecter votre syshook.dll dans tous les autres processus sur la même station Windows (dll ne sera pas être injecté dans les services et les processus d'autres utilisateurs connectés via un changement d'utilisateur rapide). Puis user32.dll appelez LoadLibrary au syshook.dll dans différents processus. Ensuite, si la fonction MouseHookProc sera appelée, on appellera dans le contexte du processus qui traite le message de la souris. Si le processus n'est pas une application console, le code

printf("right mouse down\n"); 

ne peut pas fonctionner.

Donc j'espère maintenant que vous ne saurez pas pourquoi vous devez placer MouseHookProc dans une DLL.

+2

Je pense qu'il pourrait y avoir une certaine confusion ici. Pour le moment j'ai plus ou moins compris tout ce dont j'ai besoin de ce sujet: http://stackoverflow.com/questions/2060345/in-what-thread-does-a-low-level-mouse-and-keyboard- hook-callback-run Merci d'avoir pris le temps de m'aider! –

+2

C'est une information très intéressante! Il est très intéressant que 'WH_MOUSE_LL' soit une exeption de la règle d'injection de DLL de Generel. Merci! Je vous recommande d'utiliser "Envoyer des commentaires sur ce sujet à Microsoft" sur http://msdn.microsoft.com/en-us/library/ms644990.aspx pour suggérer à Microsoft de modifier une petite documentation de 'SetWindowsHookEx' corespond à la informations à partir http://msdn.microsoft.com/en-us/library/ms644986.aspx. – Oleg

+0

donc vous dites avec WH_MOUSE_LL cela fonctionne sans être dans une DLL, mais pour les autres types de message, il doit être dans une DLL? – rogerdpack

Questions connexes