Search code examples
c#winformswinapimouse-hook

Is it possible to capture coordinates of mouse clicked on another application e.g calculator and later force a click using code (C#)?


I am developing an application using Windows Form (C#). I need to be able to press a button which starts monitoring the location of the next mouse-click inside another application. The coordinates of this click then need to be used to force a click on the same button later in the same application. For testing I am just using a calculator and pressing a key to capture X and Y and then pressing another button to press that key again but it's not working. Any help to figure out what I am doing wrong will be much appreciated.

I have used MouseKeyHook to subscribe to the mouse hook

 nuget install MouseKeyHook
    public partial class Form1 : Form
    {
        [DllImport("user32.dll")]
        static extern bool GetCursorPos(out Point lpPoint);

        [DllImport("user32.dll")]
        static extern IntPtr WindowFromPoint(System.Drawing.Point p);

        [DllImport("user32.dll")]
        static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);

        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
        [StructLayout(LayoutKind.Sequential)]
        [DllImport("user32.dll")]
        public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);

        const int WM_LBUTTONDOWN = 0x0201;

        private struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

//Button click which starts the subscribe
        private void btnStart_Click(object sender, EventArgs e)
        {
            Subscribe();

        }

        private IKeyboardMouseEvents m_GlobalHook;
public void Subscribe()
        {
            // Note: for the application hook, use the Hook.AppEvents() instead
            m_GlobalHook = Hook.GlobalEvents();

            m_GlobalHook.MouseDownExt += GlobalHookMouseDownExt;
        }

        Point p;
        IntPtr hWnd;
        private void GlobalHookMouseDownExt(object sender, MouseEventExtArgs e)
        {

            if (GetCursorPos(out p))
            {
               IntPtr mainWindow = WindowFromPoint(p);
                hWnd = ChildWindowFromPointEx(mainWindow, p, 0x0000);

                RECT rct = new RECT();
                GetWindowRect(mainWindow, ref rct);
                X = p.X - rct.Left;
                Y = p.Y - rct.Top;
            }

            Unsubscribe();
        }

        public void Unsubscribe()
        {
            m_GlobalHook.MouseDownExt -= GlobalHookMouseDownExt;
            m_GlobalHook.Dispose();
        }


        
        private void btnClick_Click(object sender, EventArgs e)
        {
            PostMessage(hWnd, WM_LBUTTONDOWN,X, Y);
            
        }
   }
```.
 

Solution

  • You cannot guarantee that the other application(calculator) responds to message you've posted.

    In addition, the calculator is a UWP window, you will get an empty hWnd with ChildWindowFromPointEx. You should use SendInput to simulate mouse input and ensure that the clicked place is not covered by other windows. Sample in C#: https://stackoverflow.com/a/54072028/10611792

    Or use UI Automation.