Last update 15-5-2005 One of the questions was to add the modifierkeys. This was done (sorry it wasn't in the first version and sorry it took so long. Kinda forgot about it :-/ Especially because it turned out very simple in .net: the static Control.Modifierkeys property)
The shortcutter project the hook was originally intended for is still work in progress.
Source and test projects (NB: project was under source control, so you might have some annoying messages first)
Keyboardhook.zip
Keyboardhook class code:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
/// Created 2004 Robert Verpalen, Subro Software. You are free to use this
/// software as long as this comment is kept intact
/// Comments and suggestions can be mailed to: subrosoftware@xs4all.nl
/// (imagine the rest of the blahblah here. Sorry for the blahblah, but a guy has to have some advertising ;-) )
/// Thanks to the author of http://www.developer.com/net/net/article.php/11087_2193301_3
/// for his example on hooking
/// I do absoluty NOT agree on having this code used for keylogging.
/// I sincerely hope this will be used for valid programming, not for
/// hacking and other malintent
namespace Subro.Keyboard
{
///
/// KeyboardHook, as the name implies, hooks into the windows messaging system to
/// catch global keystrokes.
///
///
/// /// The hook is automatically released when the class is disposed.
/// You can force the release by calling
/// The class exposes a KeyDown and KeyUp event, which can be used as any
/// other keydown/up event in the .net framework. The sender object will always be
/// this class, but all global keystrokes are processed.
/// As in other keydown/up events, setting handled of the keyeventargs object to true
/// e.Handled=true; will prevent other hooks from executing. For system keys
/// as the Windows key, this means that the key gets 'blocked'
///
public class KeyboardHook:IDisposable
{
#region Hook-dlls
[DllImport("user32", EntryPoint="SetWindowsHookExA")]
private static extern int SetWindowsHookEx(
int idHook, KeyBoardCatcherDelegate lpfn,
int hmod,
int dwThreadId);
[DllImport("user32.dll", CharSet=CharSet.Auto,
ExactSpelling=
true, CallingConvention=CallingConvention.Winapi)]
private static extern short GetKeyState(
int keyCode);
[DllImport("user32",EntryPoint ="MapVirtualKey")]
private static extern int MapVirtualKey(
int wCode,
int wMapType);
[DllImport("user32",EntryPoint ="CallNextHookEx")]
private static extern int CallNextHook(
int hHook,
int ncode,
int wParam,keybHookStruct lParam);
private const int
WH_KEYBOARD = 2,
WH_KEYBOARD_LL = 13; //Global keyboard hook. That's the one we'll be using
[DllImport("user32",EntryPoint ="UnhookWindowsHookEx")]
private static extern int UnhookWindowsHookEx (
int hHook);
#endregion
public KeyboardHook()
{
kbcatcher =
new KeyBoardCatcherDelegate(KeyBoardCatcher);
keybhook = SetWindowsHookEx(WH_KEYBOARD_LL,kbcatcher,
Marshal.GetHINSTANCE(typeof(KeyboardHook).Module).ToInt32(),0);
if(keybhook==0)
throw new ExternalException("Error: " + Marshal.GetLastWin32Error().ToString() + "\r\nCould not hook keyboard");
}
///
/// See the property for info
///
public KeyboardHook(
bool AddModifierData):
this()
{
this.AddModifierData = AddModifierData;
}
#region Dispose
/// The keyboard hook id, to be used with unhookwindowsex
///
private readonly int keybhook;
private bool disposed;
/// call Dispose when finished to release the hook.
/// If this is not done manually, the destructor releases the hook,
/// but manually disposing is more effective
public void Dispose()
{
if(!disposed)
{
UnhookWindowsHookEx(keybhook);
GC.SuppressFinalize(this);
disposed=true;
}
}
/// Destructor
~KeyboardHook()
{
Dispose();
}
#endregion
/// This structure is filled when calling the KeyBoardCatcherDelegate.
private struct keybHookStruct
{
public int vkCode,scanCode,flags,time,dwExtraInfo;
}
private delegate int KeyBoardCatcherDelegate(int code, int wparam,ref keybHookStruct lparam);
public static bool CheckKeyPressed(params Keys[] keys)
{
for (
int i = 0; i < keys.Length; i++)
if(!CheckKeyPressed(ref keys[i]))return false;
return true;
}
public static bool CheckKeyPressed(ref Keys key)
{
return CheckKeyPressed((int)key);
}
public static bool CheckKeyPressed(int vkey)
{
short ks = GetKeyState(vkey);
Console.WriteLine(ks);
return ks == 1;
}
public bool IsKeyPressed(Keys key)
{
return CheckKeyPressed(ref key);
}
public bool AreKeysPressed(params Keys[] keys)
{
return CheckKeyPressed(keys);
}
private const int HC_ACTION = 0;
[MarshalAs(UnmanagedType.FunctionPtr)]
private KeyBoardCatcherDelegate kbcatcher;
/// Catch the keys if you like. These are the global keys. Set e.Handled=true to block the key (such as alt-tab)
public event KeyEventHandler
KeyDown = null,
KeyUp = null;
private const int
wpKeyDown = 256,
wpKeyUp = 257;
/// If this value is true
/// modifier data (such as shift,control,alt) is added to the KeyEventArgs, otherwise
/// only the keydata is send.
/// Adding the modifier data can produce some overhead since
/// it is checked on every keypress.
/// If you want to check manually, set this property to false
/// and check instead
///
public bool AddModifierData=true;
public readonly Keys[] Modifiers = {Keys.Alt,Keys.Control,Keys.Shift};
/// The method that's used to catch the keyboard input
///
///
///
///
private int KeyBoardCatcher(int hookcode,int wparam,ref keybHookStruct lparam)
{
if(HC_ACTION==hookcode)
{
if((wparam==wpKeyDown && KeyDown !=
null)
|| (wparam==wpKeyUp && KeyUp !=null))
{
try
{
Keys k = (Keys)lparam.vkCode;
if(AddModifierData)
k |= Control.ModifierKeys;
KeyEventArgs e =
new KeyEventArgs(k);
if(wparam==wpKeyDown)
KeyDown(this,e);
else
KeyUp(this,e);
//If handled, do not process any other hooks after this one. (key is blocked)
if(e.Handled)
return 1;
}
/*
//for debugging only
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
*/
catch{}
} }
//Call the next hook for the input
return CallNextHook(keybhook,hookcode,wparam, lparam); } } }
. . .
Using the class is simply a matter of creating an instance and attaching a standard .net keyevent handler to the keyup or keydown event. In the downloadable project above, the keyboardhook is in a precompiled dll, that can be in any .net language.
An example use:
C#:
// class level variable
KeyboardHook kh;
// Initialization (for example in sub main or a constructor)
kh = new KeyboardHook();
kh.KeyDown+=new KeyEventHandler(NameOfTheKeyDownVoid);
. . .
VB.Net:
'create a new instance of the keyboardhook class
'using withevents here, of course addhandler can also be used
Private WithEvents kh
As New Subro.Keyboard.KeyboardHook
Private Sub NameOfTheKeyDownEventSub(
ByVal sender
As Object,
ByVal e
As System.Windows.Forms.KeyEventArgs)
Handles kh.KeyDown
'...
End Sub
. . .