Question

I have this code which sets up a keyboard hook for low-level events, then displays a message box.

HHOOK keyboardHook = SetWindowsHookEx (WH_KEYBOARD_LL, HookKey, hInstance, 0);

MessageBox(NULL, L"Click to exit", L"hook test", NULL);

UnhookWindowsHookEx(keyboardHook);

How do I run the application's main loop without creating a foreground window, and how do I set hInstance to capture global events?

Was it helpful?

Solution

The answer is all contained on the MSDN docs page for SetWindowsHookEx. To experiment with a sample application, create a new solution, add two projects, a console app and a DLL. Each project just needs one file. The DLL will contain the hook, and the console app installs the hook.

The code for the hook must live inside a DLL, as the MSDN docs very clearly say:"If the dwThreadId parameter is zero or specifies the identifier of a thread created by a different process, the lpfn parameter must point to a hook procedure in a DLL." Indeed, the hook clearly has to be in a DLL, or how could the shell run the code during another application's message loop? The DLL installs the hook using its HINSTANCE to identify where the function pointer lives.

The main application runs starts the hooks, and pumps messages, again just following the instructions in the MSDN docs: "...the hooking application must continue to pump messages or it might block the normal functioning of the 64-bit processes."

Note that this is very niche application — hardly ever will you need to inject a DLL like this. Please, please, think twice about doing this. For many applications, it's not necessary to capture low-level events, although immersive applications that suppress the Start menu are a reasonable use-case. More importantly though, you very rarely need to capture input sent to other applications. Check before using this code that you really need to do this. For example, recording a macro can be done just by hooking the input to your own application's window.

main.cpp

#include <windows.h>
#include <tchar.h>

__declspec(dllimport) void (__stdcall runHook)(BOOL startOrStop);
__declspec(dllimport) LRESULT (CALLBACK hookProc)(int code, WPARAM wParam, LPARAM lParam);

int _tmain(int argc, _TCHAR* argv[])
{
  runHook(true);
  MSG Msg;
  while(GetMessage(&Msg, NULL, 0, 0) > 0 && Msg.message != WM_QUIT) {
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
  }
  runHook(false);
  return 0;
}

Dll.cpp

#include <assert.h>
#include <windows.h>

static HINSTANCE dllInst = 0;
static HHOOK hookSelf = 0;

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
  dllInst = (HINSTANCE)dllInst;
  return TRUE;
}

__declspec(dllexport) LRESULT (CALLBACK hookProc)(int code, WPARAM wParam, LPARAM lParam)
{
  if (code >= 0) {
    UINT msgType = wParam;
    KBDLLHOOKSTRUCT* msgInfo = (KBDLLHOOKSTRUCT*)lParam;
    // The msgInfo contains the VK_CODE and flags.
  }  
  return hookSelf ? CallNextHookEx(hookSelf, code, wParam, lParam) : 0;
}

__declspec(dllexport) void (__stdcall runHook)(BOOL startOrStop)
{
  assert((bool)hookSelf != (bool)startOrStop);
  if (startOrStop)
    hookSelf = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, dllInst, 0);
  else {
    UnhookWindowsHookEx(hookSelf);
    hookSelf = 0;
  }
}

OTHER TIPS

One way to do this is creating a TSR(Terminate and Stay Resident) program on Windows.

On Windows, a typical TSR program consists of the following components :

  • Creating and hiding the main window.
  • Registering hot keys.
  • The loop testing events.
  • Functions responding to hot keys.
  • Functions invoked periodically by timers.

Here is a good article with code-snippets and detailed explanations about writing TSRs on Windows.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top