如何模拟鼠标移动

20

如何使用C++模拟鼠标事件,使指针向左移动500像素,然后点击。我应该如何实现这样的功能?


1
哪个操作系统?Windows?OS X?Linux?它们都会有很大的不同。 - Greg Hewgill
1
它被标记为Windows。 - Noah.Ehrnstrom
6个回答

46

我这里有一些修改后的Win32代码:

#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0500

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <string.h>
#include <windows.h>


#define X 123
#define Y 123
#define SCREEN_WIDTH 1024
#define SCREEN_HEIGHT 800


void MouseSetup(INPUT *buffer)
{
    buffer->type = INPUT_MOUSE;
    buffer->mi.dx = (0 * (0xFFFF / SCREEN_WIDTH));
    buffer->mi.dy = (0 * (0xFFFF / SCREEN_HEIGHT));
    buffer->mi.mouseData = 0;
    buffer->mi.dwFlags = MOUSEEVENTF_ABSOLUTE;
    buffer->mi.time = 0;
    buffer->mi.dwExtraInfo = 0;
}


void MouseMoveAbsolute(INPUT *buffer, int x, int y)
{
    buffer->mi.dx = (x * (0xFFFF / SCREEN_WIDTH));
    buffer->mi.dy = (y * (0xFFFF / SCREEN_HEIGHT));
    buffer->mi.dwFlags = (MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE);

    SendInput(1, buffer, sizeof(INPUT));
}


void MouseClick(INPUT *buffer)
{
    buffer->mi.dwFlags = (MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN);
    SendInput(1, buffer, sizeof(INPUT));

    Sleep(10);

    buffer->mi.dwFlags = (MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTUP);
    SendInput(1, buffer, sizeof(INPUT));
}


int main(int argc, char *argv[])
{
    INPUT buffer[1];

    MouseSetup(&buffer);

    MouseMoveAbsolute(&buffer, X, Y);
    MouseClick(&buffer);

    return 0;
}
在使用每个INPUT缓冲区之前,您需要调用MouseSetup()

资源

MSDN - SendInput()
MSDN - INPUT
MSDN - MOUSEINPUT


1
最好使用 x * 0xFFFF / SCREEN_WIDTH + 1 进行像素完美的坐标归一化。(对于所有 SCREEN_WIDTH < (0xFFFF / 2) 和 x <= SCREEN_WIDTH 的值)。你的公式在常见屏幕分辨率下可能会有数十个像素的偏差。 - Kay Sarraute
这是必需的 - 否则您将会有从左到右和从上到下的光标定位精度恶化。 - Ani
每个INPUT中的MouseSetup()表示每个类型为INPUT变量... 我很困惑 - 乍一看我以为你必须在每个鼠标输入之前调用它(这没有意义...)。 - jave.web
如何检索SCREEN_WIDTH和SCREEN_HEIGHT以使MouseMoveAbsolute()显示大小(分辨率)独立? - Michał Ziobro
1
"(x * (0xFFFF / SCREEN_WIDTH));" 的结果非常大,这是正常的吗? - Michael IV
在我的情况下,x * 65536 / SCREEN_WIDTH + 1 的效果更好,这也被用于AutoHotKey - J3soon

27

这里是一个使用Xlib的解决方案,适用于使用Linux的人:

#include <X11/Xlib.h>
#include<stdio.h>
#include<unistd.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

