none
Juddering mouse with global mouse hook issue RRS feed

  • Question

  • I am running a UIA application using background MTA threads for the UIA processing and adding and removing uia event handlers(structural change, focus change). I am running the UIA processing on various events being fired including uia events and global events like window location change, active window changed and a mouse click using the global mouse hook below. On getting the results of the UIA processing I am drawing markers on the display in the main thread in the respective event handlers. With uia events and other global events like changing of active window and window location changed as examples everything works fine if not starting the mousehook. However on running the UIA processing and then drawing the markers on the display after a mouse click when running the mousehook I am getting jittery mouse movement when drawing my markers, sometimes the mouse locks before moving again.

    I noticed the hookcallback is called with every mouse movement as well as mouse clicks.  Is it possible to ignore the mouse movements (e.g. changing the msllhookstructure) as opposed to filtering mouse movements (which I have done in the code below)? Or any other suggestions as to how I can stop the mouse juddering when my main thread puts the markers on the screen after a mouse click?

    private void MouseHook_MouseAction(object sender, EventArgs e)
    {
       //do my stuff here in main thread
    }
    
    
    MouseHook.MouseAction += new EventHandler(MouseHook_MouseAction);
    MouseHook.Start();
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    using System.Threading;
    
    namespace MyApp
    {
        public static class MouseHook
        {
            [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            private static extern IntPtr SetWindowsHookEx(int idHook,
              LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);
    
            [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool UnhookWindowsHookEx(IntPtr hhk);
    
            [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
              IntPtr wParam, IntPtr lParam);
    
            [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            private static extern IntPtr GetModuleHandle(string lpModuleName);
    
            public static event EventHandler MouseAction = delegate { };
    
            private const int WH_MOUSE_LL = 14;
    
            private enum MouseMessages
            {
                WM_LBUTTONDOWN = 0x0201,
                //  WM_LBUTTONUP = 0x0202,
                //  WM_MOUSEMOVE = 0x0200,
                //  WM_MOUSEWHEEL = 0x020A,
                WM_RBUTTONDOWN = 0x0204,
                // WM_RBUTTONUP = 0x0205*/
            }
    
            [StructLayout(LayoutKind.Sequential)]
            private struct POINT
            {
                public int x;
                public int y;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            private struct MSLLHOOKSTRUCT
            {
                public POINT pt;
                public uint mouseData;
                public uint flags;
                public uint time;
                public IntPtr dwExtraInfo;
            }
    
            public static void Start()
            {
                _hookID = SetHook(_proc);
    
            }
            public static void stop()
            {
                UnhookWindowsHookEx(_hookID);
            }
    
            private static LowLevelMouseProc _proc = HookCallback;
            private static IntPtr _hookID = IntPtr.Zero;
    
            private static IntPtr SetHook(LowLevelMouseProc proc)
            {
                using (Process curProcess = Process.GetCurrentProcess())
                using (ProcessModule curModule = curProcess.MainModule)
                {
                    IntPtr hook = SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle("user32"), 0);
                    if (hook == IntPtr.Zero) throw new System.ComponentModel.Win32Exception();
                    return hook;
                }
            }
    
            private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
    
            private static IntPtr HookCallback(
              int nCode, IntPtr wParam, IntPtr lParam)
            {
                if (nCode >= 0 && (MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam || MouseMessages.WM_RBUTTONDOWN == (MouseMessages)wParam))
                {
                    
                    MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
                    MouseAction(null, new EventArgs());
                }
                return CallNextHookEx(_hookID, nCode, wParam, lParam);
            }
    
        }
    }










    • Edited by scocia888 Sunday, November 24, 2019 3:16 PM
    Sunday, November 24, 2019 2:07 PM

Answers

  • > How would I run the mouse hook handler or the mouse book in a background thread.

    You can't.  The hook causes a message to be sent to your process, and messages are always handled by the main thread.

    Hooks are a serious performance drain in general, but mouse hooks are a particular problem.  As you noticed, you will get called for EVERY piece of mouse motion.  Unless your hook runs very very quickly, it will cause degradation, and even a quick handler still has to pay the price of a context switch.


    Tim Roberts | Driver MVP Emeritus | Providenza & Boekelheide, Inc.

    • Marked as answer by scocia888 Wednesday, November 27, 2019 1:10 PM
    Tuesday, November 26, 2019 11:12 PM

All replies

  • Hi scocia888, 

    Thank you for posting here.

    According to your question, I note that you handle the event in ‘MouseHook_MouseAction’.

    Could you provide more information about ‘MouseHook_MouseAction’ and the type of your application?

    >> However on running the UIA processing and then drawing the markers on the display after a mouse click when running the mousehook I am getting jittery mouse movement when drawing my markers, sometimes the mouse locks before moving again.

    Could you provide a little sample that can help us to reproduce your problem? It will be beneficial for us to understand your problem.

    We are waiting for your update.

    Best Regards,

    Xingyu Zhao


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Monday, November 25, 2019 9:48 AM
    Moderator
  • The MouseHook_MouseAction event handler starts the uia processing on a background MTA thread , E.g. when you click on a window the window UIA elements are analysed.  Each of the UIelements found on a particular application user interface are held in an automation element array and the background MTA thread invokes a build of my application user interface in the main thread where it places markers next to various elements on the current application user interface using the details from the array produced in the background MTA thread.  Hope that helps.

    How would I run the mouse hook event handler or the mouse hook in a background thread. Thanks in advance.


    • Edited by scocia888 Tuesday, November 26, 2019 2:29 PM
    Tuesday, November 26, 2019 2:21 PM
  • > How would I run the mouse hook handler or the mouse book in a background thread.

    You can't.  The hook causes a message to be sent to your process, and messages are always handled by the main thread.

    Hooks are a serious performance drain in general, but mouse hooks are a particular problem.  As you noticed, you will get called for EVERY piece of mouse motion.  Unless your hook runs very very quickly, it will cause degradation, and even a quick handler still has to pay the price of a context switch.


    Tim Roberts | Driver MVP Emeritus | Providenza & Boekelheide, Inc.

    • Marked as answer by scocia888 Wednesday, November 27, 2019 1:10 PM
    Tuesday, November 26, 2019 11:12 PM
  • Thanks Tim for the confirmation regarding this, I will stop wasting my time :-) I appreciate your time. I will look for another way around the problem. I have been playing with stopping the hook immediately after the mouseclick is registered and then starting immediately after the UI is painted.  It avoids the context switch to an extent but not perfect.  Thanks again.

    • Edited by scocia888 Wednesday, November 27, 2019 1:16 PM
    Wednesday, November 27, 2019 1:13 PM
  • Have you considered trying to use Raw Input instead of a low-level mouse hook?

    Wednesday, November 27, 2019 7:21 PM
  • No just saw this, I'll have a look at the link, thank you.
    Monday, December 2, 2019 7:57 PM