Click here to Skip to main content
15,899,475 members
Articles / Programming Languages / C#

Processing Global Mouse and Keyboard Hooks in C#

Rate me:
Please Sign up or sign in to vote.
4.97/5 (614 votes)
31 Aug 2011CPOL6 min read 17.2M   218.6K   1K   1.4K
This class allows you to tap keyboard and mouse and/or to detect their activity even when an application runs in the background or does not have any user interface at all.

NEWS

This article was posted in 2004 and updated in 2006 and 2008. During all this time until now I receive a lot of positive feedback and recommendations. There where also many useful contributions which where usually posted as code snippets in forum. Now instead of publishing yet another version on my own I decided to ask you all to actively participate. So please be enthusiastic, feel free to join the project at globalmousekeyhook.codeplex.com

You can help by:
  • Contributing code.
  • Creating issue items requesting additional features or reporting defects.
  • Voting for features and fixes you are interested in.
  • Testing the component in different environments.
  • Writing developer documentation.

This will us also allow to keep this original article up to date.

Thank you all for all your great comments on CodeProject forum and looking forward for your brilliant contributions at globalmousekeyhook.codeplex.com

Introduction

This class allows you to tap keyboard and mouse and/or to detect their activity even when an application runs in the background or does not have any user interface at all. This class raises common .NET events with KeyEventArgs and MouseEventArgs, so you can easily retrieve any information you need.

Background

There are a number of applications that run in the background and detect user inactivity to change their mode. For example, MSN Messenger (or any other messenger). I was going to write such an application, so I searched MSDN and found "exactly" what I needed: 318804 - HOW TO: Set a Windows Hook in Visual C# .NET. This article describes how to tap the mouse movement, but it works only when an application is active. At the end of this article, I found this explanation: "Global hook is not supported in .NET Framework. You cannot implement global hooks in Microsoft .NET Framework...". Anyway, I continued my research and found out that there are exceptions. There are WH_KEYBOARD_LL and WH_MOUSE_LL hooks that can be installed globally. So, I have basically replaced WH_MOUSE with WH_MOUSE_LL in the MSDN example, and it works.

The second step was to extract the information received into a .NET EventArgs and raise the appropriate events.

I found a similar article in CodeProject, under Global System Hooks in .NET by Michael Kennedy, but what I dislike is, there is an unmanaged DLL in C++ that is a main part of this solution. This unmanaged DLL is in C++, and a number of classes make it complicated to integrate it in my own tiny application.

Revisions

This article was posted in 2004 and updated in 2006. During all this time until now I receive a lot of positive feedback and recommendations. There were also a number of technology improvements like .NET Framework 3.5 or Visual Studio 2008. So I have decided to update it once more.

I have refactored and improved the solution, made it more flexible, stable and efficient. But this refactoring also had some drawbacks:

  1. Number of code lines and files has grown.
  2. Backward compatibility to older .NETs is lost.

That's why I attend to leave the old version also to be available for download.

Using the Code [Version 2]

The Simple Way

If you are developing a Windows Forms application and prefer drag & drop programming, there is a component named GlobalEventProvider inside the assembly Gma.UserActivityMonitor.dll. Just drag and drop it to your form and create events using the property editor events tab.

The Alternative Way

Use events provided by the static class HookManager. Note that the sender object in events is always null.

For more usage hints, see the source code of the attached demo application.

Using the Code [Version 1]

To use this class in your application, you need to just create an instance of it and hang on events you would like to process. Hooks are automatically installed when the object is created, but you can stop and start listening using appropriate public methods.

C#
UserActivityHook actHook;
void MainFormLoad(object sender, System.EventArgs e)
{
    actHook= new UserActivityHook(); // crate an instance

    // hang on events

    actHook.OnMouseActivity+=new MouseEventHandler(MouseMoved);
    actHook.KeyDown+=new KeyEventHandler(MyKeyDown);
    actHook.KeyPress+=new KeyPressEventHandler(MyKeyPress);
    actHook.KeyUp+=new KeyEventHandler(MyKeyUp);
}

Now, an example of how to process an event:

C#
public void MouseMoved(object sender, MouseEventArgs e)
{
    labelMousePosition.Text=String.Format("x={0}  y={1}", e.X, e.Y);
    if (e.Clicks>0) LogWrite("MouseButton     - " + e.Button.ToString());
}

Changes and Updates from [Version 0] to [Version 1]

I'd like to thank you all for all the useful comments in the discussion forum. There were a lot of bugs and proposals posted after this article was published on 4th June, 2004. Over and over again came the same topics, and I had to refer to previous posts in the discussion, that is why I have decided to revise the code and publish a FAQ. Here is the list of the most important changes:

  • The project was converted into Visual Studio 2005
  • The problem with upper case characters is solved
  • Mouse wheel information is now included in event arguments
  • Better exception handling
  • Cancellation of keyboard events using the Handled property of event arguments
  • XML documentation of functions

