I stitched together the code that I found on the Internet, the WH_KEYBOARD_LL class:
Put the following code in some of your utils libs, let it be YourUtils.cs :
using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Windows.Input; namespace MYCOMPANYHERE.WPF.KeyboardHelper { public class KeyboardListener : IDisposable { private static IntPtr hookId = IntPtr.Zero; [MethodImpl(MethodImplOptions.NoInlining)] private IntPtr HookCallback( int nCode, IntPtr wParam, IntPtr lParam) { try { return HookCallbackInner(nCode, wParam, lParam); } catch { Console.WriteLine("There was some error somewhere..."); } return InterceptKeys.CallNextHookEx(hookId, nCode, wParam, lParam); } private IntPtr HookCallbackInner(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { if (wParam == (IntPtr)InterceptKeys.WM_KEYDOWN) { int vkCode = Marshal.ReadInt32(lParam); if (KeyDown != null) KeyDown(this, new RawKeyEventArgs(vkCode, false)); } else if (wParam == (IntPtr)InterceptKeys.WM_KEYUP) { int vkCode = Marshal.ReadInt32(lParam); if (KeyUp != null) KeyUp(this, new RawKeyEventArgs(vkCode, false)); } } return InterceptKeys.CallNextHookEx(hookId, nCode, wParam, lParam); } public event RawKeyEventHandler KeyDown; public event RawKeyEventHandler KeyUp; public KeyboardListener() { hookId = InterceptKeys.SetHook((InterceptKeys.LowLevelKeyboardProc)HookCallback); } ~KeyboardListener() { Dispose(); } #region IDisposable Members public void Dispose() { InterceptKeys.UnhookWindowsHookEx(hookId); } #endregion } internal static class InterceptKeys { public delegate IntPtr LowLevelKeyboardProc( int nCode, IntPtr wParam, IntPtr lParam); public static int WH_KEYBOARD_LL = 13; public static int WM_KEYDOWN = 0x0100; public static int WM_KEYUP = 0x0101; 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, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr GetModuleHandle(string lpModuleName); } public class RawKeyEventArgs : EventArgs { public int VKCode; public Key Key; public bool IsSysKey; public RawKeyEventArgs(int VKCode, bool isSysKey) { this.VKCode = VKCode; this.IsSysKey = isSysKey; this.Key = System.Windows.Input.KeyInterop.KeyFromVirtualKey(VKCode); } } public delegate void RawKeyEventHandler(object sender, RawKeyEventArgs args); }
What I use like this:
App.xaml :
<Application ... Startup="Application_Startup" Exit="Application_Exit"> ...
App.xaml.cs :
public partial class App : Application { KeyboardListener KListener = new KeyboardListener(); private void Application_Startup(object sender, StartupEventArgs e) { KListener.KeyDown += new RawKeyEventHandler(KListener_KeyDown); } void KListener_KeyDown(object sender, RawKeyEventArgs args) { Console.WriteLine(args.Key.ToString());
The problem is that it stops working after pressing keys . No error occurs that is always the case, I just get nothing from the output after a while. When it stops working, I can not find a solid pattern.
The reproduction of this problem is quite simple, some keys hit like a crazy person, usually outside the window.
I suspect there is some kind of problem with the thread, did anyone understand how to do this?
I have already tried:
- Replacement
return HookCallbackInner(nCode, wParam, lParam); with something simple. - Replace it with an asynchronous call, trying to set Sleep 5000ms (etc.).
An asynchronous call did not help him work better; he always stops when the user holds one letter for a while.