title: C# API For Globally Capturing Media Center Remote Special Keys
body: <p>I'm writing a media application and I want to have it work with a standard Media Center remote.
  Arrow keys, Next and Enter work fine (and others I'm sure, but that's what I'm using), but Play and Pause do not work.  I'm capturing the other keys with a global hook to the WH_KEYBOARD_LL event.</p>
  
  <p>When pressing Play or Pause (not to be confused with Play/Pause on a media keyboard... that works) there are no events, it seems it does not use keyboard events.  </p>
  
  <p>Is there a standard way in C# to capture these buttons globally?</p>
  
  <p><strong>Update:</strong></p>
  
  <p>Here's the hook code I'm using:</p>
  
  <pre><code>using System;
  using System.Diagnostics;
  using System.Runtime.CompilerServices;
  using System.Runtime.InteropServices;
  using System.Windows.Input;
  
  namespace Elpis.KeyboardHook
  {
      /// &lt;summary&gt;
      /// Listens keyboard globally.
      /// 
      /// &lt;remarks&gt;Uses WH_KEYBOARD_LL.&lt;/remarks&gt;
      /// &lt;/summary&gt;
      public class KeyboardListener : IDisposable
      {
          /// &lt;summary&gt;
          /// Creates global keyboard listener.
          /// &lt;/summary&gt;
          public KeyboardListener()
          {
              // We have to store the HookCallback, so that it is not garbage collected runtime
              hookedLowLevelKeyboardProc = LowLevelKeyboardProc;
  
              // Set the hook
              hookId = InterceptKeys.SetHook(hookedLowLevelKeyboardProc);
  
              // Assign the asynchronous callback event
              hookedKeyboardCallbackAsync = KeyboardListener_KeyboardCallbackAsync;
          }
  
          #region IDisposable Members
  
          /// &lt;summary&gt;
          /// Disposes the hook.
          /// &lt;remarks&gt;This call is required as it calls the UnhookWindowsHookEx.&lt;/remarks&gt;
          /// &lt;/summary&gt;
          public void Dispose()
          {
              InterceptKeys.UnhookWindowsHookEx(hookId);
          }
  
          #endregion
  
          /// &lt;summary&gt;
          /// Destroys global keyboard listener.
          /// &lt;/summary&gt;
          ~KeyboardListener()
          {
              Dispose();
          }
  
          /// &lt;summary&gt;
          /// Fired when any of the keys is pressed down.
          /// &lt;/summary&gt;
          public event RawKeyEventHandler KeyDown;
  
          /// &lt;summary&gt;
          /// Fired when any of the keys is released.
          /// &lt;/summary&gt;
          public event RawKeyEventHandler KeyUp;
  
          #region Inner workings
  
          /// &lt;summary&gt;
          /// Hook ID
          /// &lt;/summary&gt;
          private readonly IntPtr hookId = IntPtr.Zero;
  
          /// &lt;summary&gt;
          /// Event to be invoked asynchronously (BeginInvoke) each time key is pressed.
          /// &lt;/summary&gt;
          private readonly KeyboardCallbackAsync hookedKeyboardCallbackAsync;
  
          /// &lt;summary&gt;
          /// Contains the hooked callback in runtime.
          /// &lt;/summary&gt;
          private readonly InterceptKeys.LowLevelKeyboardProc hookedLowLevelKeyboardProc;
  
          /// &lt;summary&gt;
          /// Actual callback hook.
          /// 
          /// &lt;remarks&gt;Calls asynchronously the asyncCallback.&lt;/remarks&gt;
          /// &lt;/summary&gt;
          /// &lt;param name="nCode"&gt;&lt;/param&gt;
          /// &lt;param name="wParam"&gt;&lt;/param&gt;
          /// &lt;param name="lParam"&gt;&lt;/param&gt;
          /// &lt;returns&gt;&lt;/returns&gt;
          [MethodImpl(MethodImplOptions.NoInlining)]
          private IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam)
          {
              if (nCode &gt;= 0)
                  if (wParam.ToUInt32() == (int) InterceptKeys.KeyEvent.WM_KEYDOWN ||
                      wParam.ToUInt32() == (int) InterceptKeys.KeyEvent.WM_KEYUP ||
                      wParam.ToUInt32() == (int) InterceptKeys.KeyEvent.WM_SYSKEYDOWN ||
                      wParam.ToUInt32() == (int) InterceptKeys.KeyEvent.WM_SYSKEYUP)
                      hookedKeyboardCallbackAsync.BeginInvoke((InterceptKeys.KeyEvent) wParam.ToUInt32(),
                                                              Marshal.ReadInt32(lParam), null, null);
  
              return InterceptKeys.CallNextHookEx(hookId, nCode, wParam, lParam);
          }
  
          /// &lt;summary&gt;
          /// HookCallbackAsync procedure that calls accordingly the KeyDown or KeyUp events.
          /// &lt;/summary&gt;
          /// &lt;param name="keyEvent"&gt;Keyboard event&lt;/param&gt;
          /// &lt;param name="vkCode"&gt;VKCode&lt;/param&gt;
          private void KeyboardListener_KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode)
          {
              switch (keyEvent)
              {
                      // KeyDown events
                  case InterceptKeys.KeyEvent.WM_KEYDOWN:
                      if (KeyDown != null)
                          KeyDown(this, new RawKeyEventArgs(vkCode, false));
                      break;
                  case InterceptKeys.KeyEvent.WM_SYSKEYDOWN:
                      if (KeyDown != null)
                          KeyDown(this, new RawKeyEventArgs(vkCode, true));
                      break;
  
                      // KeyUp events
                  case InterceptKeys.KeyEvent.WM_KEYUP:
                      if (KeyUp != null)
                          KeyUp(this, new RawKeyEventArgs(vkCode, false));
                      break;
                  case InterceptKeys.KeyEvent.WM_SYSKEYUP:
                      if (KeyUp != null)
                          KeyUp(this, new RawKeyEventArgs(vkCode, true));
                      break;
              }
          }
  
          private delegate void KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode);
  
          #endregion
      }
  
      /// &lt;summary&gt;
      /// Raw KeyEvent arguments.
      /// &lt;/summary&gt;
      public class RawKeyEventArgs : EventArgs
      {
          /// &lt;summary&gt;
          /// Is the hitted key system key.
          /// &lt;/summary&gt;
          public bool IsSysKey;
  
          /// &lt;summary&gt;
          /// WPF Key of the key.
          /// &lt;/summary&gt;
          public Key Key;
  
          /// &lt;summary&gt;
          /// VKCode of the key.
          /// &lt;/summary&gt;
          public int VKCode;
  
          /// &lt;summary&gt;
          /// Create raw keyevent arguments.
          /// &lt;/summary&gt;
          /// &lt;param name="VKCode"&gt;&lt;/param&gt;
          /// &lt;param name="isSysKey"&gt;&lt;/param&gt;
          public RawKeyEventArgs(int VKCode, bool isSysKey)
          {
              this.VKCode = VKCode;
              IsSysKey = isSysKey;
              Key = KeyInterop.KeyFromVirtualKey(VKCode);
          }
      }
  
      /// &lt;summary&gt;
      /// Raw keyevent handler.
      /// &lt;/summary&gt;
      /// &lt;param name="sender"&gt;sender&lt;/param&gt;
      /// &lt;param name="args"&gt;raw keyevent arguments&lt;/param&gt;
      public delegate void RawKeyEventHandler(object sender, RawKeyEventArgs args);
  
      #region WINAPI Helper class
  
      /// &lt;summary&gt;
      /// Winapi Key interception helper class.
      /// &lt;/summary&gt;
      internal static class InterceptKeys
      {
          #region Delegates
  
          public delegate IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam);
  
          #endregion
  
          #region KeyEvent enum
  
          public enum KeyEvent
          {
              WM_KEYDOWN = 256,
              WM_KEYUP = 257,
              WM_SYSKEYUP = 261,
              WM_SYSKEYDOWN = 260
          }
  
          #endregion
  
          public static int WH_KEYBOARD_LL = 13;
  
          public static IntPtr SetHook(LowLevelKeyboardProc proc)
          {
              using (Process curProcess = Process.GetCurrentProcess())
              using (ProcessModule curModule = curProcess.MainModule)
              {
                  return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
                                          GetModuleHandle(curModule.ModuleName), 0);
              }
          }
  
          [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
          public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
  
          [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
          [return: MarshalAs(UnmanagedType.Bool)]
          public static extern bool UnhookWindowsHookEx(IntPtr hhk);
  
          [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
          public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, UIntPtr wParam, IntPtr lParam);
  
          [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
          public static extern IntPtr GetModuleHandle(string lpModuleName);
      }
  
      #endregion
  }
  </code></pre>