programmatically mouse click in another window
Asked Answered
Q

3

37

Is it possible to click programmatically a location in another window without moving the mouse to that location and even if the window is not on-top? I want to send a kind of message to another window to simulate a mouse click on a location.

I tried to accomplish this with PostMessage:

PostMessage(WindowHandle, 0x201, IntPtr.Zero, CreateLParam(300,300));
PostMessage(WindowHandle, 0x202, IntPtr.Zero, CreateLParam(300,300));

I made the CreateLParam function this way:

private static IntPtr CreateLParam(int LoWord, int HiWord)
{
     return (IntPtr)((HiWord << 16) | (LoWord & 0xffff));
}

The problem is that the window gets locked on his location. I think that my application clicks on the (1,1) coordinate. Can some on help me with this problem?

Edit: This is PostMessage:

[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll")]
public static extern bool PostMessage(IntPtr WindowHandle, int Msg, IntPtr wParam, IntPtr lParam);

And 0x201 and 0x202 are WM_LBUTTONDOWN and WM_LBUTTONUP respectively.

Quahog answered 27/4, 2012 at 17:35 Comment(3)
Is this another window you control? If not, this seems like a very odd request.Rooky
What program are you trying to click on? Some programs (mostly games) have mechanisms to handle what you are trying to do and ignore it. Your best chance in that case, is using WinApi to make the game top most, move mouse, click, move mouse back, move game back to previous z order.Yardman
Also, try casting HiWord to uint before doing << 16Yardman
P
38

You can't do that by sending messages, instead use SendInput Windows API.

Call method ClickOnPoint, this is an example from form click event, so this.handle is form handle, note that these are client coordinates on window witch handle is send, you can easily change this and send screen coordinates, and in that case you don't need handle or ClientToScreen call below.

ClickOnPoint(this.Handle, new Point(375, 340));

UPDATE: using SendInput now, tnx Tom.

btw. I used only declarations needed for this sample, for anything more there is a nice library : Windows Input Simulator (C# SendInput Wrapper - Simulate Keyboard and Mouse)

  public class ClickOnPointTool
  {

    [DllImport("user32.dll")]
    static extern bool ClientToScreen(IntPtr hWnd, ref Point lpPoint);

    [DllImport("user32.dll")]
    internal static extern uint SendInput(uint nInputs, [MarshalAs(UnmanagedType.LPArray), In] INPUT[] pInputs,  int cbSize);

#pragma warning disable 649
    internal struct INPUT
    {
      public UInt32 Type;
      public MOUSEKEYBDHARDWAREINPUT Data;
    }

    [StructLayout(LayoutKind.Explicit)]
    internal struct MOUSEKEYBDHARDWAREINPUT
    {
      [FieldOffset(0)]
      public MOUSEINPUT Mouse;
    }

    internal struct MOUSEINPUT
    {
      public Int32 X;
      public Int32 Y;
      public UInt32 MouseData;
      public UInt32 Flags;
      public UInt32 Time;
      public IntPtr ExtraInfo;
    }

#pragma warning restore 649


    public static void ClickOnPoint(IntPtr wndHandle , Point clientPoint)
    {
      var oldPos = Cursor.Position;

      /// get screen coordinates
      ClientToScreen(wndHandle, ref clientPoint);

      /// set cursor on coords, and press mouse
      Cursor.Position = new Point(clientPoint.X, clientPoint.Y);

      var inputMouseDown = new INPUT();
      inputMouseDown.Type = 0; /// input type mouse
      inputMouseDown.Data.Mouse.Flags = 0x0002; /// left button down

      var inputMouseUp = new INPUT();
      inputMouseUp.Type = 0; /// input type mouse
      inputMouseUp.Data.Mouse.Flags = 0x0004; /// left button up

      var inputs = new INPUT[] { inputMouseDown, inputMouseUp };
      SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT)));

      /// return mouse 
      Cursor.Position = oldPos;
    }

  }
Paratroops answered 27/4, 2012 at 18:26 Comment(10)
Thanks, this works great. I also added that the window becomes active when it is clickt.Quahog
What is a Point (what namespace)?Califate
System.Drawing (msdn.microsoft.com/en-us/library/system.drawing.point.aspx)Paratroops
Isn't there another way without SetCursorPos or without moving the mouse??? I see almost the same not-answers everywhere I search...Jealous
Note that you can use Cursor.Position instead of GetCursorPos and SetCursorPos. This also removes the need for the POINT struct.Boeotian
This won't work if the target window is not on top. Since it clicked on screen rather than the window. In the question, it was explicitly asked "and even if the window is not on-top"Orethaorferd
mouse_event is deprecated for a long time already, you should use SendInput now.Whack
SendInput does not work. Its too slow, and i find the window is no longer where it was when the desktop gets a click from the code. Not sure why MS thought this was a good change.Endosperm
@AntonioBakula You mention that we can't send mouse clicks to other forms using messages. Why is that?Dasheen
@Dasheen frankly I don't remember any more, sorry. It was 7 years ago and in last 5 or more years I switched to web apps so no more win api for me :) Best way is to ask another question here on SOParatroops
M
20

