12.3 实现模拟鼠标录制回放

实现,模拟,鼠标,录制,回放 · 浏览次数 : 76

小编点评

**内容简介** 本文内容介绍如何使用编程记录和回放鼠标功能,并展示如何在代码中实现鼠标动作的回放。 **代码** 以下代码展示了如何使用编程记录和回放鼠标功能: ```c #include #include #include // 定义鼠标事件类型 #define MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP #define MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP // 定义鼠标事件数据 #define KEY_ITEM 4 // 定义注册热键函数 int RegisterHotKey(int hotKeyId, int hotKeyModifier, int keyId, int hotKeyExtraInfo) { return RegisterHotKey(NULL, hotKeyModifier, keyId, hotKeyExtraInfo); } // 定义鼠标移动函数 void mouse_event(DWORD dwFlags, DWORD dx, DWORD dy, DWORD dwData, ULONG_PTR dwExtraInfo) { switch (dwFlags) { case MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP: dx -= 10; break; case MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP: dx += 10; break; case MOUSEEVENTF_LEFTUP: dy -= 10; break; case MOUSEEVENTF_RIGHTUP: dy += 10; break; } // 设置鼠标位置 SetCursorPos(dx, dy); } // 定义鼠标录制函数 void Recording(char *filePath) { // 创建文件句柄 HANDLE fileHandle = CreateFile(filePath, NULL, 0); // 写文件 WriteFile(fileHandle, 100, filePath, NULL); // 关闭文件句柄 CloseHandle(fileHandle); } // 定义回放函数 void Play(char *filePath) { // 创建文件句柄 HANDLE fileHandle = CreateFile(filePath, NULL, 0); // 写文件 WriteFile(fileHandle, 100, filePath, NULL); // 关闭文件句柄 CloseHandle(fileHandle); } // 注册热键 int main() { // 注册 F1 热键 RegisterHotKey(NULL, 1, VK_F1, 0); // 注册 F2 热键 RegisterHotKey(NULL, 2, VK_F2, 0); // 注册 F3 热键 RegisterHotKey(NULL, 3, VK_F3, 0); // 注册鼠标移动事件 RegisterHotKey(NULL, MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, VK_F1, 0); // 注册鼠标移动事件 RegisterHotKey(NULL, MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, VK_F2, 0); // 启动消息循环 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { switch (msg.message) { case WM_HOTKEY: { if (1 == msg.wParam) { std::cout << "录制脚本" << std::endl; Recording((char *)\"d://script.txt\"); } else if (2 == msg.wParam) { std::cout << "回放脚本" << std::endl; Play((char *)\"d://script.txt\"); } break; } default: break; } } return 0; } ``` **效果图** ``` 录制脚本 回放脚本 ```

正文

本节将向读者介绍如何使用键盘鼠标操控模拟技术,键盘鼠标操控模拟技术是一种非常实用的技术,可以自动化执行一些重复性的任务,提高工作效率,在Windows系统下,通过使用各种键盘鼠标控制函数实现动态捕捉和模拟特定功能的操作。

有时我们经常需要进行重复性的鼠标操作,例如繁琐的点击、拖拽。这些任务可能消耗大量时间和精力,为了解决这个问题,可自行设计并实现一个简单而强大的鼠标录制回放工具,旨在帮助用户轻松录制鼠标动作,通过借助鼠标录制回放工具,用户可以轻松实现自动化操作,从而解放双手。

首先我们需要创建一个Write_File函数,当用户每次调用该函数时都会向特定的文件内追加写入一条记录,此外还需要增加一个split函数,该函数用于将特定的一条记录根据特定的分隔符切割,保留分隔符后面的坐标信息。

// 切割字符串
int split(char dst[][32], char* str, const char* spl)
{
    int n = 0;
    char* result = NULL;
    result = strtok(str, spl);
    while (result != NULL)
    {
        strcpy(dst[n++], result);
        result = strtok(NULL, spl);
    }
    return n;
}

// 每次写入一行
int Write_File(char* path, char* msg)
{
    FILE* fp = fopen(path, "a+");
    if (fp == NULL) return -1;

    char ch, buffer[1024];

    int index = 0;
    while (msg[index] != '\0')
    {
        fputc(msg[index], fp);
        index++;
    }
    fclose(fp);
    return 1;
}

