The following code register a low level mouse hook to monitor mouse events globally.
It's the simplest working example I can get.
Compiled with VC++ 2010: cl test.cpp /link /entry:mainCRTStartup /subsystem:windows
#include <windows.h>
HWND label1 ;
//THE HOOK PROCEDURE
LRESULT CALLBACK mouseHookProc(int aCode, WPARAM wParam, LPARAM lParam){
static int msgCount = 0 ;
static char str[20] ;
SetWindowText( label1, itoa(++msgCount, str, 10) ) ;
return CallNextHookEx(NULL, aCode, wParam, lParam) ;
}
int main(){
/**/// STANDARD WINDOW CREATION PART //////////////////////////////////////////////////////
/**/
/**/ WNDCLASSEX classStruct = { sizeof(WNDCLASSEX), 0, DefWindowProc, 0, 0, GetModuleHandle(NULL), NULL,
/**/ LoadCursor(NULL, IDC_ARROW), HBRUSH(COLOR_BTNFACE+1), NULL, "winClass", NULL } ;
/**/ RegisterClassEx(&classStruct) ;
/**/
/**/ HWND mainWin = CreateWindow("winClass", "", 0, 200,200, 100,100, NULL, NULL, NULL, NULL) ;
/**/ ShowWindow(mainWin, SW_SHOWDEFAULT) ;
/**/
/**/ label1 = CreateWindow("static", "0", WS_CHILD, 5,5, 80,20, mainWin, NULL, NULL, NULL) ;
/**/ ShowWindow(label1, SW_SHOWNOACTIVATE) ;
/**/
/**/// END OF WINDOW CREATION PART ////////////////////////////////////////////////////////
//HOOK INSTALATION
HHOOK hookProc = SetWindowsHookEx(WH_MOUSE_LL, mouseHookProc, GetModuleHandle(NULL), 0) ;
//MESSAGE LOOP
MSG msg ;
while( GetMessage(&msg, NULL, 0, 0) ){
TranslateMessage(&msg) ;
DispatchMessage(&msg) ;
}
UnhookWindowsHookEx(hookProc) ;
}
It's the basic one thread, one window, one message pump example. Except for the mouse hook.
My doubt is that what this code is doing contradicts two things I've read over and over in SO, MSDN, forums, blogs, etc..
Global hook procedures must reside in a DLL
MSDN documentation forSetWindowsHookEx
confirms this by saying:If the dwThreadId parameter is zero, the lpfn parameter MUST point to a hook procedure in a DLL
A GUI thread (one with a message pump) can't be interrupted because
GetMessase
's waiting state is not alertable. Which means that whenGetMessage
blocks in wait for more messages, it cannot receive a signal that interrupts its wait state.
However, there's no DLL anywhere to be seen here, and also the hook procedure must be interrupting the thread, otherwise the program wouldn't work, and it does (I'm assuming there's only one thread in this program).
So either I've completely misinterpreted these two points or this code is working in a way that doesn't match the asynchronous procedure call approach that I was expecting.
Either way, I'm cluless as to what is happening here.
Could you please explain how this code works.
Is it a single-thread program?
Is the hook procedure interrupting the thread?
Are any of the two points above actually true?
hook procedure must be in a DLL only if hook is need be injected into another process. for
WH_MOUSE_LL
so here not need DLL (for what ??) and hook procedure can be placed in EXE too.
in general yes. if not take to account possible system working threads, however all messages and hooks called in context of first thread
from MSDN
so may say that
mouseHookProc
called insideGetMessage
call. this function wait in kernel for messages. when system want call hook procedure, it do this viaKiUserCallbackDispatcher
call. "interrupting the thread" - what you mean under interrupting ?1.) this is not true, as show this example. hook procedure must be in a DLL, only if hook must be called in context of thread which received message, so in arbitrary process context - in this case we need inject code to another process - because this and DLL need. if we called always in self process context - no injection, not need DLL
2.)
GetMessage
really wait not in alertable state, so any APC cannot be delivered when code wait inGetMessage
, but APC here absolute not related. APC and windows messaging different features. of course exist and similar points. for both APC and some windows message delivery, thread must wait in kernel . for APC in alertable state, for windows messages inGetMessage
orPeekMessage
. for APC delivery system callKiUserApcDispatcher
, for windows messagesKiUserCallbackDispatcher
. both is callbacks from kernel mode, but it called under different conditionsinterruption in exactly sense this when code execution can be interrupted at arbitrary place and interrupt routine begin executed. in this sense interrupt not exist at all in windows user mode. APC or windows messages (hook messages is special case of windows messages) never break execution at arbitrary place, but use callback mechanism instead. exceptions is special case. thread must first call some api for enter to kernel space (for windows messaging this is
GetMessage
orPeekMessage
, for APC - wait in alertable state functions orZwTestAlert
). and then kernel can use callback to user space for deliver APC or windows message. at this moment only 3 callbacks point exist from kernel to user space (this unchanged from win2000 up to win10)KiUserApcDispatcher
- used for APC delivery ,KiUserCallbackDispatcher
- used for call window procedure or hook procedureKiUserExceptionDispatcher
- used for exception infrastructure - this most close to interrupt by sense,