I found in the past, a way to send message to Windows Media Player so I used that to simulate click in application I wanted!

Using this class (code below) to find the window and to send messages you want!

using System;
using System.Runtime.InteropServices;

namespace Mouse_Click_Simulator
{
    /// <summary>
    /// Summary description for Win32.
    /// </summary>
    public class Win32
    {
        // The WM_COMMAND message is sent when the user selects a command item from 
        // a menu, when a control sends a notification message to its parent window, 
        // or when an accelerator keystroke is translated.
        public const int WM_KEYDOWN = 0x100;
        public const int WM_KEYUP = 0x101;
        public const int WM_COMMAND = 0x111;
        public const int WM_LBUTTONDOWN = 0x201;
        public const int WM_LBUTTONUP = 0x202;
        public const int WM_LBUTTONDBLCLK = 0x203;
        public const int WM_RBUTTONDOWN = 0x204;
        public const int WM_RBUTTONUP = 0x205;
        public const int WM_RBUTTONDBLCLK = 0x206;

        // The FindWindow function retrieves a handle to the top-level window whose
        // class name and window name match the specified strings.
        // This function does not search child windows.
        // This function does not perform a case-sensitive search.
        [DllImport("User32.dll")]
        public static extern int FindWindow(string strClassName, string strWindowName);

        // The FindWindowEx function retrieves a handle to a window whose class name 
        // and window name match the specified strings.
        // The function searches child windows, beginning with the one following the
        // specified child window.
        // This function does not perform a case-sensitive search.
        [DllImport("User32.dll")]
        public static extern int FindWindowEx(
            int hwndParent, 
            int hwndChildAfter, 
            string strClassName, 
            string strWindowName);


        // The SendMessage function sends the specified message to a window or windows. 
        // It calls the window procedure for the specified window and does not return
        // until the window procedure has processed the message. 
        [DllImport("User32.dll")]
        public static extern Int32 SendMessage(
            int hWnd,               // handle to destination window
            int Msg,                // message
            int wParam,             // first message parameter
            [MarshalAs(UnmanagedType.LPStr)] string lParam); // second message parameter

        [DllImport("User32.dll")]
        public static extern Int32 SendMessage(
            int hWnd,               // handle to destination window
            int Msg,                // message
            int wParam,             // first message parameter
            int lParam);            // second message parameter
    }
}

For Example:

 Win32.SendMessage(iHandle, Win32.WM_LBUTTONDOWN, 0x00000001, 0x1E5025B);

Here's My Application Source Code that I Created to auto click in "BlueStacks" Application in a specific interval!

For FindWindow, wParam, lParam, etc. you can feel free to ask me how to do it! it's not too hard :) ;) Hope it helped! :)

Mcfall answered 23/6, 2014 at 2:46 Comment(4)
As it seems, mouse clicks are posted, not sent. PostMessage would be a better solution. At least Spy++ claims for me that clicks are posted.Whack
Came here trying to send clicks to BlueStacks, was not disappointed!Moina
How to provide coords for the click? I need to click at some position, not just click at unknown one.Theft
@Kosmos use this: var w = (y << 16) | x; Win32.SendMessage(iHandle, Win32.WM_LBUTTONDOWN, 0x00000001, w);Pivot
B
4

I can't add a comment :D

Work for me:

[DllImport("User32.dll")]
    public static extern Int32 SendMessage(
    int hWnd,               
    int Msg,                
    int wParam,            
    IntPtr lParam);

and combine coord in lParam like this:

private static IntPtr CreateLParam(int LoWord, int HiWord)
    {
        return (IntPtr)((HiWord << 16) | (LoWord & 0xffff));
    }

and use:

Clicktoapp.SendMessage(0x00040156, Clicktoapp.WM_LBUTTONDOWN, 0x00000001, CreateLParam(150,150));
        Clicktoapp.SendMessage(0x00040156, Clicktoapp.WM_LBUTTONUP, 0x00000000, CreateLParam(150, 150));

0x00040156 - Window Handle.

I was looking for a window handle using spy ++ Each time it is new, so it's better to use FindWindow.

P.S

Screenshot of the application window even if the window is not on top

https://mcmap.net/q/327125/-get-a-screenshot-of-a-specific-application

i use this solution. work great.

Bogor answered 20/2, 2020 at 16:29 Comment(1)
What are "Clicktoapp.WM_LBUTTONDOWN" and CreateLParam(int LoWord, int HiWord) attributes?Geostatic

© 2022 - 2024 — McMap. All rights reserved.