使用WH_MOUSE_LL的SetWindowsHookEx会使鼠标速度减慢几秒钟

时间:2010-07-12 12:01:49

标签: c# winapi mouseevent

我使用以下代码在当前进程上获取鼠标消息。

using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
    return SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
}

出于某种原因,当此代码运行时,鼠标会慢慢运行几秒钟然后恢复正常。

任何想法?
感谢

编辑 - 挂钩方法

private static IntPtr mouseEvent(int nCode, IntPtr wParam, IntPtr lParam)
{
    if (nCode >= 0 && MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
    {
        MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));     
        LastLeftClick = new ClickInfo { Time = DateTime.Now, X = hookStruct.pt.x, Y = hookStruct.pt.y };
    }
    return CallNextHookEx(hookID, nCode, wParam, lParam);
}

public class ClickInfo
{
    public int X { get; set; }
    public int Y { get; set; }
    public DateTime Time { get; set; }
}

6 个答案:

答案 0 :(得分:2)

你的钩子程序是什么样的?

如果您的进程只有一个UI线程,请使用消息过滤器: http://msdn.microsoft.com/en-us/library/system.windows.forms.application.addmessagefilter.aspx

答案 1 :(得分:2)

我有同样的问题(只有它的c ++项目,而不是c#)并通过将WH_MOUSE_LL的挂钩更改为WH_MOUSE(从低级别到正常级别)来解决它。对于WM_LBUTTONUP和WM_RBUTTONUP消息,它可以正常工作。

让我感到高兴的是WH_MOUSE_LL的代码在我写它的时候表现不错(没有鼠标冻结等)似乎Windows的一些安全更新改变了鼠标钩子的行为,以前的精细代码变成了问题

答案 2 :(得分:2)

通过设置低级钩子响应性,鼠标现在变得依赖于主线程的响应,并且常见的错误是在启动过程中尽早设置钩子。

SetHook
LongRunningStartupProcess
鼠标直到此处才响应

在启动期间,最好将挂钩调度到线程上,以便在启动过程结束时发生。 Dispatcher.CurrentDispatcher.BeginInvoke(new Action(SetHook));

DispatchSetHook
LongRunningStartupProcess
SetHook(回调)
鼠标变得响应

在您的应用程序中仍然存在持续的管理问题,以确保主线程不执行任何长时间运行的进程,因为这也会锁定鼠标。这可以通过设置挂钩然后在主线程上执行Thread.Sleep来轻松验证。

答案 3 :(得分:1)

你的钩子过程很贵;你只需要弄清楚为什么以及如何解决它。

尽管代码看起来非常小,但我怀疑有一些初始的C#互操作费用会触发延迟,可能是由于JIT或分页。

如果您更改代码以尽可能多地处理此线程,则问题应该消失。作为一名C ++开发人员,我甚至担心Marshal.PtrToStructure,因为低级别的钩子非常敏感,我不能说这个操作保证是如此便宜以至于不会损害鼠标移动。

我过去曾经使用过低级别的鼠标钩子(在C ++中),并且除非钩子程序本身很昂贵,否则从未出现过问题。在C ++中,我尝试避免对执行剩余处理的HWND执行除PostMessage之外的任何操作。

答案 4 :(得分:1)

当你收到钩子事件时,关掉书,然后做你的工作,如果真的还需要,把钩子重新打开。

这将阻止鼠标滞后。

只有在你真正需要的时候才会保持迷茫。

答案 5 :(得分:0)

很抱歉,经过了这么长的时间,我仍在跟进,但是我通过产生一个单独的线程处理钩子解决了这个问题(我没有将所有内容添加到代码中,因为它还可以翻译消息,但是主要思想应该很清楚):

    public Form1()
    {
        InitializeComponent();

        Thread thread = new Thread(HookThread);
        thread.IsBackground = true;
        thread.Start();
    }

    private void HookThread()
    {
        _hookControl = new Control();
        IntPtr handle = _hookControl.Handle;

        _hookProc = new HookProc(HookFunction);
        using (Process curProcess = Process.GetCurrentProcess())
        using (ProcessModule curModule = curProcess.MainModule)
        {
            _hook = SetWindowsHookEx(HookType.WH_MOUSE_LL, _hookProc, GetModuleHandle(curModule.ModuleName), 0);// (uint)AppDomain.GetCurrentThreadId());
        }

        Application.Run();

        UnhookWindowsHookEx(_hook);
        _hook = IntPtr.Zero;
    }

    private IntPtr HookFunction(int code, IntPtr wParam, IntPtr lParam)
    {
        if (code < 0)
        {
            //you need to call CallNextHookEx without further processing
            //and return the value returned by CallNextHookEx
            return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
        }

        int msg = wParam.ToInt32();
        string messages = string.Join(", ", _messageMapping.Where(t => t.Item1 == msg).Select(t => t.Item2));
        if (string.IsNullOrWhiteSpace(messages))
            messages = msg.ToString();
        Trace.WriteLine($"Messages: { messages }");

        //return the value returned by CallNextHookEx
        return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
    }

您可以通过在创建的_hookControl上调用BeginInvoke来终止其他线程的线程:

        _hookControl.BeginInvoke(((Action)(() => Application.ExitThread())));