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