Recipe9.10.Using the Windows Keyboard Hook


Recipe 9.10. Using the Windows Keyboard Hook

Problem

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.

Solution

The Windows Forms application shown in Example 9-13 uses the WH_KEYBOARD Windows hook.

Example 9-13. Using the Windows keyboard 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 GetKeyState(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;         public const int KF_UP = 0x8000;         public const long KB_TRANSITION_FLAG = 0x80000000;         public const int VK_W = 0x57;                  // 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;         }     } } 

Discussion

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. 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 = GetKeyState(VK_APPS);                  // Is the Menu key already down?         if ((state & KF_UP) == KF_UP)         {             // Is the key up?             if ((lparam & KB_TRANSITION_FLAG) == KB_TRANSITION_FLAG)             {                 // Is this the V key?                 if (wparam == VK_V)                 {                     // 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 & KF_UP) ==KF_UP). If it is depressed, the V key is checked to see if it is being released ((lparam & KB_TRANSITION_FLAG) == KB_TRANSITION_FLAG). If these conditions are true, a message is displayed. (Of course, you could add your own code here to do something more interesting.)

See Also

See Recipe 9.11; see Subclassing & Hooking with Visual Basic (O'Reilly); and see the "Delegate Class" and "Hooks" topics in the MSDN documentation.



C# Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2004
Pages: 424

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net