void mouseClick(int button)
{
    Display *display = XOpenDisplay(NULL);

    XEvent event;

    if(display == NULL)
    {
        fprintf(stderr, "Errore nell'apertura del Display !!!\n");
        exit(EXIT_FAILURE);
    }

    memset(&event, 0x00, sizeof(event));

    event.type = ButtonPress;
    event.xbutton.button = button;
    event.xbutton.same_screen = True;

    XQueryPointer(display, RootWindow(display, DefaultScreen(display)), &event.xbutton.root, &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root, &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);

    event.xbutton.subwindow = event.xbutton.window;

    while(event.xbutton.subwindow)
    {
        event.xbutton.window = event.xbutton.subwindow;

        XQueryPointer(display, event.xbutton.window, &event.xbutton.root, &event.xbutton.subwindow, &event.xbutton.x_root, &event.xbutton.y_root, &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);
    }

    if(XSendEvent(display, PointerWindow, True, 0xfff, &event) == 0) fprintf(stderr, "Error\n");

    XFlush(display);

    usleep(100000);

    event.type = ButtonRelease;
    event.xbutton.state = 0x100;

    if(XSendEvent(display, PointerWindow, True, 0xfff, &event) == 0) fprintf(stderr, "Error\n");

    XFlush(display);

    XCloseDisplay(display);
}


int main(int argc, char * argv[]) {

    int x , y;
    x = atoi(argv[1]);
    y = atoi(argv[2]);
    Display *display = XOpenDisplay(0);

    Window root = DefaultRootWindow(display);
    XWarpPointer(display, None, root, 0, 0, 0, 0, x, y);
    mouseClick(Button1);
    XFlush(display);
    XCloseDisplay(display);
    return 0;
}

只需构建它,然后模拟在x,y处点击:

$ ./a.out x y
即,也就是。
$g++ -lX11 sgmousesim2.cpp

$./a.out 123 13

2
这对于X11来说是相当正确的,但问题已经澄清为要求Windows解决方案。 - Flexo
8
刚刚注意到这个 :) 或许它会帮助那些在寻找 Linux 特定解决方案的人。 - axiom
我仍然感激你的帮助。 - Steven Walton

3
使用SendInput生成您想要模拟的输入。根据MSDN文档:

合成击键、鼠标移动和按钮点击。


1

我从未使用过C++来完成这个任务。不过,Java有一个叫做Robot的类可以产生鼠标事件。我曾在Java 1.4版本中使用过它,但它仍然有效。我尝试了模拟Mac OS X中的物理鼠标移动的示例。它在MacOSX Lion上的Oracle Java 1.6.0_26上运行得很顺利。Java的好处是它是跨平台的。

import java.awt.AWTException;
import java.awt.Robot;

public final class MovingMouseDemo
{
   public static void main(String[] args) throws AWTException
   {
     Robot robot = new Robot();
     robot.setAutoDelay(5);
     robot.setAutoWaitForIdle(true);

     //put mouse in the top left of the screen
     robot.mouseMove(0, 0);
     //wait so that you can see the result
     robot.delay(1000);
     //put the mouse 200 pixels away from the top
     //10 pixels away from the left 
     robot.mouseMove(200, 10);
     robot.delay(1000);
     robot.mouseMove(40, 130);
  }
}

你仍然可以使用JNI将其与C++绑定。

希望能有所帮助。


0

C++单独无法完成此任务。它没有“鼠标”概念,更不用说“点击”了。

您需要一些与窗口系统通信的库,例如QT。然后就是搜索API并进行正确的C++调用。


0

2
如果您阅读文档,它会说:“*注意 此函数已被取代。请改用SendInput。” - Mateen Ulhaq
1
它在Win2k和WinXP上都能正常工作。我已经检查过了。我不知道W7的支持情况,但我认为mouse_event也会起作用,因为有许多遗留代码在使用它。此外,这个函数的语法比SendInput的语法更明显。 - George Gaál
无论某些东西是否有效并不重要。很多东西都能正常工作,可能只是在测试时或在一台机器上。重要的是,供应商告诉你不要使用它,而你却忽略了他们的建议。你将无法通过该供应商的认证平台。如果你不需要认证,那么你很可能会因为销售有问题的产品而惹怒客户。这个已经被弃用了,有其原因。不要使用它。也许有1/10000的几率会导致伤害甚至死亡,你不知道,所以不要使用它。 - Dan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接