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 for SetWindowsHookEx
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 when GetMessage
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
However, the WH_MOUSE_LL hook is not injected into another process. Instead, the context switches back to the process that installed the hook and it is called in its original context. Then the context switches back to the application that generated the event.
so here not need DLL (for what ??) and hook procedure can be placed in EXE too.
Is it a single-thread program?
in general yes. if not take to account possible system working threads, however all messages and hooks called in context of first thread
Is the hook procedure interrupting the thread?
from MSDN
This hook is called in the context of the thread that installed it. The call is made by sending a message to the thread that installed the hook. Therefore, the thread that installed the hook must have a message loop.
so may say that mouseHookProc
called inside GetMessage
call. this function wait in kernel for messages. when system want call hook procedure, it do this via KiUserCallbackDispatcher
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 in GetMessage
, 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 in GetMessage
or PeekMessage
. for APC delivery system call KiUserApcDispatcher
, for windows messages KiUserCallbackDispatcher
. both is callbacks from kernel mode, but it called under different conditions
interruption 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
or PeekMessage
, for APC - wait in alertable state functions or ZwTestAlert
). 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 procedure
KiUserExceptionDispatcher
- used for exception infrastructure - this most close to interrupt by sense,