我尝试在windows钩子上创建热键。它是有效的,但有时wpf窗口,我使用这个解决方案的地方,抛出System.ExecutionEngineException(当我快速按下很多按钮时经常发生。我发现这是因为消息循环被我的钩子破坏了。有没有一些方法可以避免它?
我知道RegisterHotKey功能,但我需要的能力,以中止按键压力,如果我的热键激活(以防止执行类似的热键在其他程序)。
下面是我的hook设置:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
private 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);
}
}
我的钩子:
private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
bool isKeyProcessed = false;
try
{
if (nCode >= 0 && (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN))
{
int vkCode = Marshal.ReadInt32(lParam);
isKeyProcessed = KeyDown?.Invoke((Keys)vkCode) ?? false;
}
else if (nCode >= 0 && (wParam == (IntPtr)WM_KEYUP || wParam == (IntPtr)WM_SYSKEYUP))
{
int vkCode = Marshal.ReadInt32(lParam);
isKeyProcessed = KeyUp?.Invoke((Keys)vkCode) ?? false;
}
}
catch (Exception ex)
{
isKeyProcessed = false;
try
{
ExceptionHappend?.Invoke(ex);
}
catch { }
}
if (!isKeyProcessed)
return CallNextHookEx(_hookID, nCode, wParam, lParam);
else
return (IntPtr)1;
}
这是我的钩子事件处理程序(对于按下键,在按下键时,我只是从当前组合键中删除了按下键的按钮)
public bool HandleKeyDown(Keys key)
{
lock (_lock)
{
currentCombo.Keys.Add(key);
if (hotkeys.ContainsKey(currentCombo))
{
RunHandler(hotkeys[currentCombo]);
currentCombo.Keys.Clear();
return true;
}
return false;
}
}
private void RunHandler(Action handler)
{
_runningActions = _runningActions.Where(x=>x.Status == TaskStatus.Running).ToList();
var task = new Task(handler);
task.Start();
_runningActions.Add(task);
}
最后,我的热键,示例:
public void ShowWorkedQ()
{
Dispatcher.Invoke(new Action(() =>
{
OutLabel.Text = "Pressed Alt+Q";
}));
}
感谢您的长期阅读!
1条答案
按热度按时间h9vpoimq1#
看起来您没有保持回调委托活动。您需要将其存储在字段中以防止其被销毁。
当你传递一个委托给PInvoke函数时,PInvoke将创建一个小的本机函数,使回调能够跳回到托管代码。但是GC不能正确跟踪这个对象,除非你仍然持有对原始委托的引用。
还要注意的是,低级KB钩子中的
lParam
实际上表示指向KBDLLHOOKSTRUCT
的指针。虽然您当前的代码 * 技术上 * 只读取第一个值,但您实际上应该封送整个结构。