LowLevelMouseProc在后台线程中

时间:2010-10-02 23:01:23

标签: 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);

IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam){/**/}

如果我把它放在主窗口上,那么一切都有效,直到窗口必须做更复杂的工作,导致鼠标停止响应该工作的持续时间(例如,在面板中更新多个子节点)。

如果我启动一个新的Thread并从那里设置了钩子,问题是线程在设置钩子后简单地退出并且永远不会调用回调函数。

有没有办法让线程保持活着? 或者,如果存在另一种方法来连接鼠标而不冒无反应行为的风险?

我意外地注意到当工作线程执行时

GetMessage(out msg, new IntPtr(0), 0, 0);

没有收到任何消息但是线程正在保持活动以达到所需的目的。 此外,我需要一种优雅的方式来关闭线程,但GetMessage永远不会返回。

我不太了解所有这些消息,我只是希望能够连接鼠标并防止它冻结。

任何帮助都是适当的。

3 个答案:

答案 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注入到我的程序的其他对象中。