Global system hooks allow an application to intercept Windows messages intended for other applications. This has always been difficult (impossible, according to MSDN) to implement in C#. This article attempts to implement global system hooks by creating a DLL wrapper in C++ that posts messages to the hooking application's message queue. Put simply, this lets you implement any type of global Windows hook from managed code.
Why Global System Hooks and C# Don't Play Well Together
Global system hooks have always been a problem for .NET developers. To understand why, we first need to discuss how hooks work at a system level and how Windows processes these event notifications.
Normally, an application implements hooks by creating a callback function and informing Windows of this function. When Windows processes a hookable event, such as mouse movement or the creation of a window, it calls this callback function, which the hooking application handles accordingly. With local hooks (when an application hooks other events taking place in the same process), this callback function can be located anywhere, and local hooks can be written pretty easily in C# -- a quick look around CodeProject reveals several examples. Unfortunately, Windows is not so permissive with global system hooks, which allow one process to intercept events from another process. Windows insists that the callback functions for global system hooks be located in DLLs. Neither C# nor VB.NET are able to produce standard Windows DLLs, which means they're also unable to process global system hooks -- the MSDN library even explicitly says global system hooks cannot be handled within managed code applications. How discouraging!
Luckily, C# programmers are a clever and crafty bunch. Michael Kennedy, in his CodeProject article "Global System Hooks in .NET", came up with a workaround. Using C++, he created a DLL that essentially worked as a wrapper to managed code. The C++ DLL registered a callback function with Windows. When a hook notification arrived in that callback, it called a managed code function. Using this technique, he was able to trap several hook notifications, such as low-level keyboard and mouse events. The problem he discovered, though, was that Windows changes the execution context when sending out certain hook notifications. When Windows hooked a low-level mouse or keyboard event, it called any hooking application from within the context of the hooking application. But for most other events, the callback function was called from the context of the hooked application. This essentially meant that his wrapper functions couldn't be used with most Windows hooks.
I've created my own workaround to this problem, although as you'll see below, it comes with some definite caveats. In a nutshell, what I've done is (like Kennedy) to create a C++ DLL which contains the actual callback function. However, the DLL doesn't just serve as a wrapper to a managed code delegate. Instead, when it receives a hook notification, it posts a custom Windows message to the managed application's Windows message queue. This message can then be intercepted in the C# program's
WndProc event. So, just as a window regularly receives messages like
WM_MOUSEMOVE, your managed application will now also receive messages like
The code consists of three projects. First, there's the GlobalCbtHook, a C++ project which creates the DLL used for the callback functions. Second, there's GlobalHooksTest, a simple application designed to demonstrate this code, which implements
WH_CBT hooks. The third (and coolest) of the three projects is TransparencyMenu. This is also a demonstration project, showing an actual practical application of global system hooks.
GlobalCbtHook -- the DLL Project
This project is deceptively simple. There are, however, a few quirks to my programming, which have created the potential for some bugs in your application, as described below. One other note: with some minor exceptions, all the similarly-named functions in the DLL do the same thing, just for different types of hooks. That is,
InitializeShellHook work in the same way -- so in my explanations below, I use
ABC as a sort of generic hook.
Warning One: SetProp and GetProp
Because Windows injects the callback function into all running processes, there will always be multiple instances of this code loaded. Yet they all need to share at least one piece of information, namely, the handle to the window that needs to receive the hook event notifications. After several frustrating hours reading up on shared memory, I decided I wanted an easier solution. That's when the MSDN documentation threw me a lifesaver in the form of
SetProp are API functions that allow you to associate any type of handle with any window. That is, if you have a window handle (
hWnd), you can associate any other handle with it, and in such a way that any application can access that data. Luckily, there's one window handle that's essentially constant in any Windows session, the desktop handle. So, in order to store the handle to the window receiving hook events, I use the desktop window's properties. This is kind of a hack, but works pretty reliably and effectively. The
InitializeAbcHook functions checks to see if there is already a handle named
WILSON_HOOK_HWND_ABC already associated with the desktop window. Assuming there's not, this handle is set to the handle of the window that will receive the hook events.
This code is in
if (GetProp(GetDesktopWindow(), "WILSON_HOOK_HWND_CBT") != NULL)
RegisterWindowMessage("WILSON_HOOK_CBT_REPLACED"), 0, 0);
SetProp(GetDesktopWindow(), "WILSON_HOOK_HWND_CBT", destination);
SetProp is used in
CbtHookCallback to retreive the window handle:
HWND dstWnd = (HWND)GetProp(GetDesktopWindow(),
There is one major problem to this approach: it means that only one application can be hooking these events at a time. When a second application (that uses the same DLL) starts hooking events, a message (
WILSON_HOOK_CBT_REPLACED) is sent to the first application, informing it that it's now out of luck and no longer hooking anything. So, if you use this code in your own software, it'd be a good idea to recompile it, changing the WILSON_HOOK strings to something different, so that your application will not conflict with any other software using this approach.
Warning Two: Return Values
The other major problem with my particular approach to hooking is that it doesn't allow you to return a value when a hook callback is called. Normally, a hook callback function is allowed to return a value, which can affect whatever event is happening that generated the hook. For instance, when the CBT
CREATEWND hook is raised, setting a non-zero return value prevents the window from being created. This functionality could be used to prevent certain classes or types of windows from being created. However, in its current form, my solution doesn't allow you to set a return value. There is, theoretically, a fairly easy workaround for this. I use the
SendNotifyMessage in the C++ DLL to send the message to the hooking application, which means the DLL just posts a message and then doesn't wait around to see if the hooking application actually processes it. However, you could instead use the
SendMessage command, which forces the DLL to wait for the hooking application to process the hook and return the value set in
Message.Result in the managed code. The downside to this approach, and the reason I didn't implement it this way, is that it can very easily lead to system crashes. If your managed code encounters an unhandled exception, execution won't return to the calling
SendMessage function, which in turn hangs the application being hooked. A dozen hard reboots in the span of about 20 minutes convinced me of the wisdom of
The RegisterWindowMessage Function
My code uses the
RegisterWindowMessage function. This is an API function which generates a unique message ID that can be used among multiple applications. When one application registers a message with a certain name, any other application that registers a message of the same name will get the same message ID in response. All the messages posted by the DLL use message IDs generated by this function. Thus, managed code must also call this function to know what message IDs to watch for in
Inside the DLL
The C++ DLL contains code to handle eight different types of hooks. If you are so inclined, you should be able to follow the model and easily create additional code to handle the other hook types. For each type of hook, there are three functions in the DLL. These three functions are more-or-less the same between all the different hook types.
InitializeAbcHook is an exported function that's called from managed code. The first parameter is the ID of the thread to hook. Setting this to zero will hook all processes -- giving you a global hook. You could also use this to only hook events from one thread, which in many scenarios would be far more efficient than hooking events in all threads. The second parameter is the handle of the window that hook events will be broadcast to. This should be the handle of a form or a control. The
InitializeAbcHook function checks to see if a message handle has already been stored (a.k.a., another application is already hooking using our DLL), then stores this handle, and finally installs the hook using this line:
hookAbc = SetWindowsHookEx(WH_ABC, (HOOKPROC)AbcHookCallback,
UninitializeAbcHook just removes the hook. Make sure your managed code application calls this before quitting -- if you don't, the DLL will keep hooking, even though your application (that's supposed to receive the hooks) has ended. This can lead to some major problems, as well as needlessly degrades performance.
AbcHookCallback is the actual callback function, and varies a bit depending on the hook type. For the simplest hooks, like mouse and keyboard hooks, this function just sends the necessary message to the queue of the hooking application, and then continues the hook chain by calling
CallNextHookEx. For hooks like CBT or Shell, there are multiple types of events that could be occuring -- such as
CREATEWND. The callback function determines which type of sub-hook is being called, and then raises the appropriate event. For
GetMsg, there is too much data to send to the hooking application using only one message. For these two hooks, after all, there are four pieces of data we want to know in our managed application: the handle of the hooked window, the message that was hooked, and the
wParam values -- however, when sending our own messages to the hooking application, we can only send data in
wParam. So these two callback functions send two messages, one with the handle and intercepted message, the other with the intercepted
wParam. They also have a check to prevent infinite recursion --
GetMsg sends a message to the hooking application, which is in turn intercepted by the same callback function that posted the message.
The Managed Code: GlobalHooks.cs
I've created a class called
GlobalHooks which functions as an intermediary between a managed application and the DLL, making it easy to implement global hooks in your own projects. The class contains an abstract internal class called
Hook, as well as eight subclasses of
Hook, one for each type of hook supported.
To use global system hooks in your own code, the first thing to do is create an instance of the
private GlobalHooks _GlobalHooks;
When you instantiate this class, you must pass it a window handle:
_GlobalHooks = new GlobalHooks(this.Handle);
You also need to override the
WndProc function to allow the
GlobalHooks function to process all incoming messages. This
ProcessWindowMessage function checks for messages coming from the DLL, indicating a hook notification has been raised.
protected override void WndProc(ref Message m)
if (_GlobalHooks != null)
base.WndProc (ref m);
Next, you need to create some event handlers for whatever you want to hook. For instance, to receive notification every time a top-level window is created, create an event handler for the
GlobalHooks.Shell.WindowCreated, as follows:
To begin hooking events, you must call the
Start function in the appropriate class.
Just as important are the corresponding
Stop functions, which must be called when you want to stop hooking events. Make sure you stop hooking events before your application terminates.
That's all it takes!
Despite the potential pitfalls, the technique described here has worked quite well for me when I've needed to hook global events. However, if you're creating an application that only needs to hook low-level mouse or keyboard events, Michael Kennedy's code might be better suited for you. Also, the
GetMsg hooks are both pretty inefficient, as they essentially duplicate every message generated by Windows (creating a copy to send to the hooking application). If your application only needs to hook one or two messages, you can easily rewrite the DLL to only post notification messages to the hooking application when the hooked program receives those one or two messages you're looking for.