我正在尝试在后台线程上设置鼠标挂钩。
delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
LowLevelMouseProc _proc = HookCallback;
SetWindowsHookEx(PInvoke.WH_MOUSE_LL, _proc, IntPtr.Zero, 0);
和
IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam){/**/}
如果我把它放在主窗口上,那么一切都有效,直到窗口必须做更复杂的工作,导致鼠标停止响应该工作的持续时间(例如,在面板中更新多个子节点)。
如果我启动一个新的Thread并从那里设置了钩子,问题是线程在设置钩子后简单地退出并且永远不会调用回调函数。
有没有办法让线程保持活着? 或者,如果存在另一种方法来连接鼠标而不冒无反应行为的风险?
我意外地注意到当工作线程执行时
GetMessage(out msg, new IntPtr(0), 0, 0);
没有收到任何消息但是线程正在保持活动以达到所需的目的。 此外,我需要一种优雅的方式来关闭线程,但GetMessage永远不会返回。
我不太了解所有这些消息,我只是希望能够连接鼠标并防止它冻结。
任何帮助都是适当的。
答案 0 :(得分:1)
低级鼠标挂钩需要在调用SetWindowsHookEx的线程中运行消息循环。这就是为什么它不能在简单的后台线程中工作,并且在UI线程中工作。如果要在后台线程中使用此挂钩,请在SetWindowsHookEx之后调用Application.Run方法。线程保留在此循环中并处理低级别挂钩消息。
答案 1 :(得分:0)
在你的钩子回调方法中,只需启动一个新线程。像这样:
IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
ThreadPool.QueueUserWorkItem(obj =>
{
// do whatever ...
});
}
如果您的处理需要访问控件上的表单,请不要忘记在主线程上调用。
编辑:
如果你在主表单线程上做了一些冻结UI的事情,你应该考虑在后台线程而不是主线程中执行该操作。当您需要根据处理更新控件时,您可以调用。
this.WhateverControl.Invoke( /* ... /* );
答案 2 :(得分:0)
我的程序中遇到了类似的问题。 创建键盘钩子后,将钩子的回调传递给UI线程。当UI线程忙时,回调是在UI调度程序队列中,但如果回调将持续太长时间,则窗口将取消挂钩。 我为这个回调创建单独线程的尝试是无用的,直到我尝试在该线程中运行单独的Dispatcher。 所以,我正试图用这段代码来解决我的问题:
private void InitializeKeyboardHookWithSeparateDispatcher()
{
using (var objCreated = new ManualResetEventSlim(false))
{
var thread = new Thread(() =>
{
_keyboardListener = new KeyboardListener();
// ReSharper disable once AccessToDisposedClosure
objCreated.Set();
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
objCreated.Wait();
}
}
KeyboardListener是我的高级抽象,它实际上调用了SetWindowsHookEx。 我正在使用manualreseteventslim,因为需要使用构造函数注入将KeyboardListener注入到我的程序的其他对象中。