69. How do I grab events for all applications? An example system-wide hook.
System-wide hooks are a way to capture events for all applications running on the
system. Rather than spend hours duplicating someone else's excellent job, I
suggest you read Kyle Marsh's primer on the subject, titled "Win32
Hooks" which forms part of the MSDN library.
If you want to play around with hooks, I have supplied a simple skeletal
keyboard hook, which will detect the user pressing the F2 key and send a message
to the installing application, no matter who has the foreground. The code shown
here can be downloaded via the link at the bottom of this page. The code zip
includes all source for both the hook and the associated test harness
application. I don't include project files or a makefile, since I don't know
what compiler you'll be using. The sample was compiled and tested using VC7.
Note: there is a section declared as shared in the DEF file:
SECTIONS .SHARHOO READ WRITE SHARED
For obvious reasons, I keep some stuff shared across all instances of the hook (hence the section). Note
that shared items must be initialised.
#pragma data_seg(".SHARHOO")
static UINT uMsg = 0;
static HWND hWndMain = 0;
static HHOOK hKeyHook = NULL ;
#pragma data_seg()
HINSTANCE hInstance = 0;
HOOKPROC lpfnHookProc = 0;
Here's the entry point for process attach, which saves the module handle.
BOOL APIENTRY DllMain (HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
hInstance = hModule; // the DLLs instance handle.
break ;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break ;
}
return TRUE;
}
Here's where the hook gets installed:
BOOL __stdcall
InstallExampleKeyboardHook (HWND hWnd, UINT uMyMsg)
{
hWndMain = hWnd ;
uMsg = uMyMsg;
lpfnHookProc = (HOOKPROC) KeyboardFunc ;
hKeyHook = SetWindowsHookEx (WH_KEYBOARD, lpfnHookProc, hInstance, NULL);
if (hKeyHook)
return TRUE ;
else
return FALSE ;
}
Here's a skeletal keyboard hookproc. You'll note that the hookproc receives
both the keydown and keyup messages, so we have to differentiate between them
and only post a message for the keydown.
LRESULT
__stdcall
KeyboardFunc (int
nCode, WPARAM wParam, LPARAM lParam)
{
BOOL bPassToChain;
char
szDebug [100];
// Check for exception
cases...
if (nCode
< 0)
return
(CallNextHookEx (hKeyHook, nCode, wParam, lParam));
if (nCode
== HC_NOREMOVE)
return
(CallNextHookEx (hKeyHook, nCode, wParam, lParam));
switch
(wParam)
{
case
VK_F2:
wsprintf (szDebug, "F2
key message, lparam = 0x%X\n", lParam);
OutputDebugString (szDebug);
//
only send on keydown, not keyup (autorepeat)
if
(HIWORD (lParam) & 0xC000)
{
OutputDebugString ("F2 Keyup\n");
}
else
{
wsprintf (szDebug, "Sending F2
keydown message %d to hwnd 0x%X\n",
uMsg, hWndMain);
OutputDebugString (szDebug);
PostMessage (hWndMain, uMsg, 0,
0);
}
bPassToChain =
FALSE;
break;
default
:
bPassToChain = TRUE
;
break
;
}
if (bPassToChain)
return
(CallNextHookEx (hKeyHook, nCode, wParam, lParam));
else
return TRUE ; //
We have processed this key
}
Fairly
obviously, you can remove the calls to debugging code as you get confident with
your modified version..
