views:

61

answers:

4

I'm trying to capture global mouse and keyboard input.

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
}

So everything works here, except if I #define TEST to put in the Sleep, the mouse becomes incredibly sluggish, as might be expected if I suddenly only allow the mouse to update 20 times a second. And without the sleep, I am pegging the CPU at 100%. But that's okay for now (that goes away if I use GetMessage).

Now as I understand it, the low-level hooks work by context-switching to the process which installed it, and then sending the process some kind of message to let it execute the hook callback. What confuses me a little, though, is why my program will never print "msg recvd", but it prints "right mouse down/up" whenever i click the right mouse button. This leads me to conclude that my MouseHookProc is being invoked during the PeekMessage call. It just happens to be some kind of special message and PeekMessage returns 0. But I still need to call PeekMessage or some equivalent.

Since my program needs to do a bunch of things, I clearly can't weigh down my message pumping loop (the one that calls PeekMessage) by calling another function that takes, say 50ms to return. How might I multithread my program to maintain mouse responsiveness while simultaneously doing a little heavy lifting? In a multithreaded win32 program, there is still just one message queue, right?

Update: After reading up on MS's documentation I think I know what the right thing for me to do is. I should just spawn a thread in my application which calls SetWindowsHookEx to register the mouse hook, and then sit around in its own message loop, and the system will take care of sending the mouse updates to this thread. It will be free to do whatever it wants within the MouseHookProc, and the rest of my application will run independently.

A: 

Instead of doing:

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

Switch this to:

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

This will allow your app to continue to process all messages in the queue until it's empty (like having no sleep), then give up some CPU time when the app is "idle".

Reed Copsey
A: 

MouseHookProc should reside in dll, otherwise you can not capture "global" input ( http://msdn.microsoft.com/en-us/library/ms997537.aspx )

About the loop - you can modify it like this:

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
}
Dark
A low-level mouse hook does *not* require a DLL, it is not a global hook like WH_MOUSE.
Hans Passant
+2  A: 

The problem is your message loop, it burns 100% CPU cycles because you use PeekMessage(). Windows knows how to keep the hook alive even if you don't poll for messages, use GetMessage() to solve your problem. Using Sleep(1) will solve your problem too but is not necessary here.

Hans Passant
A: 

I aksed you whether you place the place MouseHookProc in DLL, because attempts to place it inside an EXE it is a typical error. I made it also many years ago.

First of all, how you can read in http://msdn.microsoft.com/en-us/library/ms644990.aspx:

SetWindowsHookEx can be used to inject a DLL into another process. A 32-bit DLL cannot be injected into a 64-bit process, and a 64-bit DLL cannot be injected into a 32-bit process. If an application requires the use of hooks in other processes, it is required that a 32-bit application call SetWindowsHookEx to inject a 32-bit DLL into 32-bit processes, and a 64-bit application call SetWindowsHookEx to inject a 64-bit DLL into 64-bit processes. The 32-bit and 64-bit DLLs must have different names.

So you must place in a DLL. To be exactly if you want support both 32-bit and 64-bit platforms you have to implement two dlls: one 32-bit and 64-bit DLL. But why? And how SetWindowsHookEx works?

If you execute in an EXE the code like following

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

you give user32.dll request to inject your syshook.dll in all other processes on the same windows station (dll will not be injected to services and processes of other users logged through fast user switching). Then user32.dll call LoadLibrary to the syshook.dll in different processes. Then if the function MouseHookProc will be called, in will be called in the context of the process which proccess the mouse message. If the process is not a console application the code like

printf("right mouse down\n");

can not work.

So I hope now you will undestend why you must place MouseHookProc in a DLL.

Oleg
I think there might be some confusion here. For the time being I have more or less figured out everything I need from this topic: http://stackoverflow.com/questions/2060345/in-what-thread-does-a-low-level-mouse-and-keyboard-hook-callback-runThanks for taking the time to help, though!
Steven Lu
It's very interesting information! It is very interesting that `WH_MOUSE_LL` is an exeption of the generel DLL injection rule. Thanks! I would recommend you to use "Send comments about this topic to Microsoft" on http://msdn.microsoft.com/en-us/library/ms644990.aspx to suggest Microsoft to change a little documentation of `SetWindowsHookEx` corespond to the information from http://msdn.microsoft.com/en-us/library/ms644986.aspx.
Oleg