Click here to Skip to main content
Email Password   helpLost your password?

Sample Image - CSLLKeyboard.jpg

Introduction

Cats and babies have a lot in common. They both like eating the house plants, and share the same hatred of closed doors. They also love using keyboards, with the result that the important email you were sending to your boss is dispatched in mid-sentence, your accounts in Excel are embellished with four rows of gobbledygook, and your failure to notice that Windows Explorer was open results in several files moving to the Recycle Bin.

The solution is an application which you can switch to as soon as the keyboard is under threat, and which will ensure that any keyboard activity is harmless. This article illustrates how the keyboard can be neutralized in a C# application using a low-level Windows API hook.

Background

There are a number of articles and code samples regarding hooks in Windows, and some of them are listed at the end of this article. Neutralizing the keyboard when children are around must be a common need -- someone here wrote almost exactly the same thing in C++[^]! However, when I was looking for source code to create my application, I found very few .NET examples, and none that involved a self-contained class in C#.

The .NET framework gives managed access to the keyboard events you'll need for most ordinary uses through KeyPress, KeyUp and KeyDown. Unfortunately, these events can't be used to stop Windows from processing key combinations like Alt+Tab or the Windows Start key, which allow users to navigate away from an application. The conveniently placed Windows key in particular was irresistible to my baby son!

The solution is to catch the keyboard events at the operating system level rather than through the framework. To do this, the application needs to use Windows API functions to add itself to the "hook chain" of applications listening for keyboard messages from the operating system. When it receives this type of message, the application can selectively pass the message on, as it normally should, or suppress it so that no further applications -- including Windows -- can act on it. This article explains how.

Please note, however, that this code only applies to NT-based versions of Windows (NT, 2000 and XP), and that it isn't possible to use this method to disable Ctrl+Alt+Delete (suggestions on how to do that can be found in this MSDN Magazine Q&A[^]).

Using the code

For ease of use, I have attached three separate zip files to this article. One contains only the KeyboardHook class which is the main focus of this article. The others are complete projects for an application called "Baby Keyboard Bash" which displays the keys' names or coloured shapes in response to keystrokes. For ease of use, I have included two projects, one for Microsoft Visual C# 2005 Express Edition and one for Visual Studio 2003.

Instantiating the class

The keyboard hook is set up and handled by the KeyboardHook class in keyboard.cs. This class implements IDisposable, so the simplest way to instantiate it is to use the using keyword in the application's Main() method, to enclose the Application.Run() call. This will ensure that the hook is set up as soon as the application starts and, more importantly, is disabled as the application shuts down.