接着我们需要实现Recording()函数部分,该函数的左右是用于捕捉当前鼠标坐标与点击事件,函数中通过调用GetCursorPos()获取当前鼠标的屏幕坐标位置,这个函数参数传递非常简单,只需要传入一个POINT类型的结构体变量,其函数原型如下所示;

BOOL GetCursorPos(LPPOINT lpPoint);

参数:

  • lpPoint:指向 POINT 结构的指针,用于接收鼠标的屏幕坐标位置。

返回值:

  • 如果函数成功,返回值为非零,表示获取鼠标位置成功;
  • 如果函数失败,返回值为零,表示获取鼠标位置失败。

POINT 结构包含了两个成员变量 xy,分别表示鼠标在屏幕上的横坐标和纵坐标。

当有了当前鼠标坐标位置以后,接着就是需要获取到鼠标点击事件,鼠标点击可使用GetAsyncKeyState 获取指定虚拟键码对应的键盘键的状态,该函数原型如下所示;

SHORT GetAsyncKeyState(int vKey);

参数:

  • vKey:指定虚拟键码,它是一个整数,表示要获取的键的键码。

返回值:

  • 如果指定的虚拟键处于按下状态,返回值的最高位(符号位)为 1,其余位表示次数(持续时间)。如果指定的虚拟键处于释放状态或者参数无效,返回值为 0。

GetAsyncKeyState 函数允许检测键盘中某个虚拟键的状态,无论这个虚拟键是否处于焦点的窗口中。它适用于各种应用,通过VK_LBUTTON可用于检测鼠标左键是否被按下,通过VK_RBUTTON则可用于检测鼠标右键状态。

代码的主要功能如下:

  1. Recording 函数中,使用一个死循环不断检测鼠标的位置和按键状态。
  2. 使用 GetCursorPos 函数获取当前鼠标的位置,并将其保存在 xy 变量中。
  3. 使用 GetAsyncKeyState 函数检测鼠标左键和右键的状态,并将其保存在 lbuttonrbutton 变量中。
  4. 如果当前的鼠标位置或按键状态与之前保存的值不同,表示鼠标动作发生了变化,将当前的位置和按键状态记录下来。
  5. 将记录的鼠标动作信息以字符串的形式写入脚本文件,格式为 "X:位置,Y:位置,L:左键状态,R:右键状态"。
  6. 保存当前的鼠标位置和按键状态,用于下一次循环时比较是否发生了变化。
// 录制脚本
void Recording(char *script)
{
    int static_x = 0, static_y = 0;
    bool static_lbutton = 0, static_rbutton = 0;

    while (1)
    {
        POINT Position;
        GetCursorPos(&Position);

        int x = Position.x;
        int y = Position.y;
        bool lbutton = GetAsyncKeyState(VK_LBUTTON);
        bool rbutton = GetAsyncKeyState(VK_RBUTTON);

        if (x != static_x || y != static_y || lbutton != static_lbutton || rbutton != static_rbutton)
        {
            char szBuf[1024] = { 0 };
            std::cout << "X轴 = " << x << " Y轴 = " << y << " 鼠标左键 = " << lbutton << " 鼠标右键 = " << rbutton << std::endl;

            sprintf(szBuf, "X:%d,Y:%d,L:%d,R:%d\n", x, y, lbutton, rbutton);
            Write_File((char*)script, szBuf);

            static_x = x;
            static_y = y;
            static_lbutton = lbutton;
            static_rbutton = rbutton;
        }
    }
}

接着我们继续封装Play()回放功能,该功能的实现原理与录制保持一致,通过逐条读取传入文件中的参数,并调用SetCursorPos实现鼠标位置的移动操作,该函数与获取参数传递保持一致,这里我们需要注意mouse_event函数,该函数用于模拟鼠标的各种事件,如鼠标移动、鼠标按键的点击和释放等,其函数原型如下所示;

void mouse_event(DWORD dwFlags, DWORD dx, DWORD dy, DWORD dwData, ULONG_PTR dwExtraInfo);

其中dwFlags指定要模拟的鼠标事件类型和选项。可以是以下常量的组合;

  • MOUSEEVENTF_ABSOLUTE:指定鼠标位置是绝对坐标。如果不设置此标志,则坐标是相对于当前鼠标位置的增量。
  • MOUSEEVENTF_MOVE:模拟鼠标移动事件。
  • MOUSEEVENTF_LEFTDOWN:模拟鼠标左键按下事件。
  • MOUSEEVENTF_LEFTUP:模拟鼠标左键释放事件。
  • MOUSEEVENTF_RIGHTDOWN:模拟鼠标右键按下事件。
  • MOUSEEVENTF_RIGHTUP:模拟鼠标右键释放事件。

