Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SendInput doesn't perform click mouse button unless I move cursor

SendInput doesn't perform click mouse button unless I move cursor.

I would appreciate a help on this one, as I seems cannot wrap my head around it.

I have a program that perform mouse click on foreground window, in which I am using SendInput to emulate left mouse click. The issue is, that if I move cursor to clicking position than SendInput will make the click, however if I don't move cursor than no click happens even trough I do pass x and y points to the MouseInputData. I would like to perform left mouse click without the need of actually moving the cursor at all.

Bellow is the class I have (it fairly simple and stright forward)

namespace StackSolution.Classes {     public static class SendInputClass     {          [DllImport("user32.dll", SetLastError = true)]         static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);          [DllImport("user32.dll")]         static extern bool SetCursorPos(int X, int Y);          [DllImport("user32.dll")]         [return: MarshalAs(UnmanagedType.Bool)]         static extern bool GetCursorPos(out Point lpPoint);            [StructLayout(LayoutKind.Sequential)]         struct INPUT         {             public SendInputEventType type;             public MouseKeybdhardwareInputUnion mkhi;         }         [StructLayout(LayoutKind.Explicit)]         struct MouseKeybdhardwareInputUnion         {             [FieldOffset(0)]             public MouseInputData mi;              [FieldOffset(0)]             public KEYBDINPUT ki;              [FieldOffset(0)]             public HARDWAREINPUT hi;         }         [StructLayout(LayoutKind.Sequential)]         struct KEYBDINPUT         {             public ushort wVk;             public ushort wScan;             public uint dwFlags;             public uint time;             public IntPtr dwExtraInfo;         }         [StructLayout(LayoutKind.Sequential)]         struct HARDWAREINPUT         {                      public int uMsg;             public short wParamL;             public short wParamH;         }         struct MouseInputData         {             public int dx;             public int dy;             public uint mouseData;             public MouseEventFlags dwFlags;             public uint time;             public IntPtr dwExtraInfo;         }         [Flags]         enum MouseEventFlags : uint         {             MOUSEEVENTF_MOVE = 0x0001,             MOUSEEVENTF_LEFTDOWN = 0x0002,             MOUSEEVENTF_LEFTUP = 0x0004,             MOUSEEVENTF_RIGHTDOWN = 0x0008,             MOUSEEVENTF_RIGHTUP = 0x0010,             MOUSEEVENTF_MIDDLEDOWN = 0x0020,             MOUSEEVENTF_MIDDLEUP = 0x0040,             MOUSEEVENTF_XDOWN = 0x0080,             MOUSEEVENTF_XUP = 0x0100,             MOUSEEVENTF_WHEEL = 0x0800,             MOUSEEVENTF_VIRTUALDESK = 0x4000,             MOUSEEVENTF_ABSOLUTE = 0x8000         }         enum SendInputEventType : int         {             InputMouse,             InputKeyboard,             InputHardware         }          public static void ClickLeftMouseButton(int x, int y)         {             INPUT mouseInput = new INPUT();             mouseInput.type = SendInputEventType.InputMouse;             mouseInput.mkhi.mi.dx = x;             mouseInput.mkhi.mi.dy = y;             mouseInput.mkhi.mi.mouseData = 0;              //getting current cursor location             Point p;             if (GetCursorPos(out p))                 SetCursorPos(x, y);               mouseInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_LEFTDOWN;             SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));              mouseInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_LEFTUP;             SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));              //returning cursor to previous position             SetCursorPos(p.X, p.Y);         }         }   } 

Same ClickLeftMouseButton function will not click if I remove getting cursor position like that.

   public static void ClickLeftMouseButton(int x, int y)         {             INPUT mouseInput = new INPUT();             mouseInput.type = SendInputEventType.InputMouse;             mouseInput.mkhi.mi.dx = x;             mouseInput.mkhi.mi.dy = y;             mouseInput.mkhi.mi.mouseData = 0;                                    mouseInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_LEFTDOWN;             SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));              mouseInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_LEFTUP;             SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));                     }    

Thank you in advance.

like image 952
Dmitris Avatar asked Nov 05 '11 17:11

Dmitris


1 Answers

There are a few things you should consider when using the SendInput function.

If you do not specify the MOUSEEVENTF_ABSOLUTE flag then dx and dy (MouseInputData structure) are relative coordinates to the current mouse position. If you do specify MOUSEEVENTF_ABSOLUTE then dx and dy are absolute coordinates between 0 and 65535. So if your x and y coordinates are screen coordinates you should use the following function to calculate dx and dy:

enum SystemMetric {   SM_CXSCREEN = 0,   SM_CYSCREEN = 1, }  [DllImport("user32.dll")] static extern int GetSystemMetrics(SystemMetric smIndex);  int CalculateAbsoluteCoordinateX(int x) {   return (x * 65536) / GetSystemMetrics(SystemMetric.SM_CXSCREEN); }  int CalculateAbsoluteCoordinateY(int y) {   return (y * 65536) / GetSystemMetrics(SystemMetric.SM_CYSCREEN); } 

Furthermore before you send the MOUSEDOWN and MOUSEUP events to via SendInput you have to move the mouse to the control you want to click on:

public static void ClickLeftMouseButton(int x, int y) {   INPUT mouseInput = new INPUT();   mouseInput.type = SendInputEventType.InputMouse;   mouseInput.mkhi.mi.dx = CalculateAbsoluteCoordinateX(x);   mouseInput.mkhi.mi.dy = CalculateAbsoluteCoordinateY(y);   mouseInput.mkhi.mi.mouseData = 0;                          mouseInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_MOVE |MouseEventFlags.MOUSEEVENTF_ABSOLUTE;   SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));    mouseInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_LEFTDOWN;   SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));    mouseInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_LEFTUP;   SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));             }  

The above code assumes that x and y are screen pixel coordinates. You can calculate those coordinates for a button (the target) on a winform by using the following code:

 Point screenCoordsCentre= button1.PointToScreen(new Point(button1.Width/2, button1.Height/2)); 

Hope, this helps.

like image 139
Hans Avatar answered Sep 18 '22 18:09

Hans