MSDN's definition of a Windows hook is:
A hook is a point in the system message-handling mechanism where an application can install a subroutine to monitor the message traffic in the system and process certain types of messages before they reach the target window procedure.
While the .NET framework library has wrapped a significant portion of the Win32 API, there are numerous areas where we have to resort to P/Invoke to get at the functionality we need. Hooks are one of those areas. One likely reason for this is that hooks are pretty low-level things which most programs rarely need. Another is that they are intimately tied to the message based nature of the Windows API itself. They would likely not be portable to any other Operating System.
Windows hooks are implemented using callback functions. The callback procedure has a signature similar to the
SendMessage API, so Windows hooks are very generic and are used to extend a number of different types of Windows message processing methods (see below). This library provides a C# wrapper around the hook procedure that can be used from a Windows Forms application.
MouseHook is a particular type of Windows hook that allows your code to be notified when any window on a particular thread gets a mouse message. This example uses a mouse hook to capture the button up messages for the two navigation buttons on 5-button mice. By using a mouse hook, the application's main
Form can control navigation via the mouse, regardless of what child control currently has input focus. If you don't have a 5-button mouse, the demo isn't going to be very exciting for you, but the code demonstrates the basic concepts.
Last summer, there was an interesting article, by Dino Esposito, in the MSDN magazine, about implementing hooks in C#. "Hmmm", I thought to myself, "That's interesting, but why in the world would you ever want to do that?"
Well, the application I'm currently working on uses a web browser-like "forward/back" idiom, wherein the user can navigate between views, much like they would a series of web pages. Each view is a
UserControl that gets loaded dynamically as the user moves around the application.
I, for one, can no longer live without the forward and back buttons on my five button mouse, so I thought I'd add mouse navigation to our app.
I quickly realized that this was not as simple as it first appeared. Because each view
UserControl takes up the entire client area of the
Form it is hosted on, the
Form itself never gets any
MouseUp events. I didn't want to deal with this in each and every view I'd create, so I had to come up with a way to handle the mouse button navigation in one place.
My first idea was to attach to each view's
MouseUp event in the
Form and do the navigation from there. This quickly proved itself to be a kludge as each
UserControl has its own child controls, and when they have focus, the
UserControl doesn't get any mouse events either. To work, one would need to recursively attach all child control events to the same handler. Sounds messy, so scratch idea #1.
The next thought was to use the
WM_PARENTNOTIFY message in the
WndProc. This looked promising at first and I had a proof of concept all whipped in no time. Then, I thought to myself, "The only mouse messages that spawns
WM_PARENTNOTIFY are mouse down messages. I wonder when exactly a web browser navigates".
A quick test proved that web browsers navigate on mouse up, not mouse down (as, upon further reflection, one would expect). Scratch idea #2.
That led me to the implementation I was hoping I wouldn't have to delve into: a
MouseHook. I recalled implementing these in VB6 (in what seems like the dark ages now) and the havoc that they could wreck upon the VB6 IDE. Well, it seemed like the only solution, so a quick Google search led me back to Dino's article, and after a quick look at the code, it began to appear that it wouldn't be as bad as I had feared.
Windows hooks are thread specific, and this implementation uses the thread of the code that calls the
Install method, which if you call it synchronously from a
Form, will be the main UI thread. This is perfect for my navigation problem because, now I can have code in the
Form that will respond to a forward/back mouse click, no matter what control has focus.
The code download includes and uses the
LocalWindowHook class which Dino Esposito used to demonstrate the basics of Windows hooks in the MSDN sample code.
Using the code
There are two classes you can use to get at the
The first is a class,
MouseHook, that inherits directly from
LocalWindowHook. Instantiate it, attach to its events, call
Install(), and away you go. It will remove the hook when you
Dispose it or when its
private MouseHook hook = new MouseHook();
private void HookUp()
this.hook.MouseUp += new MouseHookEventHandler( this.hook_MouseUp );
private void hook_MouseUp(object sender, MouseHookEventArgs e)
The second class is a
System.ComponentModel.Component derived type that can be placed onto a design surface. In addition to making the
MouseHook easier to use from the VS.NET IDE, the
MouseHookComponent automatically installs the hook from the constructor that the Windows Forms Designer uses.
That means, all you really need to do is drop it on a
Form and attach to the events you are interested in.
The are a number of different types of hooks supported by the Windows API. Dino Esposito's
LocalWindowsHook class is completely generic, and can be used to create any one of those hook types:
public enum HookType : int
WH_JOURNALRECORD = 0,
WH_JOURNALPLAYBACK = 1,
WH_KEYBOARD = 2,
WH_GETMESSAGE = 3,
WH_CALLWNDPROC = 4,
WH_CBT = 5,
WH_SYSMSGFILTER = 6,
WH_MOUSE = 7,
WH_HARDWARE = 8,
WH_DEBUG = 9,
WH_SHELL = 10,
WH_FOREGROUNDIDLE = 11,
WH_CALLWNDPROCRET = 12,
WH_KEYBOARD_LL = 13,
WH_MOUSE_LL = 14
MouseHook class I've created here is just one example of a derived class that abstracts the complexity of a particular kind of Windows hook. I've also included the
CbtLocalHook class from the MSDN sample code.
- 09/19/2003 - Initial release.