SendInput не выполняет щелчок мышью, пока я не переместлю курсор - PullRequest
23 голосов
/ 05 ноября 2011

SendInput не выполняет щелчок мышью, если я не перемещаю курсор.

Буду признателен за помощь в этом, так как, кажется, не могу обернуться вокруг него.

У меня есть программа, которая выполняет щелчок мышью в окне переднего плана, в котором я использую SendInput для эмуляции щелчка левой кнопкой мыши. Проблема в том, что если я переместлю курсор в позицию щелчка, то SendInput выполнит щелчок, однако, если я не переместлю курсор, то никакого щелчка не произойдет, даже если я передам x и y указателю MouseInputData. Я хотел бы выполнить щелчок левой кнопкой мыши без необходимости вообще перемещать курсор.

Пыльник - мой класс (довольно простой и прямой)

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);
        }    
    }
  }

Та же функция ClickLeftMouseButton не будет щелкать, если я уберу полученную позицию курсора таким образом.

   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()));            
        }   

Заранее спасибо.

1 Ответ

28 голосов
/ 05 ноября 2011

Есть несколько вещей, которые вы должны учитывать при использовании функции SendInput.

Если вы не укажете флаг MOUSEEVENTF_ABSOLUTE, то dx и dy (структура MouseInputData) будут относительными координатами текущей позиции мыши. Если вы укажете MOUSEEVENTF_ABSOLUTE, то dx и dy будут абсолютными координатами от 0 до 65535. Поэтому, если ваши координаты x и y являются экранными координатами, вы должны использовать следующую функцию для вычисления dx и 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);
}

Кроме того, прежде чем отправлять события MOUSEDOWN и MOUSEUP через SendInput, необходимо переместить мышь на элемент управления, на который вы хотите нажать:

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()));            
} 

Приведенный выше код предполагает, что x и y являются координатами пикселя экрана. Вы можете вычислить эти координаты для кнопки (цели) на форме winin, используя следующий код:

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

Надеюсь, это поможет.

...