FAQ [Version 1]

Question

The project cannot be run in Visual Studio .NET 2005 in debug mode because of an exception error caused by calling the SetWindowsHookEx. Why? Is it a problem of .NET 2.0?

Answer

The compiled release version works well so that cannot be a .NET 2.0 problem. To workaround, you just need to uncheck the check box in the project properties that says: "Enable Visual Studio hosting process". In the menu: Project -> Project Properties... -> Debug -> Enable the Visual Studio hosting process.

Question

I need to suppress some keystrokes after I have processed them.

Answer

Just set the e.Handled property to true in the key events you have processed. It prevents the keystrokes being processed by other applications.

Question:

Is it possible to convert your global hooks to application hooks which capture keystrokes and mouse movements only within the application?

Answer

Yes. Just use...

C#
private const int WH_MOUSE = 7;
private const int WH_KEYBOARD = 2;

... everywhere, instead of:

C#
private const int WH_MOUSE_LL = 14;
private const int WH_KEYBOARD_LL = 13;

Question

Does it work on Win98 (Windows ME, Windows 95)?

Answer

Yes and No. The global hooks WH_MOUSE_LL and WH_KEYBOARD_LL can be monitored only under Windows NT/2000/XP. In other cases, you can only use application hooks (WH_MOUSE and WH_KEYBOARD) which capture keystrokes and mouse movement only within the application.

Question

I have a long delay when closing applications using hooks by clicking the x button in the titlebar. If I close the application via another event (button click) for example, that works fine.

Answer

It's a known bug of Microsoft. It has to do with the Windows themes. If you disable the Windows themes, the problem goes away. Another choice is to have the hook code run in a secondary thread.

Question

How do I catch key combinations like Ctrl+Shift+A?

Answer

You'll have to track which keys have gone down but not up. Only the most recently pressed key keeps sending KeyDown messages, but the others will still send a KeyUp when released. So if you make three flags IsCtrlDown, IsShiftDown, and IsADown, and set them to true at KeyDown and false at KeyUp, the expression (IsCtrlDown && IsShiftDown && IsADown) will give you the required result.

Question

Does it works with .NET Framework 1.1 and Visual Studio 2003?

Answer

Yes. The file UserActivityHook.cs can be used without any changes, in a Visual Studio 2003 .NET 1.1 project.

License

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


Written By
Software Developer
Germany Germany
Tweeter: @gmamaladze
Google+: gmamaladze
Blog: gmamaladze.wordpress.com

Comments and Discussions

 
GeneralRe: Convert to application hooks Pin
George Mamaladze20-Feb-06 2:51
George Mamaladze20-Feb-06 2:51 
GeneralRe: Convert to application hooks Pin
Jools9920-Feb-06 9:23
Jools9920-Feb-06 9:23 
AnswerRe: Convert to application hooks Pin
George Mamaladze21-Feb-06 0:47
George Mamaladze21-Feb-06 0:47 
GeneralRe: Convert to application hooks Pin
yezie22-Feb-06 2:49
yezie22-Feb-06 2:49 
QuestionWhy it does not work in a new thread?? Pin
yezie13-Feb-06 14:51
yezie13-Feb-06 14:51 
AnswerRe: Why it does not work in a new thread?? Pin
George Mamaladze14-Feb-06 9:08
George Mamaladze14-Feb-06 9:08 
GeneralRe: Why it does not work in a new thread?? Pin
p314to3-Mar-06 0:43
p314to3-Mar-06 0:43 
GeneralRe: Why it does not work in a new thread?? Pin
Worldlifesite3-Mar-06 16:40
Worldlifesite3-Mar-06 16:40 
I've been working on hooks the past three weeks. You have thread based hooks and global hooks. The hooktype(11 of them) will be thread based or global or both.

The hooktype says what kind of listener or hook you want to listen in on.

The Windows Mananger(WinMgr) handles all the events and notifications that goes on with windows on the desktop.

The WinMgr gets all messages sent to it and also generates messages. The WinMgr is the operating's system program for handling windowing functions.

Winforms, tell the WinMgr that its' being created and with these setting or that it's in focus and have these keyboard or mouse events going on, along with some system events.

The WinMgr is the program to handle all of this state communications. The operating system has opened up parts of that communications to other applications so that they can 1) better communicate with the desktop and 2) communicated with other applications.

The WinMgr has different functions to handle different tasks it does. The WinProc() is usually used to communicate that a window is opening with these settings and that its the parent/owner for example of the thread and that its opening these other windows on that thread. If IE is launched it will be the owner and parent and the tab windows or windows open in a new window will be on that thread and tell the WinMgr about itself.

The WinMgr program is pretty busy. It's kind of like the operating system for winforms sort ok. It manages them within the operating system.

