3

I'm trying to send a right mouse click to a window specified coordinates.

I've tested with 2 codes

Code 1:

[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImport("user32.dll")]        
static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint);

public struct POINT
{
    public int x;
    public int y;
}

var client = Process.GetProcessesByName("client_dx");
var whandle = client.MainWindowHandle;

POINT point = new POINT();
point.x = 1836;
point.y = 325;
ScreenToClient(whandle, ref point);
int lparm = (point.x << 16) + point.y;    
int lngResult = SendMessage(whandle, 0x0204, 0, lparm);
int lngResult2 = SendMessage(whandle, 0x0205, 0, lparm);

Code 2:

[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImport("user32.dll")]        
static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint);

public struct POINT
{
    public int x;
    public int y;
}

public int MakeLParam(int LoWord, int HiWord)
{
    return (int)((HiWord << 16) | (LoWord & 0xFFFF));
}

var client = Process.GetProcessesByName("client_dx");
var whandle = client.MainWindowHandle;

POINT point = new POINT();
point.x = 1836;
point.y = 325;

ScreenToClient(whandle, ref point);

int lparm = MakeLParam(point.x, point.y);
int lngResult = SendMessage(whandle, 0x0204, 0, lparm);
int lngResult2 = SendMessage(whandle, 0x0205, 0, lparm);

It's sending a right click but not to the correct coordinates, seems like it ignores the coordinates I specify in LPARAM, because if I move the mouse around the window, it's clicking everywhere I put the mouse pointer but not in the coordinates that I specify.

I've tested changing this line in code 2:

int lparm = MakeLParam(point.x, point.y);

To this one:

int lparm = (point.x << 16) + point.y;

But doesn't work, I'm getting the same results...

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
kuhi
  • 531
  • 5
  • 23
  • Did you test/check the coordinates returned (by ref) to the point structure by ScreenToClient() ? – VillageTech Dec 04 '19 at 16:08
  • 1
    As @VillageTech is eluding to, the coordinates used are relative to the client window you are addressing, not the entire desktop. In the docs for WM_RBUTTONDOWN(/UP), it says "The coordinate is relative to the upper-left corner of the client area." – Señor CMasMas Dec 04 '19 at 16:12
  • As you can see I'm "translating" the desktop coordinates to client coordinates.. ScreenToClient(whandle, ref point); But anyways if that was the case it doesn't explain why right click area is changing as I move my mouse, it's ignoring my coordinates – kuhi Dec 04 '19 at 18:03
  • What VillageTech was referring to is you never check the bool returned by `ScreenToClient`, to see if the call was a success or failure. – Scott Chamberlain Feb 03 '20 at 00:44
  • Use `mouse_event` – Reza Aghaei Feb 03 '20 at 04:09
  • Or if you want to use `SendMessage`, translate the window screen position to its relative position. – Reza Aghaei Feb 03 '20 at 05:34
  • I've checked the ScreenToClient it's working with Debug.WriteLine and it's working.. @RezaAghaei if you take a look at the code, I'm using ScreenToClient to get the window relative coordinates – kuhi Feb 03 '20 at 09:18
  • @kuhi Take a look at working examples in the answer below :) – Reza Aghaei Feb 03 '20 at 09:19
  • At least in *Code 1* you have used wrong parameters to get `LParam`. – Reza Aghaei Feb 03 '20 at 09:20

1 Answers1

7

You can use SendMessage or mouse_event or SendInputto perform a mouse operation. Here I will share some details and examples about the first two.

Using SendMessage

  • SendMessage performs the mouse action without needing to move the cursor.
  • SendMessage needs handle of the window to send message.
  • SendMessage sends the mouse message to a relative position inside the window. The coordinate should be relative to the upper-left corner of the client area of the window.
  • If you know which point in the client coordinate, you want to send click, then just use the client coordinates. It's usually the case.
  • If you have a screen position and you want to translate it to client relative position, you can use ScreenToClient. But since you usually know the relative position you are going to click, you usually don't need ScreenToClient.
  • To pass parameters to MakeLParam the low-order word specifies the x-coordinate and high-order word specifies the y-coordinate of the cursor. To make it less confusing, use the following function:

    IntPtr MakeLParam(int x, int y) => (IntPtr)((y << 16) | (x & 0xFFFF));
    
  • And as a side-note, if you want to the send the message to a window and return without waiting for the thread to process the message, you can use PostMessage.

