Click here to Skip to main content
Click here to Skip to main content

Using Hooks from C#

, 30 Dec 2009
Rate this:
Please Sign up or sign in to vote.
An article on using Windows hooks from .NET, demonstrated with a MouseHook.

Introduction

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.

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

Background

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 Form's 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 MouseHook.

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

private MouseHook hook = new MouseHook();

private void HookUp()
{
    this.hook.Install();
    this.hook.MouseUp += new MouseHookEventHandler( this.hook_MouseUp );
}

private void hook_MouseUp(object sender, MouseHookEventArgs e)
{
    // do some stuff with your exciting new mouse hook data
}

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.

Future enhancements

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
}

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

References

History

  • 09/19/2003 - Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Don Kackman
Team Leader Starkey Laboratories
United States United States
The first computer program I ever wrote was in BASIC on a TRS-80 Model I and it looked something like:
10 PRINT "Don is cool"
20 GOTO 10
It only went downhill from there.
 
Hey look, I've got a blog

Comments and Discussions

 
GeneralMy vote of 4 PinmemberSivaji156526-Sep-13 23:51 
Questiongood article PinmemberCIDev7-Nov-12 4:39 
I know the article is old, but it is a good explanation of using hooks.
Just because the code works, it doesn't mean that it is good code.

GeneralMy vote of 5 Pinmemberluoly1218-Aug-12 16:40 
QuestionCan't run in VS2010 Pinmemberhambor29-Aug-11 3:16 
AnswerRe: Can't run in VS2010 Pinmemberwooohaa26-Jun-12 3:03 
QuestionGlobal CBTHook? PinmemberZiggy98118-Aug-10 21:13 
GeneralCool - If you like Hooks PinmemberMarkJoel6021-Jan-10 4:57 
Generalin vb. Pinmemberrdpeake11-Jan-10 6:07 
GeneralRe: in vb. PinmemberDon Kackman18-Jan-10 16:28 
GeneralRe: in vb. Pinmemberrdpeake19-Jan-10 2:10 
GeneralRe: in vb. [modified] PinmemberDon Kackman20-Jan-10 3:54 
GeneralRe: in vb. Pinmemberrdpeake20-Jan-10 13:52 
GeneralWPF Pinmemberdarrellp1-Jan-10 5:36 
GeneralRe: WPF PinmemberDon Kackman2-Jan-10 8:36 
GeneralRe: WPF Pinmemberdarrellp2-Jan-10 10:16 
GeneralRe: WPF PinmemberDon Kackman2-Jan-10 14:27 
GeneralI didn't know it! Pinmemberyyhotspring30-Dec-09 17:41 
GeneralAbove link is SPAM Pinmemberspoodygoon31-Dec-09 7:13 
QuestionIs this a joke? PinmemberMember 14391572-Dec-09 2:52 
AnswerRe: Is this a joke? PinmemberDon Kackman2-Dec-09 4:05 
GeneralHook ReadFile Pinmemberwei_future5-Jun-08 21:04 
Questionc# DoModal in handler doesn't return?!? Pinmemberjfstephe1-Nov-07 8:20 
GeneralVB.Net Pinmemberwestville_mike17-Oct-07 4:27 
QuestionCancel events? PinmemberJohnny J.23-Aug-07 22:40 
QuestionHook on Insert Smart Card into Smart Card Reader PinmemberIdeazzkds17-Dec-06 20:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 30 Dec 2009
Article Copyright 2003 by Don Kackman
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid