C#
  1  /// KEYBOARD.CS
  2  /// (c) 2006 by Emma Burrows
  3  /// This file contains the following items:
  4  ///  - KeyboardHook: class to enable low-level keyboard hook using
  5  ///    the Windows API.
  6  ///  - KeyboardHookEventHandler: delegate to handle the KeyIntercepted
  7  ///    event raised by the KeyboardHook class.
  8  ///  - KeyboardHookEventArgs: EventArgs class to contain the information
  9  ///    returned by the KeyIntercepted event.
 10  ///    
 11  /// Change history:
 12  /// 17/06/06: 1.0 - First version.
 13  /// 18/06/06: 1.1 - Modified proc assignment in constructor to make class backward 
 14  ///                 compatible with 2003.
 15  /// 10/07/06: 1.2 - Added support for modifier keys:
 16  ///                 -Changed filter in HookCallback to WM_KEYUP instead of WM_KEYDOWN
 17  ///                 -Imported GetKeyState from user32.dll
 18  ///                 -Moved native DLL imports to a separate internal class as this 
 19  ///                  is a Good Idea according to Microsoft's guidelines
 20  /// 13/02/07: 1.3 - Improved modifier key support:
 21  ///                 -Added CheckModifiers() method
 22  ///                 -Deleted LoWord/HiWord methods as they weren't necessary
 23  ///                 -Implemented Barry Dorman's suggestion to AND GetKeyState
 24  ///                  values with 0x8000 to get their result
 25  
 26  using System;
 27  using System.Diagnostics;
 28  using System.Windows.Forms;
 29  using System.Runtime.InteropServices;
 30  using System.Text;
 31  
 32  /// <summary>
 33  /// Low-level keyboard intercept class to trap and suppress system keys.
 34  /// </summary>
 35  public class KeyboardHook : IDisposable
 36  {
 37      /// <summary>
 38      /// Parameters accepted by the KeyboardHook constructor.
 39      /// </summary>
 40      public enum Parameters
 41      {
 42          None,
 43          AllowAltTab,
 44          AllowWindowsKey,
 45          AllowAltTabAndWindows,
 46          PassAllKeysToNextApp
 47      }
 48  
 49      //Internal parameters
 50      private bool PassAllKeysToNextApp = false;
 51      private bool AllowAltTab = false;
 52      private bool AllowWindowsKey = false;
 53  
 54      //Keyboard API constants
 55      private const int WH_KEYBOARD_LL = 13;
 56      private const int WM_KEYUP = 0x0101;
 57      private const int WM_SYSKEYUP = 0x0105;
 58  
 59      //Modifier key constants
 60      private const int VK_SHIFT = 0x10;
 61      private const int VK_CONTROL = 0x11;
 62      private const int VK_MENU = 0x12;
 63      private const int VK_CAPITAL = 0x14;
 64  
 65      //Variables used in the call to SetWindowsHookEx
 66      private HookHandlerDelegate proc;
 67      private IntPtr hookID = IntPtr.Zero;
 68      internal delegate IntPtr HookHandlerDelegate(
 69          int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
 70  
 71      /// <summary>
 72      /// Event triggered when a keystroke is intercepted by the 
 73      /// low-level hook.
 74      /// </summary>
 75      public event KeyboardHookEventHandler KeyIntercepted;
 76  
 77      // Structure returned by the hook whenever a key is pressed
 78      internal struct KBDLLHOOKSTRUCT
 79      {
 80          public int vkCode;
 81          int scanCode;
 82          public int flags;
 83          int time;
 84          int dwExtraInfo;
 85      }
 86  
 87      #region Constructors
 88      /// <summary>
 89      /// Sets up a keyboard hook to trap all keystrokes without 
 90      /// passing any to other applications.
 91      /// </summary>
 92      public KeyboardHook()
 93      {
 94          proc = new HookHandlerDelegate(HookCallback);
 95          using (Process curProcess = Process.GetCurrentProcess())
 96          using (ProcessModule curModule = curProcess.MainModule)
 97          {
 98              hookID = NativeMethods.SetWindowsHookEx(WH_KEYBOARD_LL, proc,
 99                  NativeMethods.GetModuleHandle(curModule.ModuleName), 0);
100          }
101      }
102  
103      /// <summary>
104      /// Sets up a keyboard hook with custom parameters.
105      /// </summary>
106      /// <param name="param">A valid name from the Parameter enum; otherwise, the 
107      /// default parameter Parameter.None will be used.</param>
108      public KeyboardHook(string param)
109          : this()
110      {
111          if (!String.IsNullOrEmpty(param) && Enum.IsDefined(typeof(Parameters), param))
112          {
113              SetParameters((Parameters)Enum.Parse(typeof(Parameters), param));
114          }
115      }
116  
117      /// <summary>
118      /// Sets up a keyboard hook with custom parameters.
119      /// </summary>
120      /// <param name="param">A value from the Parameters enum.</param>
121      public KeyboardHook(Parameters param)
122          : this()
123      {
124          SetParameters(param);
125      }
126      
127      private void SetParameters(Parameters param)
128      {
129          switch (param)
130          {
131              case Parameters.None:
132                  break;
133              case Parameters.AllowAltTab:
134                  AllowAltTab = true;
135                  break;
136              case Parameters.AllowWindowsKey:
137                  AllowWindowsKey = true;
138                  break;
139              case Parameters.AllowAltTabAndWindows:
140                  AllowAltTab = true;
141                  AllowWindowsKey = true;
142                  break;
143              case Parameters.PassAllKeysToNextApp:
144                  PassAllKeysToNextApp = true;
145                  break;
146          }
147      }
148      #endregion
149  
150      #region Check Modifier keys
151      /// <summary>
152      /// Checks whether Alt, Shift, Control or CapsLock
153      /// is enabled at the same time as another key.
154      /// Modify the relevant sections and return type 
155      /// depending on what you want to do with modifier keys.
156      /// </summary>
157      private void CheckModifiers()
158      {
159          StringBuilder sb = new StringBuilder();
160  
161          if ((NativeMethods.GetKeyState(VK_CAPITAL) & 0x0001) != 0)
162          {
163              //CAPSLOCK is ON
164              sb.AppendLine("Capslock is enabled.");
165          }
166  
167          if ((NativeMethods.GetKeyState(VK_SHIFT) & 0x8000) != 0)
168          { 
169              //SHIFT is pressed
170              sb.AppendLine("Shift is pressed.");
171          }
172          if ((NativeMethods.GetKeyState(VK_CONTROL) & 0x8000) != 0)
173          {
174              //CONTROL is pressed
175              sb.AppendLine("Control is pressed.");
176          }
177          if ((NativeMethods.GetKeyState(VK_MENU) & 0x8000) != 0)
178          {
179              //ALT is pressed
180              sb.AppendLine("Alt is pressed.");
181          }
182          Console.WriteLine(sb.ToString());
183      }
184      #endregion Check Modifier keys
185  
186      #region Hook Callback Method
187      /// <summary>
188      /// Processes the key event captured by the hook.
189      /// </summary>
190      private IntPtr HookCallback(
191          int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam)
192      {
193          bool AllowKey = PassAllKeysToNextApp;
194  
195          //Filter wParam for KeyUp events only
196          if (nCode >= 0 && 
197              (wParam == (IntPtr)WM_KEYUP || wParam == (IntPtr)WM_SYSKEYUP))
198          {
199  
200              // Check for modifier keys, but only if the key being
201              // currently processed isn't a modifier key (in other
202              // words, CheckModifiers will only run if Ctrl, Shift,
203              // CapsLock or Alt are active at the same time as
204              // another key)
205              if (!(lParam.vkCode >= 160 && lParam.vkCode <= 164))
206              {
207                  CheckModifiers();
208              }
209  
210              // Check for key combinations that are allowed to 
211              // get through to Windows
212              //
213              // Ctrl+Esc or Windows key
214              if (AllowWindowsKey)
215              {
216                  switch (lParam.flags)
217                  {
218                      //Ctrl+Esc
219                      case 0:
220                          if (lParam.vkCode == 27)
221                              AllowKey = true;
222                          break;
223  
224                      //Windows keys
225                      case 1:
226                          if ((lParam.vkCode == 91) || (lParam.vkCode == 92))
227                              AllowKey = true;
228                          break;
229                  }
230              }
231              // Alt+Tab
232              if (AllowAltTab)
233              {
234                  if ((lParam.flags == 32) && (lParam.vkCode == 9))
235                      AllowKey = true;
236              }
237  
238              OnKeyIntercepted(new KeyboardHookEventArgs(lParam.vkCode, AllowKey));
239  
240              //If this key is being suppressed, return a dummy value
241              if (AllowKey == false)
242                  return (System.IntPtr)1;
243          }
244          //Pass key to next application
245          return NativeMethods.CallNextHookEx(hookID, nCode, wParam, ref lParam);
246  
247      }
248      #endregion
249  
250      #region Event Handling
251      /// <summary>
252      /// Raises the KeyIntercepted event.
253      /// </summary>
254      /// <param name="e">An instance of KeyboardHookEventArgs</param>
255      public void OnKeyIntercepted(KeyboardHookEventArgs e)
256      {
257          if (KeyIntercepted != null)
258              KeyIntercepted(e);
259      }
260  
261      /// <summary>
262      /// Delegate for KeyboardHook event handling.
263      /// </summary>
264      /// <param name="e">An instance of InterceptKeysEventArgs.</param>
265      public delegate void KeyboardHookEventHandler(KeyboardHookEventArgs e);
266  
267      /// <summary>
268      /// Event arguments for the KeyboardHook class's KeyIntercepted event.
269      /// </summary>
270      public class KeyboardHookEventArgs : System.EventArgs
271      {
272  
273          private string keyName;
274          private int keyCode;
275          private bool passThrough;
276  
277          /// <summary>
278          /// The name of the key that was pressed.
279          /// </summary>
280          public string KeyName
281          {
282              get { return keyName; }
283          }
284  
285          /// <summary>
286          /// The virtual key code of the key that was pressed.
287          /// </summary>
288          public int KeyCode
289          {
290              get { return keyCode; }
291          }
292  
293          /// <summary>
294          /// True if this key combination was passed to other applications,
295          /// false if it was trapped.
296          /// </summary>
297          public bool PassThrough
298          {
299              get { return passThrough; }
300          }
301  
302          public KeyboardHookEventArgs(int evtKeyCode, bool evtPassThrough)
303          {
304              keyName = ((Keys)evtKeyCode).ToString();
305              keyCode = evtKeyCode;
306              passThrough = evtPassThrough;
307          }
308  
309      }
310  
311      #endregion
312  
313      #region IDisposable Members
314      /// <summary>
315      /// Releases the keyboard hook.
316      /// </summary>
317      public void Dispose()
318      {
319          NativeMethods.UnhookWindowsHookEx(hookID);
320      }
321      #endregion
322  
323      #region Native methods
324  
325      [ComVisibleAttribute(false),
326       System.Security.SuppressUnmanagedCodeSecurity()]
327      internal class NativeMethods
328      {
329          [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
330          public static extern IntPtr GetModuleHandle(string lpModuleName);
331  
332          [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
333          public static extern IntPtr SetWindowsHookEx(int idHook,
334              HookHandlerDelegate lpfn, IntPtr hMod, uint dwThreadId);
335  
336          [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
337          [return: MarshalAs(UnmanagedType.Bool)]
338          public static extern bool UnhookWindowsHookEx(IntPtr hhk);
339  
340          [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
341          public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
342              IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
343  
344          [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
345          public static extern short GetKeyState(int keyCode);
346          
347      } 
348   
349  
350      #endregion
351  }