You need to watch and respond to specific user keyboard input, and, based on the input, you want to perform one or more actions. For example, pressing the Windows key and the E key at the same time launches Windows Explorer. You would like to add other Windows key combinations for your own applications. In addition, you could prevent the user from using specific keys (such as the Windows key) from within your application.
The following Windows Forms application
uses the WH_KEYBOARD
Windows hook:
using System; using System.Windows.Forms; using System.Runtime.InteropServices; namespace WindowsApplication2 { public class Form1 : System.Windows.Forms.Form { // Required designer variable. private System.ComponentModel.Container components = null; private System.Windows.Forms.Button button1; private System.Windows.Forms.Button button2; private System.Windows.Forms.TextBox textBox1; public Form1( ) { // Required for Windows Form Designer support InitializeComponent( ); } protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose( ); } } base.Dispose( disposing ); } #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent( ) { this.button1 = new System.Windows.Forms.Button( ); this.button2 = new System.Windows.Forms.Button( ); this.textBox1 = new System.Windows.Forms.TextBox( ); this.SuspendLayout( ); // // button1 // this.button1.Name = "button1"; this.button1.TabIndex = 0; this.button1.Text = "Start"; this.button1.Click += new System.EventHandler(this.button1_Click); // // button2 // this.button2.Location = new System.Drawing.Point(0, 48); this.button2.Name = "button2"; this.button2.TabIndex = 1; this.button2.Text = "End"; this.button2.Click += new System.EventHandler(this.button2_Click); // // textBox1 // this.textBox1.Location = new System.Drawing.Point(80, 0); this.textBox1.Multiline = true; this.textBox1.Name = "textBox1"; this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.textBox1.Size = new System.Drawing.Size(752, 504); this.textBox1.TabIndex = 2; this.textBox1.Text = ""; this.textBox1.WordWrap = false; // // Form1 // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(832, 509); this.Controls.AddRange(new System.Windows.Forms.Control[] { this.textBox1, this.button2, this.button1}); this.Name = "Form1"; this.Text = "Form1"; this.ResumeLayout(false); } #endregion [STAThread] static void Main( ) { Application.Run(new Form1( )); } // Declare Windows API calls used to access Windows hooks [DllImport("user32.dll")] public static extern int SetWindowsHookEx(int hookType, HookProc callback, int instance, int threadID); [DllImport("user32.dll")] public static extern int CallNextHookEx(int hookHandle, int code, int wparam, int lparam); [DllImport("user32.dll")] public static extern bool UnhookWindowsHookEx(int hookHandle); [DllImport("user32.dll")] public static extern int GetAsyncKeyState(int vKey); // Fields, constants, and structures used by the keyboard hook int hookHandle = 0; HookProc cb = null; public const int WH_KEYBOARD = 2; public const int HC_ACTION = 0; public const int HC_NOREMOVE = 3; public const int VK_CONTROL = 0x11; public const int VK_LWIN = 0x5B; public const int VK_RWIN = 0x5C; public const int VK_APPS = 0x5D; public const int VK_LSHIFT = 0xA0; public const int VK_RSHIFT = 0xA1; public const int VK_LCONTROL = 0xA2; public const int VK_RCONTROL = 0xA3; public const int VK_LMENU = 0xA4; public const int VK_RMENU = 0xA5; public const int VK_BROWSER_BACK = 0xA6; public const int VK_BROWSER_FORWARD = 0xA7; public const int VK_BROWSER_REFRESH = 0xA8; public const int VK_BROWSER_STOP = 0xA9; public const int VK_BROWSER_SEARCH = 0xAA; public const int VK_VOLUME_MUTE = 0xAD; public const int VK_VOLUME_DOWN = 0xAE; public const int VK_VOLUME_UP = 0xAF; public const int VK_MEDIA_NEXT_TRACK = 0xB0; public const int VK_MEDIA_PREV_TRACK = 0xB1; public const int VK_MEDIA_STOP = 0xB2; public const int VK_MEDIA_PLAY_PAUSE = 0xB3; // Keyboard hook delegate public delegate int HookProc(int code, int wparam, int lparam); public int Proc(int code, int wparam, int lparam) { if (code == HC_ACTION) { switch (wparam) { case VK_BROWSER_BACK: // Handle Back keyboard button here textBox1.Text += "Browser Back key caught" + Environment.NewLine; break; case VK_BROWSER_FORWARD: // Handle Forward keyboard button here textBox1.Text += "Browser Forward key caught" + Environment.NewLine; break; case VK_BROWSER_REFRESH: // Handle Refresh keyboard button here textBox1.Text += "Browser Refresh key caught" + Environment.NewLine; break; case VK_BROWSER_STOP: // Handle Stop keyboard button here textBox1.Text += "Browser Stop key caught" + Environment.NewLine; break; case VK_BROWSER_SEARCH: // Handle Search keyboard button here textBox1.Text += "Browser Search key caught" + Environment.NewLine; break; case VK_VOLUME_MUTE: // Handle Mute keyboard button here textBox1.Text += "Volume Mute key caught" + Environment.NewLine; break; case VK_VOLUME_DOWN: // Handle Volume - keyboard button here textBox1.Text += "Volume Down key caught" + Environment.NewLine; break; case VK_VOLUME_UP: // Handle Volume + keyboard button here textBox1.Text += "Volume Up key caught" + Environment.NewLine; break; case VK_MEDIA_NEXT_TRACK: // Handle Next Track keyboard button here textBox1.Text += "Media Next Track key caught" + Environment.NewLine; break; case VK_MEDIA_PREV_TRACK: // Handle Previous Track keyboard button here textBox1.Text += "Media Previous Track key caught" + Environment.NewLine; break; case VK_MEDIA_STOP: // Handle Stop keyboard button here textBox1.Text += "Media Stop key caught" + Environment.NewLine; break; case VK_MEDIA_PLAY_PAUSE: // Handle Play keyboard button here textBox1.Text += "Media Play/Pause key caught" + Environment.NewLine; break; } } return (CallNextHookEx(hookHandle, code, wparam, lparam)); } // Click event handlers for button1 and button2 private void button1_Click(object sender, System.EventArgs e) { // Set the keyboard hook if (hookHandle == 0) { cb = new HookProc(Proc); hookHandle = SetWindowsHookEx(WH_KEYBOARD, cb, 0, AppDomain.GetCurrentThreadId( )); } else { textBox1.Text += "Hook already set" + Environment.NewLine; } textBox1.Text += "Start: " + hookHandle + Environment.NewLine; } private void button2_Click(object sender, System.EventArgs e) { // Unhook the keyboard hook textBox1.Text += "End: " + UnhookWindowsHookEx(hookHandle) + Environment.NewLine; hookHandle = 0; } } }
The hooks provided by the Windows operating system allow for very
powerful code to be written with a minimum of work. The hook used in
this recipe is the WH_KEYBOARD
hook, which watches
messages that are generated by the keyboard.
The WH_KEYBOARD
hook allows keyboard messages to
be watched or discarded. To discard a keyboard message, return a
1
from the Proc
hook callback
method. The
HookProc
delegate is used as the method to which
the keyboard hook calls back whenever a keyboard message is received.
This hook does not allow the message to be modified.
To use a hook, as the code in the Solution section shows, you first need to declare the following three Windows API functions:
SetWindowsHookEx
This API creates the hook specified by the first parameter and attaches it to the callback method specified in the second parameter. The return value of this function is the handle to the newly created hook. This handle needs to be stored so that it can later be used to remove the hook.
CallNextHookEx
This API calls the next hook in the hook chain if
SetWindowsHookEx
has been called multiple times
for a single type of hook. The return value is dependent on the type
of hook that is installed.
UnhookWindowsHookEx
This API removes the callback to the hook specified by the hook
handle passed as its only parameter. The hook handle is returned by
the SetWindowsHookEx
method. This hook handle is
returned by the SetWindowHookEx
function.
Once these functions are declared, the next step is to declare the
delegate for the hook callback method. This hook callback method is
automatically invoked whenever a keyboard message is sent. The return
value of both the delegate and callback methods is the return value
of the CallNextHookEx
API method.
The keyboard hook used in this recipe will intercept only messages
that are sent to the message queue of the thread on which the hook is
installed. The thread on which to install the hook is passed as the
fourth argument of the SetWindowsHookEx
API
method. For this recipe, the current thread is passed as an argument
using the static AppDomain.GetCurrentThreadId
method. Therefore, if you have a multithreaded application and you
want each thread to intercept messages sent by the keyboard, you will
have to call SetWindowsHookEx
on each thread to
set up the WH_KEYBOARD
hook.
The keyboard hook can also be used to capture keys pressed in combination. For example, if the Windows Menu key is pressed along with the V key, a keyboard hook callback procedure can be implemented to capture this action:
// Hook callback method public int Proc(int code, int wparam, int lparam) { if (code == HC_ACTION) { // Check the state of the Window's keyboard Pop-Up Menu key int state = GetAsyncKeyState(VK_APPS); // Is the Menu key already down? if ((state & 0x8000) == 0x8000) { // Is the key up? if ((lparam & 0x80000000) == 0x80000000) { // Is this the v key? if (wparam == 0x56) { // Handle AppMenu-v key combination here... textBox1.Text += "AppMenu-v action caught" + Environment.NewLine; } } } } return (CallNextHookEx(hookHandle, code, wparam, lparam)); }
This callback gets the state of the Menu key and determines whether
it is depressed ((state
& 0x8000)
==
0x8000)
.
If it is depressed, the V key is checked to see if it is being
released ((lparam
&
0x80000000)
==
0x80000000)
. If these conditions are true, a
message is displayed. (Of course, you could add your own code here to
do something more interesting.)
See Recipe 7.11; Subclassing & Hooking with Visual Basic by Stephen Teilhet (O’Reilly); and see the “Delegate Class” and “Hooks” topics in the MSDN documentation.