我已经设置了一个键盘挂钩来捕获用户按下的按钮。但是,它运行良好,当显示模式表单时(它实际上用作自定义消息框),应用程序崩溃。
我读过你永远不应该在钩子中调用阻塞函数。在我读到的建议之一是在线程中调用钩子事件。我已经尝试过了,但它仍然会导致应用程序崩溃。
有人对如何解决这个问题有任何想法吗?或者任何人如何在不使用键盘挂钩的情况下建议另一种方法来捕获按钮按下
谢谢
设置和启动钩子
public void Start()
{
if (m_HookHandle == IntPtr.Zero)
{
m_HookDelegate = new HookProc(this.HookProcedure);
m_HookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, m_HookDelegate, GetModuleHandle(null), 0);
if (m_HookHandle == IntPtr.Zero)
{
throw new SystemException("Failed to acquire a valid hook handle");
}
}
}
这是回调的 Hook 过程
private int HookProcedure(int code, IntPtr wParam, IntPtr lParam)
{
bool handled = false;
if ((code >= 0) && (wParam == (IntPtr)0x0100))
{
KBDLLHOOKSTRUCT hookStruct = new KBDLLHOOKSTRUCT();
hookStruct.dwExtraInfo = IntPtr.Zero;
hookStruct.flags = 0;
hookStruct.scanCode = 0;
hookStruct.time = 0;
hookStruct.vkCode = 0;
hookStruct = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
// Let clients determine what to do
KeyboardHookEventArgs e = new KeyboardHookEventArgs();
e.HCode = code;
e.WParam = wParam;
e.LParam = lParam;
e.Handled = false;
KeyBoardInfo keyInfo = new KeyBoardInfo();
keyInfo.VKCode = hookStruct.vkCode;
keyInfo.ScanCode = hookStruct.scanCode;
PublishHookEvent(e, keyInfo);
handled = e.Handled;
}
// Yield to the next hook in the chain
if (handled)
return -1;
else
return CallNextHookEx(m_HookHandle, code, wParam, lParam);
}
每当接收到钩子事件时,PublishHookEvent 都会调用每个注册的监听器来处理钩子事件
protected void PublishHookEvent(KeyboardHookEventArgs args, KeyBoardInfo keyBoardInfo)
{
if (this.m_KeyboardHookListeners.Count > 0)
{
if (args.WParam.ToInt32() == 256)
{
IMQKeyboardHookListener hookListener = null;
for (int idx = 0; idx < this.m_KeyboardHookListeners.Count; idx++)
{
hookListener = this.m_KeyboardHookListeners[idx];
if (hookListener != null)
hookListener.ProcessKeyboardHookEvent(args, keyBoardInfo);
}
}
}
}
下面是键盘钩子事件的监听器之一
public void ProcessKeyboardHookEvent(KeyboardHookEventArgs e, KeyBoardInfo keyBoardInfo)
{
if (this.InvokeRequired == true)
{
this.BeginInvoke(new Action<KeyboardHookEventArgs, KeyBoardInfo>(ProcessKeyboardHookEvent), new object[] { e, keyBoardInfo });
}
else
{
Keys keyCode = (Keys)keyBoardInfo.VKCode;
if (keyCode == Keys.Return)
{
e.Handled = true;
this.ProcessKeyboardEvent(keyCode, (char)keyCode);
}
}
}
在调用 MessageBoxForm 之前,一切都运行良好。这是一个自定义表单,添加了两个按钮 OK/CANCEL。
public DialogResult ShowMessageBox(string message)
{
this.MessageBoxForm.Message = message;
DialogResult result = this.MessageBoxForm.ShowDialog();
return result;
}
应用程序在调用 ShowDialog() 期间崩溃或挂起。我还尝试在调用 ShowDialog() 之前取消挂钩,有时可以,有时不行。这个想法是首先取消挂钩以防止 ShowDialog 的阻塞调用出现任何问题,然后在表单关闭时再次挂钩。
public DialogResult ShowMessageBox(string message)
{
KeyboardHook.GetInstance().Unhook();
this.MessageBoxForm.Message = message;
DialogResult result = this.MessageBoxForm.ShowDialog();
KeyboardHook.GetInstance().Hook();
return result;
}