其他常量可根据需要自行查阅相关文档。

  • dx:鼠标事件发生时的横坐标(绝对坐标或增量坐标,根据 dwFlags 决定)。
  • dy:鼠标事件发生时的纵坐标(绝对坐标或增量坐标,根据 dwFlags 决定)。
  • dwData:鼠标事件的一些数据。对于滚轮事件,它表示滚动的数量。对于其他事件,通常设为 0。
  • dwExtraInfo:额外的信息。通常设为 0。

mouse_event 函数允许模拟鼠标事件,通过设置 dwFlags 参数来指定需要模拟的事件类型,设置 dxdy 参数来指定事件发生时的鼠标位置。通过调用这个函数,可以实现自动化鼠标操作,如模拟鼠标点击、拖动等。

如下代码段实现了鼠标动作脚本的回放功能,它从之前录制保存的脚本文件中读取鼠标动作信息,并按照脚本中记录的顺序模拟鼠标动作,实现鼠标动作的回放。

代码的主要功能如下:

  1. Play 函数中,打开之前保存的脚本文件,并使用 fgets 函数每次读取一行数据,保存在 buf 字符数组中。
  2. 使用 split 函数切割每行数据,将每行数据切割成以逗号分隔的四个字符串,并将这四个字符串转换为整数类型保存在 key_item 数组中。
  3. 根据 key_item 数组中的数据,判断是否需要进行鼠标点击动作,并调用 mouse_event 函数模拟鼠标点击。
  4. 调用 SetCursorPos 函数设置鼠标的位置,并使用 Sleep 函数模拟鼠标移动的延时,实现鼠标动作的回放。
  5. 循环执行以上步骤,直到脚本文件中的所有动作都被回放完毕。
// 回放脚本
void Play(char *script)
{
    FILE* fp = fopen(script, "r");
    char buf[1024];

    while (feof(fp) == 0)
    {
        // 每次读入一行
        memset(buf, 0, 1024);
        fgets(buf, 1024, fp);

        // 以逗号切割
        char split_comma[4][32] = { 0 };
        int comma_count = split(split_comma, buf, ",");

        int key_item[4] = { 0 };

        // std::cout << "长度: " << comma_count << std::endl;
        for (int x = 0; x < comma_count; x++)
        {
            // 继续切割冒号
            char split_colon[2][32] = { 0 };
            split(split_colon, split_comma[x], ":");

            // std::cout << "字典键 = " << split_colon[0] << " 字典值 = " << split_colon[1] << std::endl;
            key_item[x] = atoi(split_colon[1]);
        }

        if (key_item[3] != 0)
        {
            mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
        }
        if (key_item[4] != 0)
        {
            mouse_event(MOUSEEVENTF_RIGHTUP | MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0);
        }

        // 得到数据后开始回放
        SetCursorPos(key_item[0], key_item[1]);
        Sleep(70);
    }
}

最后是主函数部分,我们通过RegisterHotKey函数注册两个全局热键,通过F1实现鼠标录制部分,通过F2则实现鼠标回放,最后通过GetMessage函数接收全局消息事件,当出现WM_HOTKEY消息则依次判断是否启用录制回放等功能,代码如下所示;

int main(int argc, char* argv[])
{
    // 注册热键 F1 , F2
    if (0 == RegisterHotKey(NULL, 1,0, VK_F1))
    {
        cout << GetLastError() << endl;
    }
    if (0 == RegisterHotKey(NULL, 2,0, VK_F2))
    {
        cout << GetLastError() << endl;
    }
    if (0 == RegisterHotKey(NULL, 3, 0, VK_F3))
    {
        cout << GetLastError() << endl;
    }

    // 消息循环
    MSG msg = { 0 };

    while (GetMessage(&msg, NULL, 0, 0))
    {
        switch (msg.message)
        {
        case WM_HOTKEY:
        {
            if (1 == msg.wParam)
            {
                std::cout << "录制脚本" << std::endl;
                Recording((char *)"d://script.txt");
            }

            else if (2 == msg.wParam)
            {
                std::cout << "回放脚本" << std::endl;
                Play((char *)"d://script.txt");
            }

            else if (3 == msg.wParam)
            {
                exit(0);
                return 0;
            }

            break;
        }
        default:
            break;
        }
    }
    return 0;
}

