Itay Karo
Itay Karo

Reputation: 18286

SetWindowsHookEx with WH_MOUSE_LL slows down the mouse for several seconds

I am using the following code to get mouse messages on the current process.

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

For some reason when this code runs the mouse get slow for several seconds and then back to normal.

Any ideas?
Thanks

EDIT - hook method

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

Upvotes: 6

Views: 12525

Answers (6)

Romout
Romout

Reputation: 308

I am sorry to follow up after such a long time but I have solved the issue by spawning a separate thread which handles the hook (I have not added everything to the code as it also translates messages but the main idea should be clear):

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

You can terminate the thread from any other thread by calling BeginInvoke on the created _hookControl:

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

Upvotes: 4

David Ewen
David Ewen

Reputation: 3732

By setting a low level hook responsiveness of the mouse now becomes dependent on your main thread being responsive and a common mistake made is to set the hook early in the start-up process.

SetHook
LongRunningStartupProcess
Mouse isn’t responsive until here

During start-up it is best to dispatch the hook onto the thread so it happens at the end of the start-up process. Dispatcher.CurrentDispatcher.BeginInvoke(new Action(SetHook));

DispatchSetHook
LongRunningStartupProcess
SetHook(callback)
Mouse becomes responsive

This still has ongoing management issues within your application to ensure the main thread doesn’t do any long running processes as that will lock the mouse up too. This can easily be verified by setting the hook and then doing a Thread.Sleep on the main thread.

Upvotes: 2

Jewel S
Jewel S

Reputation:

Your hook proc is expensive; you just need to figure out why and how to fix it.

Even though though the code looks very minimal i suspect that there is some initial C# interop expense triggering the delays, perhaps due to JIT or paging.

If you change the code to do as much processing as possible off of this thread the problem should go away. as a C++ developer I even worry about the Marshal.PtrToStructure since low-level hooks are very sensitive and I can't say off the top of my head that this operation is guaranteed to be so cheap that it wouldn't impair mouse movement.

I've used low-level mouse hooks quite a bit in the past (in C++) and have never had problems unless the hook procedure itself is expensive. In C++ I try to avoid doing anything more than a PostMessage to an HWND that does the rest of the processing.

Upvotes: 1

Martin Blore
Martin Blore

Reputation: 11

When you receive the hook event, turn off the book, then do your work, and if really still needed, put the hook back on.

This will stop the mouse from lagging.

Only stay hooked when you really need to.

Upvotes: 1

Bobrovsky
Bobrovsky

Reputation: 14246

I had the same problem (only it's c++ project, not c#) and resolved it by changing hook from WH_MOUSE_LL to WH_MOUSE (from low-level to normal level). For WM_LBUTTONUP and WM_RBUTTONUP messages it works ok.

The thing that amuses me is that code with WH_MOUSE_LL was doing fine at the time I wrote it (no mouse freezes etc.) It seems like some security update for Windows changed the behavior of the mouse hooks and previously fine code become a problem.

Upvotes: 4

Tergiver
Tergiver

Reputation: 14507

What does your hook procedure look like?

If your process only has one UI thread, use a Message Filter instead: http://msdn.microsoft.com/en-us/library/system.windows.forms.application.addmessagefilter.aspx

Upvotes: 3

Related Questions