The class raises an event to warn the application that a key has been pressed, so it is important for the main form to have access to the instance of KeyboardHook created in the Main() method; the simplest solution is to store this instance in a public member variable. In a Visual Studio 2003 project, this will usually go in the Form1 class (or whatever the application's main class is called), and in Visual Studio 2005, in the Program class in Program.cs.

KeyboardHook has three constructors to enable or disable certain settings:

Enabling Alt+Tab and/or the Windows keys allows the person who is actually using the computer to switch to another application and interact with it using the mouse while keystrokes continue to be trapped by the keyboard hook. The PassAllKeysToNextApp setting effectively disables the keystroke trapping; the class will still set up a low-level keyboard hook and raise its KeyIntercepted event, but it will also pass the keyboard events to other listening applications.

The simplest way to instantiate the class to trap all keystrokes is therefore:

public static KeyboardHook kh;

[STAThread]
static void Main()
{
  //Other code

  using (kh = new KeyboardHook())
  {
    Application.Run(new Form1());
  }

Handling the KeyIntercepted event

When a key is pressed, the KeyboardHook class raises a KeyIntercepted event containing some KeyboardHookEventArgs. This needs to be handled by a method of the type KeyboardHookEventHandler, which can be set up as follows:

kh.KeyIntercepted += new KeyboardHook.KeyboardHookEventHandler(kh_KeyIntercepted);

The KeyboardHookEventArgs returns the following information on the key that was pressed:

A method with the appropriate signature can then be used to perform whatever tasks the keystroke calls for. Here is an example from the enclosed sample application:

void kh_KeyIntercepted(KeyboardHookEventArgs e)
{
  //Check if this key event is being passed to

  //other applications and disable TopMost in 

  //case they need to come to the front

  if (e.PassThrough)
  {
    this.TopMost = false;
  }
  ds.Draw(e.KeyName);
}

The rest of this article explains how the low-level keyboard hook is implemented in KeyboardHook.

Implementing a low-level Windows API keyboard hook

The Windows API contains three methods in user32.dll that are useful for this purpose:

The key to creating an application which can hijack the keyboard is to implement the first two methods, and forgo the third. The result is that any keys pressed go no further than the application.

In order to achieve this, the first step is to include the System.Runtime.InteropServices namespace and import the API methods, starting with SetWindowsHookEx:

using System.Runtime.InteropServices
...
//Inside class:


[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
  LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

The code to import UnhookWindowsHookEx and CallNextHookEx is listed in the sections concerning those methods further in this article.

The next step is to call SetWindowsHookEx to set up the hook, passing the following four parameters:

SetWindowsHookEx returns a hook ID which will be used to unhook the application when it shuts down, so this needs to be stored in a member variable for future use. The relevant code from the KeyboardHook class is as follows:

private HookHandlerDelegate proc;
private IntPtr hookID = IntPtr.Zero;
private const int WH_KEYBOARD_LL = 13;


public KeyboardHook()
{
  proc = new HookHandlerDelegate(HookCallback);
  using (Process curProcess = Process.GetCurrentProcess())
  using (ProcessModule curModule = curProcess.MainModule)
  {
     hookID = SetWindowsHookEx(WH_KEYBOARD_LL, proc,
                    GetModuleHandle(curModule.ModuleName), 0);
  }
}

Processing keyboard events

As mentioned above, SetWindowsHookEx requires a pointer to the callback function that will be used to process the keyboard events. It expects a function with the following signature:

LRESULT CALLBACK LowLevelKeyboardProc
(   int nCode,
    WPARAM wParam,
    LPARAM lParam
);

The C# method for setting up a "pointer to a function" is to use a delegate, so the first step in giving SetWindowsHookEx what it needs is to declare a delegate with the right signature:

    private delegate IntPtr HookHandlerDelegate(
        int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);

And then write a callback method with the same signature; this method will contain all the code that actually processes the keyboard event. In the case of KeyboardHook, it checks whether the keystroke should be passed to other applications and then raises the KeyIntercepted event. Here is a simplified version without the keystroke handling code:

private const int WM_KEYUP = 0x0101;
private const int WM_SYSKEYUP = 0x0105;

private IntPtr HookCallback(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam)
{
  //Filter wParam for KeyUp events only - otherwise this code

  //will execute twice for each keystroke (ie: on KeyDown and KeyUp)

  //WM_SYSKEYUP is necessary to trap Alt-key combinations

  if (nCode >= 0)
  { 
      if (wParam == (IntPtr)WM_KEYUP || wParam == (IntPtr)WM_SYSKEYUP)
      {
        //Raise the event

        OnKeyIntercepted(new KeyboardHookEventArgs(lParam.vkCode, AllowKey));
      }
      //Return a dummy value to trap the keystroke

      return (System.IntPtr)1;
  }
  //The event wasn't handled, pass it to next application

  return CallNextHookEx(hookID, nCode, wParam, ref lParam);
}

A reference to HookCallback is then assigned to an instance of HookHandlerDelegate and passed in the call to SetWindowsHookEx, as illustrated in the previous section.

Whenever a keyboard event occurs, the following parameters will be passed to HookCallBack:

private struct KBDLLHOOKSTRUCT
{ 
  public int vkCode;
  int scanCode;
  public int flags;
  int time;
  int dwExtraInfo;
}

The two public parameters are the only ones used by the callback method in KeyboardHook. vkCoke returns the virtual key code, which can be cast to System.Windows.Forms.Keys to obtain the key's name, while flags indicates if this is an extended key (the Windows Start key, for instance) or if the Alt key was pressed at the same time. The complete code for the HookCallback method illustrates which flags values to check for in each case.

If the information provided by flags and the other components of the KBDLLHOOKSTRUCT are not needed, the signature of the callback method and delegate can be changed as follows:

private delegate IntPtr HookHandlerDelegate(
        int nCode, IntPtr wParam, IntPtr lParam);

In this case, lParam will return only the vkCode.

Passing keystrokes to the next application

A well-behaved keyboard hook callback method should end by calling the CallNextHookEx function and returning its result. This ensures that other applications get a chance to handle the keystrokes destined for them.

However, the key functionality of the KeyboardHook class is preventing keystrokes from being propagated to any further applications. So whenever it processes a keystroke, HookCallback returns a dummy value instead:

return (System.IntPtr)1;

On the other hand, it does call CallNextHookEx if it didn't handle the event, or if the parameter passed with KeyboardHook's overloaded constructor allows certain key combinations through.

CallNextHookEx is enabled by importing the function from user32.dll as shown in the following code:

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
  IntPtr wParam, ref KeyInfoStruct lParam);

The imported method is then called by this line in the HookCallback method, which ensures that all the parameters received through the hook are passed on to the next application:

CallNextHookEx(hookID, nCode, wParam, ref lParam);

As mentioned before, if the flags in lParam are not relevant, the signature for the imported CallNextHookEx can be changed to define lParam as System.IntPtr.

Removing the hook

The last step in processing the hook is to remove it when the instance of the KeyboardHook class is destroyed, using the UnhookWindowsHookEx function imported from user32.dll as follows.

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);

Since KeyboardHook implements IDisposable, this can be done in the Dispose method.

public void Dispose()
{
  UnhookWindowsHookEx(hookID);
}

hookID is the id returned by the call to SetWindowsHookEx in the constructor. This removes the application from the hook chain.

Sources

Here are some good sources on Windows hooks in C# and in general:

It seems that articles in any language seem to attract at least one response on the lines of "how do I do the same thing in (some other language)?", so here are some samples I found:

And nothing to do with Windows API hooks or C#, but I would like to recommend Mike Ellison's excellent Word 2003 template if you're ever planning to write an article for Code Project!

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralGreat Work...
ahsan sarfraz
19:50 20 Jan '10  
I was in search of these keyyboard hooks n have been trying different things to control thenm ...but useless...
you have done a great job ,.. it was very very very helpful to me.
tahnk you
regards

AHSAN SARFRAZ
UNIVERSITY OF ENGINEERING AND TECHNOLOGY TAXILA PAKISTAN
COMPUTER SYSTEM ENGINEERING DEPARTEMENT

Generalwelldone
Member 1984062
0:19 23 Dec '09  
Nice Article Emaa keep up the goodThumbs Up work
GeneralKeyboard events
vinodachu
22:58 8 Oct '09  
thanks a lot
GeneralWinForms Keys to WPF Keys
Spacelord_XaN
0:48 29 Apr '09  
Thanks for that great Article!

If someone needs it I found this:

How to convert Form keys to Wpf Keys?
Generalthanks
c870916
23:01 6 Apr '09  
thank you for your help Poke tongue
QuestionHello Emma .... Its Doing so in ASP.NETWEBSITE?
LuizItatiba
9:52 4 Mar '09  
Would do this or adapt the code in ASP.NET AJAX ENABLED WEB APPLICATION PROJECTS AND ASP.NET WEBSITES.Se would rather have the procedure as example?


I look at now and I thank

Translated from Portuguese into English by Google

LADEF

QuestionHello Emma. need your help.
dhaddo
9:26 2 Mar '09  
Hello,

For one of my academic projects requirement, I want to make program which will take input from user and act for the game as input for the game(take input from my program which will be output for game). However for other applications its working just fine but it is not working for games due to some game guard or game listener. I need to bypass the GG or place kbrd input on such file that game would take the input. Please help me.

Regards,
Haris
GeneralThanks
flaunt
9:54 20 Dec '08  
This was a good starting point for what i'm trying to do.
GeneralNew WPF Version Available
BarryDorman
9:53 26 Nov '08  
Hi Emma, my daughter has spent hours in front of this application over the past year. Thanks for your great contribution.

I wanted to let you (and anyone else interested) know that I've uploaded a similar application written in WPF. You can view it here - http://www.codeproject.com/KB/cs/ShapeShow.aspx
Thanks!
QuestionHelp! Demo works but seems to have a memory issue.
cflux
18:29 11 Sep '08  
Hi first I want to say excellent article. But I was playing with the example code and noticed that the applications process memory resources keeps increasing as more keys are pressed. Is anyone else seeing this behavior? Does anyone know why this is happening or how to fix it?

Thanks
GeneralExcellent work
jtradke
17:37 28 Aug '08  
Hi - just wanted to say that I'm using your code in a new open source project I'm working on (linked below). It pops up an OSD notification when you press CAPSLOCK, NUMLOCK, etc. Thanks!

http://code.google.com/p/lock-osd/
GeneralEsc key acts as toggle?
mikedrummo
0:27 7 Jul '08  
I am only using GetKeyState in VBA to see if the Escape key is pressed when I select some Excel cells.
Res = GetKeyState(vbKeyEscape)

The result are that the Esc key acts as a toggle.

I haven't found any documentation about this yet.

Can you offer any help?

thanks
Mike
QuestionHelp! How come using this class as a Console App doesn't work?
troy4u
23:13 1 Jul '08  
I used the supplied Keyboard.cs with 2 different types of apps:

Both apps were setup almost in the exact same way.

1. Windows Forms: worked perfectly
2. Console App: call backs won't fire

Is there a reason why a Win Form is needed? Can it work with a console app somehow?

ThanksConfused
QuestionRe: Help! How come using this class as a Console App doesn't work?
troy4u
0:11 2 Jul '08  
I found out the problem. I forgot to call Application.Run() which creates the needed windows message pump.
Now here's a more interesting problem, how do I hook up a message pump with a windows service?

i.e. something that inherits from System.ServiceProcess.ServiceBase

I see no good place to add the pump and still have the service run properly.
Any advice would be great!Unsure
GeneralHow to neutralize keyboard for one form rather than the entire application?
jonny_chympo
9:27 26 Jun '08  
Great article Emma!

I was previously using code from this website (which has a great method for disabling ctrl+alt+del for those interested..): [^]
but found that the keyboard hook was not functioning properly and causing unhandled exceptions for some odd reason...

I was lucky to stumble upon your article and your method works perfectly!

I was wondering how I might be able to use your code in order to disable the keyboard for just one form in my application??
So instead of:
public static KeyboardHook kh;

[STAThread]
static void Main()
{
//Other code
using (kh = new KeyboardHook())
{
Application.Run(new Form1());
}
}
I would like to use it in the following manner:
public partial class Form A : Form
{

public static KeyboardHook kh;

//.....
private void Button_Click(object sender, EventArgs e)
{
using (kh = new KeyboardHook())
{
FormB b = new FormB(param1, param2);
b.Show();
}

}
}
Is this possible? The code compiles and runs but the keyboard hooks are not working in Form B - all the keys are still active. Is there another way to do it? Maybe without the "using" block?

Thanks a lot! Again, great article!
QuestionHow to prevent keyboard's event raise?
hoangnguyenletran
0:49 11 Mar '08  
Hi,

I appreciate your program. However, now I'm in stuck with how to lock keyboards, it means prevent keyboard's event raised, including media keys, browser keys and extra keys. Because when my app is running, I don't want user can do anything else even press CTRL TAB, ALT TAB, CTRL ALT DEL or anything else. Please response me asap.

I am looking forward to hearing from you. Thank you so much for your time
QuestionWindows service
leecha
23:59 6 Nov '07  
Hello,

Does anybody tried to use this in windows service, and if it's posible please respond.
10x !
AnswerRe: Windows service
Sandeep Aparajit
3:30 15 May '08  
I dont think it is possible, since a Windows service is started even before you logon to the system. On a server machine like Windows 2003 server, their can be n-number of logons i.e. number of different user sessions at a time. Hence if a key is presses it would be difficult to know in which session and on which window the key was presses. One more reason is Operating System security.

Well, this is just a guess, Im not very sure if it is possible.

------------------------------
Sandeep Aparajit
Mark usefull posts as Helpful/Answers.
http://sandeep-aparajit.blogspot.com

GeneralA suggestion
Mohamad K Ayash
5:09 5 Sep '07  
Very good work but let me suggest you tell the readers how windows kernel does a hook since a lot may be wondering about this, the answer is that when a key is pressed it goes to a port in the computer numbered 0x60 and then the scan code( a special representation of the key pressed) goes to interrupt 0x9 and their the opearating system retrieve the key and interpret it so to hook we can program the interrupt to ignore some keys and poof its hooked...

Best Wishes...

To follow the path, Walk with the MASTER, See through the MASTER, Be the MASTER!

GeneralCtrl and Windows key
digfreelancer
5:50 14 Aug '07  
I want all keys are allows to pass to next applications accepts Ctrl and Windows key. Please help...
GeneralProblems when hooking to the Esc key
Zoltan Balazs
6:17 30 May '07  
I played around with the solution attached to the article and noticed the following:
if I try to display a window of any kind to the user as a response to the Esc key
the application becomes iresponsive. It just locks for a few seconds. Have you got any ideas
what's going on?

I replaced the kh_KeyIntercepted method as follows:
void kh_KeyIntercepted(KeyboardHook.KeyboardHookEventArgs e)
{
switch (e.KeyCode)
{
case (int)Keys.Escape:
MessageBox.Show("test");
break;
}
}



GeneralNormally using the keystrokes in my app
JohannesAckermann
5:58 24 May '07  
Great article! One problem, and I'm sure I'm just being a total moron:
The class you supplied block all the keystrokes wonderfully... so wonderfully in fact that even my own app doesn't see them! My question is this: without having to resort to SenKeys or something like that in the KeyHook event handler, how can I make my app accept keys as per usual but simply block any other apps from receiving them?
I'm basically writing a standard app that must be the ONLY app accessable by the user of the PC. There is a close button which only closes the app if a correct password is entered. That's beside the point... the problem is that my app is just standard WindowsForms with textboxes etc. Implementing this class as is and doing nothing with the hooked keys results in my own app not getting the keystrokes... i.e. typing in a textbox does nothing. Now, can I somehow just let my app handle its own input, but block the rest (alt-tab,windowskey, etc)?

thanks in advance!

Johannes
GeneralRe: Normally using the keystrokes in my app
Emma Burrows
8:27 24 May '07  
Have a look at form1.cs in the VS 2003 or 2005 sample projects to see how my sample application handles the keystrokes before they are blocked. Basically, you attach a handler to the keyboard class's KeyIntercepted event, and then you do whatever you want to do with the intercepted key strokes in the handler. In my case, I pass the info to another class to display coloured text or shapes.

HTH
GeneralRe: Normally using the keystrokes in my app
JohannesAckermann
10:35 24 May '07  
Hi,

Thanks for the reply.

I get exactly what you do in the sample, but it still doesn't solve my problem:
Let's say I've got a standard textbox. The user clicks inside the textbox and start typing. Now the keyboard hook catches each key press, so the textbox doesn't get the keypress. The only way I managed to get the key passed on to the textbox as per usual is by calling SendKey, but this is not really a solution. Firstly it seems to be extremely slow, and secondly I have to write a uge switch statment to see if the key was a special key, because SendKey.Send wants special keys in curly braces, and characters as standard. I can't just pass everything in curly braces, because then all letter appear as capitals... which is the key, not the character. Also checking for Shift etc... it all becomes extremely complicated. Holding a key down also causes problem, like holding down Backspace made the app crash.

I basically just want my app to accept keypresses as per usual... e.g. when typing text into a text box I don't want to have to manually interperet what is being typed. All I actually want is to trap and discard System-wide key presses like Alt-Tab, the Windows Key etc.

Maybe I'm missing something simple in your documentation, and it is highly likely that I'm just being stupid, but I can't figure it out. Sorry!

Any advice would be greatly appreciated!

Johannes
QuestionCheckModifiers() incorrect on Vista
DarkInGray
18:55 30 Apr '07  
Hi Emma, nice work, deeply appreciated.Blush
Ever tested on Windows Vista?
I ran demo code on two Vista machines and found "AllowAltTabAndWindows" mechanism does not work.
I traced into the CheckModifiers() method, it seems that GetKeyState result bit test are not reflecting the actual key-up states.
I'm not good enough at DLLImport interOps, so why those modifier bits incorrect are still mysterious to me.
I'm using VS2005 SP1 + KS2005_SP1_Vista_20070306

Thanks,
DarkInGray


Last Updated 26 Mar 2007 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010