读者可自行编译并运行这段代码,通过录制一段鼠标功能并回放,输出效果图如下所示;

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/d3a88993.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

与12.3 实现模拟鼠标录制回放相似的内容:

12.3 实现模拟鼠标录制回放

本节将向读者介绍如何使用键盘鼠标操控模拟技术,键盘鼠标操控模拟技术是一种非常实用的技术,可以自动化执行一些重复性的任务,提高工作效率,在Windows系统下,通过使用各种键盘鼠标控制函数实现动态捕捉和模拟特定功能的操作。有时我们经常需要进行重复性的鼠标操作,例如繁琐的点击、拖拽。这些任务可能消耗大量...

[转帖]Redis进阶(发布订阅,PipeLine,持久化,内存淘汰)

目录 1、发布订阅 1.1 什么是发布订阅 1.2 客户端实例演示 1.3 Java API演示 1.4 Redis发布订阅和rabbitmq的区别 2、批量操作 2.1 普通模式与 PipeLine 模式 2.2 适用场景 2.3 源码解析 2.4 Pipelining的局限性 2.5 事务与 L

前端使用 Konva 实现可视化设计器(12)- 连接线 - 直线

这一章实现的连接线,目前仅支持直线连接,为了能够不影响原有的其它功能,尝试了2、3个实现思路,最终实测这个实现方式目前来说最为合适了。 请大家动动小手,给我一个免费的 Star 吧~ 大家如果发现了 Bug,欢迎来提 Issue 哟~ github源码 gitee源码 示例地址 相关定义 连接点 记

[转帖]阿里达摩院最新存算芯片技术解读

https://zhuanlan.zhihu.com/p/448261354 12月3日,阿里达摩院成功研发新型架构芯片,已经被证明能够在阿里推荐系统中发挥极大的应用价值,并受到技术圈的普遍关注。 据悉这颗芯片与数据中心的推荐系统对于带宽/存储的需求完美匹配,大幅提升带宽的同时还实现了超低功耗,充分

[转帖]阿里达摩院最新存算芯片技术解读

https://zhuanlan.zhihu.com/p/448261354 陈巍谈芯: 12月3日,阿里达摩院成功研发新型架构芯片,已经被证明能够在阿里推荐系统中发挥极大的应用价值,并受到技术圈的普遍关注。 据悉这颗芯片与数据中心的推荐系统对于带宽/存储的需求完美匹配,大幅提升带宽的同时还实现了超

聊聊神经网络的基础知识

来自《深度学习入门:基于Python的理论与实现》 张量 Numpy、TensorFlow、Pytorch等框架主要是为了计算张量或是基于张量计算。 标量:0阶张量;12,4,3, 向量:一阶张量;[12,4,3] 矩阵:二阶张量;[ [12,4,3], [11,2,3] ] 多阶张量:多维数组;

它来了!真正的 python 多线程

哈喽大家好,我是咸鱼 几天前,IBM 工程师 Martin Heinz 发文表示 python 3.12 版本回引入"Per-Interpreter GIL”,有了这个 Per-Interpreter 全局解释器锁,python 就能实现真正意义上的并行/并发 我们知道,python 的多线程/进程

.NET周刊【5月第2期 2024-05-12】

国内文章 C#在工业数字孪生中的开发路线实践 https://mp.weixin.qq.com/s/b_Pjt2oii0Xa_sZp_9wYWg 这篇文章探讨了C#在工业数字孪生技术中的应用,介绍了三种基于C#的数字孪生系统实现方案: WPF + Unity:结合WPF技术和Unity引擎,实现客户

[转帖]Keepalived如何实现Nginx高可用

https://www.jb51.net/article/266305.htm Keepalived安装可参考Mysql+Keepalived实现双主热备 Master上的keepalived.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

我又学会了使用Range实现网络文件下载的断点续传

目录前言1、Range请求头1.1、概述1.2、使用限制1.3、范围请求1.4、预防资源变更2、断点续传下载实现2.1、流程设计2.2、代码实现2.3、运行结果3、RandomAccessFile4、思维拓展参考资料 前言 在某次摸鱼的过程中,老大突然后面冒出来说要做一个拉取文件到本地的需求(写的时