C# 后台线程中的LowLevelMouseProc
我正在尝试在背景线程上设置鼠标挂钩C# 后台线程中的LowLevelMouseProc,c#,multithreading,pinvoke,hook,messaging,C#,Multithreading,Pinvoke,Hook,Messaging,我正在尝试在背景线程上设置鼠标挂钩 delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam); LowLevelMouseProc _proc = HookCallback; SetWindowsHookEx(PInvoke.WH_MOUSE_LL, _proc, IntPtr.Zero, 0); 及 如果我把它放在主窗口线程上,一切都会工作,直到窗口必须做更复杂的工作,这会导致鼠标在工作期间停止响应(
delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
LowLevelMouseProc _proc = HookCallback;
SetWindowsHookEx(PInvoke.WH_MOUSE_LL, _proc, IntPtr.Zero, 0);
及
如果我把它放在主窗口线程上,一切都会工作,直到窗口必须做更复杂的工作,这会导致鼠标在工作期间停止响应(例如更新面板中的多个子项)
如果我启动了一个新线程并从那里设置了钩子,问题是在设置了钩子之后线程就退出了,并且回调函数永远不会被调用
有没有办法让线程保持活动状态?
或者如果有另一种方法可以连接鼠标而不冒无反应行为的风险
我意外地注意到当工作线程执行时
GetMessage(out msg, new IntPtr(0), 0, 0);
从未收到任何消息,但线程仍处于活动状态,以达到所需的目的。
我还需要一种优雅的方式来关闭线程,但GetMessage永远不会返回
我不太明白所有这些信息,我只是想能够连接鼠标并保护它不被冻结
欢迎提供任何帮助。在钩子回调方法中,只需启动一个新线程。大概是这样的:
IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
ThreadPool.QueueUserWorkItem(obj =>
{
// do whatever ...
});
}
如果处理需要访问控件上的窗体,请不要忘记在主线程上调用
编辑:
如果你在你的主窗体线程上做了一些事情来冻结UI,你应该考虑在后台线程中做这个动作而不是主线程。当处理后需要更新控件时,可以调用
this.WhateverControl.Invoke(/*…/*)代码>低级鼠标钩子需要在名为SetWindowsHookEx的线程中运行消息循环。这就是为什么它不能在简单的后台线程中工作,而只能在UI线程中工作。若要在后台线程中使用此钩子,请在SetWindowsHookEx之后调用Application.Run方法。线程保留在此循环中,并处理低级钩子消息。我的程序中也有类似的问题。
创建键盘钩子后,钩子的回调被传递给UI线程。当UI线程忙时,回调在UI调度程序队列中,但若回调持续时间过长,windows将解除对您的挂钩。
在我尝试在该线程中运行单独的调度程序之前,我为该回调创建单独线程的尝试是无用的。
因此,我试图用以下代码解决我的问题:
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需要通过构造函数注入注入到我程序的其他对象中
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();
}
}