WinMgr goes direct to the hardward, like the video, winforms do not go direct.

The WinMgr uses different manangement features to handle all of it's tasks.

The winforms all have a structure and a WinProc() connected to it. The structure contains the info like where is the WinProc()(it's pointer or wHND), what size, position, etc it is. All winform have the same structure to contain this info for the WinMgr to use in doing its job.

You can hook into the listener features of the WinMgr via hooks. Different hooks perform differnt types of listening, like for keyboard events or mouse movements and drag and drops or window creating, destroying and state events or listen in on system events. Some handle the before it happens and some after it happens.

You have to share the WinMgr with other programs as they share the desktop. There is a queue. When you hook into the WinMgr to listen in on some event you go into a queue, the hook list, with the other applications that are doing the same. This is why global hooks must unhook and callnext hook, to keep the queue in order for the other apps else a crash can happen, if the other app do not code for such conditions.

The hook features allows you to hook into a thread for certain hooktypes or go global and get then all for a certain hooktype.

You can chain multiple hooktypes in your code. You'll plop in the queue on after another if they are the same type and other apps do nt jump in while your processing is going on. There is a way to tie up your thread for your processing to be numero uno in win32, but its greatly frowned upon as you must share the road with others.

The operating system will do the thread based hooks first as it runs down it's hiearchy of hooks and then global ones.

The operating system requires global hooks to be in it's own .dll, 1) because of how .dlls go into memory and 2) because a global hook is set to be a system resource and just like it's other system resources, like video drivers, they are required to be .dll because the operating system pulls them in that way. Recall that .dlls will stay in memory(some cached) but local resources get dumped, cached if coded too, when space is tight. The thread based hooks are local resources.

The hooktype that considers itself so import that it must always be available will be a global hooktype only.

Every hook must have a corresponding function to receive the information it asked the WinMgr for via the hooktype. This function decides what it's logic will be based of what it received as an answer.

My just begun project's blog: http://spaces.msn.com/jsvsmartclient

NOTE that hooking is platform dependent. Win16 and win32 are pretty much the same because the underlying windowing hasn't changed. WinServer will be a little different.

For a very good understanding of WinMgr and all it does for to:

Prepare to read over the course of a few days or so: MSDN library on Windowing
GeneralDidn't work - works now with static events Pin
tkluge21-Jan-06 6:44
tkluge21-Jan-06 6:44 
QuestionCtrl+Shift+Alt + Q + U + I + T (easter egg) Pin
wakake21-Dec-05 3:00
wakake21-Dec-05 3:00 
AnswerRe: Ctrl+Shift+Alt + Q + U + I + T (easter egg) Pin
Dan Neely21-Apr-06 10:30
Dan Neely21-Apr-06 10:30 
GeneralRe: Ctrl+Shift+Alt + Q + U + I + T (easter egg) Pin
chaiguy133724-May-07 16:57
chaiguy133724-May-07 16:57 
GeneralStrange behaviour when clicking on the titlebar Pin
nagarsoft7-Dec-05 13:06
nagarsoft7-Dec-05 13:06 
GeneralRe: Strange behaviour when clicking on the titlebar Pin
tempac28-Jan-06 4:37
tempac28-Jan-06 4:37 
GeneralRe: Strange behaviour when clicking on the titlebar Pin
nagarsoft28-Jan-06 4:41
nagarsoft28-Jan-06 4:41 
GeneralRe: Strange behaviour when clicking on the titlebar Pin
tempac28-Jan-06 5:27
tempac28-Jan-06 5:27 
GeneralMouse Cursor Pin
jawahar165-Dec-05 6:54
jawahar165-Dec-05 6:54 
Generalnot work in window services Pin
aptechvn4-Dec-05 3:17
aptechvn4-Dec-05 3:17 
GeneralRe: not work in window services Pin
punkcpp30-Jan-06 19:19
punkcpp30-Jan-06 19:19 
GeneralRe: not work in window services Pin
Pranav Lal2-Aug-06 21:54
Pranav Lal2-Aug-06 21:54 
QuestionDoes not work in VS2005? Pin
Philipp Tietjen1-Dec-05 4:09
Philipp Tietjen1-Dec-05 4:09 
AnswerRe: Does not work in VS2005? Pin
George Mamaladze1-Dec-05 9:29
George Mamaladze1-Dec-05 9:29 
QuestionRe: Does not work in VS2005? Pin
pvoinson13-Jan-06 5:36
pvoinson13-Jan-06 5:36 
AnswerRe: Does not work in VS2005? Pin
George Mamaladze13-Jan-06 5:41
George Mamaladze13-Jan-06 5:41 
AnswerRe: Does not work in VS2005? Pin
freckleback28-Dec-05 6:12
freckleback28-Dec-05 6:12 

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

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