Using mouse_event

  • mouse_event Performs the mouse action on the current cursor position.
  • Before calling mouse_event you need to move the cursor to the location on screen which you are going to perform click.
  • To get screen position of a client point of a window, you can use ClientToScreen method.
  • To move the cursor you can set Cursor.Position to a screen position.
  • It's recommended to use SendInput function instead.

Example 1 - SendMessage

Using SendMessage you can click on the specified relative position of a specified window. In the following example, I've found the edit control inside notepad main window, then I've performed a right click on (20,20) coordinate inside the client rectangle of the edit control:

//using System;
//using System.Diagnostics;
//using System.Drawing;
//using System.Linq;
//using System.Runtime.InteropServices;
//using System.Windows.Forms;
const int WM_RBUTTONDOWN = 0x0204;
const int WM_RBUTTONUP = 0x0205;
const int WM_MOUSEMOVE = 0x0200;
[DllImport("User32.DLL")]
static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

IntPtr MakeLParam(int x, int y) => (IntPtr)((y << 16) | (x & 0xFFFF));

[DllImport("user32.dll")]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,
    string lpszClass, string lpszWindow);

void PerformRightClick(IntPtr hwnd, Point point)
{
    var pointPtr = MakeLParam(point.X, point.Y);
    SendMessage(hwnd, WM_MOUSEMOVE, IntPtr.Zero, pointPtr);
    SendMessage(hwnd, WM_RBUTTONDOWN, IntPtr.Zero, pointPtr);
    SendMessage(hwnd, WM_RBUTTONUP, IntPtr.Zero, pointPtr);
}
void button1_Click(object sender, EventArgs e)
{
    var notepad = Process.GetProcessesByName("notepad").FirstOrDefault();
    if (notepad != null)
    {
        var edit = FindWindowEx(notepad.MainWindowHandle, IntPtr.Zero, "Edit", null);
        PerformRightClick(edit, new Point(20, 20));
    }
}

Example 2 - mouse_event

Using mouse_event you can click on the current mouse position. It means you need to move the mouse to the desired location. In the following example, I've found the edit control inside notepad main window, then I've performed a right click on (20,20) coordinate inside the client rectangle of the edit control:

//using System;
//using System.Diagnostics;
//using System.Drawing;
//using System.Linq;
//using System.Runtime.InteropServices;
//using System.Windows.Forms;
const int MOUSEEVENTF_RIGHTDOWN = 0x0008;
const int MOUSEEVENTF_RIGHTUP = 0x0010;
[DllImport("user32.dll")]
static extern void mouse_event(uint dwFlags, uint dx, uint dy,
    uint cButtons, uint dwExtraInfo);

[DllImport("user32.dll")]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,
    string lpszClass, string lpszWindow);

[StructLayout(LayoutKind.Sequential)]
struct POINT { public int X; public int Y; }

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

void PerformRightClick(IntPtr hwnd, Point p)
{
    POINT point = new POINT() { X = p.X, Y = p.Y };
    ClientToScreen(hwnd, ref point);
    Cursor.Position = new Point(point.X, point.Y);
    uint X = (uint)Cursor.Position.X;
    uint Y = (uint)Cursor.Position.Y;
    mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, X, Y, 0, 0);
}
void button1_Click(object sender, EventArgs e)
{
    var notepad = Process.GetProcessesByName("notepad").FirstOrDefault();
    if (notepad != null)
    {
        var edit = FindWindowEx(notepad.MainWindowHandle, IntPtr.Zero, "Edit", null);
        PerformRightClick(edit, new Point(20, 20));
    }
}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398