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..

* Note that NT6+ (Vista and above) introduce the concept of integrity levels. There are four of these, low, high, medium and system. One important point about integrity levels is that a process running at integrity level N cannot hook processes running at any integrity level greater than N. Hence hooks can no longer be guaranteed to work. However they're still powerful enough to be useful, provided you don't expect to get away with hooking something like an elevated installer process :)

Download