在WPF / C#中使用全局键盘挂钩(WH_KEYBOARD_LL)

我将自己在Internet WH_KEYBOARD_LL助手类中找到的代码拼接在一起:

将以下代码放入您的某些utils库中,将其 为 :

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);

}

我这样使用:

<Application ...

Startup="Application_Startup"

Exit="Application_Exit">

...

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());

// I tried writing the data in file here also, to make sure the problem is not in Console.WriteLine

}

private void Application_Exit(object sender, ExitEventArgs e)

{

KListener.Dispose();

}

}

问题在于,它会 。如此一来,不会出现任何错误,只是一段时间后我什么也没输出。当它停止工作时,我找不到稳定的模式。

重现此问题很简单,通常会在窗户外面,像疯子一样敲一些键。

我怀疑背后有一些邪恶的 线程问题 ,有人知道如何使它继续工作吗?


我已经尝试过的

  1. return HookCallbackInner(nCode, wParam, lParam);用简单的东西代替。
  2. 用异步调用替换它,尝试将Sleep设置为5000ms(等等)。

异步调用并没有使它更好地工作,它似乎总是在用户将单个字母保持一小段时间时停止。

回答:

您正在SetHook方法调用中内联创建回调委托。那个委托最终将被垃圾收集,因为您没有在任何地方保留对它的引用。一旦委托被垃圾回收,您将不再获得任何回调。

为避免这种情况,只要挂钩就位,就需要保持对委托的引用(直到您调用UnhookWindowsHookEx)。

以上是 在WPF / C#中使用全局键盘挂钩(WH_KEYBOARD_LL) 的全部内容, 来源链接: utcz.com/qa/431910.html

回到顶部