qq_41990041 2024-11-11 04:20 采纳率: 100%
浏览 19
已采纳

C++ 句柄后台鼠标拖动如何实现

C++ 中 如何实现指定句柄 鼠标后台拖动
MUmu模拟器中
后台点击没有问题 如下代码

void mouse_move_press(HWND hwnd, int x, int y) {
    // 将x和y坐标转换为符合要求的格式
    LPARAM point = MAKELPARAM(x, y);

    // 发送鼠标左键按下消息
    PostMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, point);

    // 发送鼠标左键松开消息
    PostMessage(hwnd, WM_LBUTTONUP, MK_LBUTTON, point);
}

但是 在按下消息后 插入移动消息 窗口只响应了点击 没有移动

  • 写回答

30条回答 默认 最新

  • bug菌¹ Java领域优质创作者 2024-11-11 22:16
    关注

    该回答引用ChatGPT-4o并结合自身经验回答,望对题主有所帮助/启发;若有帮助,还望采纳🤝🤝🤝


    你好!你希望在 Windows 环境下通过 C++ 实现对指定窗口句柄(如 Mumu 模拟器)的后台鼠标拖动操作。你目前的代码仅发送了鼠标按下和松开消息,但在插入移动消息后窗口仍然只响应点击而不响应拖动。以下是解决这一问题的详细方法和示例代码。

    问题分析

    1. 消息顺序和内容:拖动操作需要按特定顺序发送 WM_LBUTTONDOWN、一系列 WM_MOUSEMOVEWM_LBUTTONUP 消息。如果移动消息没有正确发送或参数不正确,拖动操作将无法实现。

    2. 坐标系问题:确保发送的坐标是相对于窗口的客户区(Client Area),而不是屏幕坐标。使用 MAPPOINT 或相应的转换函数将屏幕坐标转换为客户区坐标。

    3. 消息发送方式PostMessage 将消息放入消息队列,而 SendMessage 直接发送消息并等待处理。在某些情况下,SendMessage 可能更适合,因为它保证消息按顺序处理。

    4. 模拟拖动的平滑性:发送一系列有渐变坐标的 WM_MOUSEMOVE 消息,以模拟真实的鼠标拖动。

    5. 目标窗口的处理方式:某些应用程序可能不会正确处理直接发送的鼠标消息,特别是如果它们有自定义的消息处理逻辑或使用 DirectX 等技术绘制界面。

    解决方案

    1. 确保坐标正确

    确保发送的坐标是客户区坐标。如果你有屏幕坐标,可以使用 ScreenToClient 函数将其转换为客户区坐标。

    POINT pt;
    pt.x = screenX;
    pt.y = screenY;
    ScreenToClient(hwnd, &pt);
    

    2. 使用 SendMessage 代替 PostMessage

    有时使用 SendMessage 能确保消息按顺序被处理,从而更好地模拟拖动操作。

    3. 发送一系列 WM_MOUSEMOVE 消息

    为了模拟拖动的过程,发送多个移动消息,使拖动看起来更自然。

    4. 示例代码

    以下是一个完整的示例函数,展示如何实现鼠标拖动操作:

    #include <windows.h>
    #include <thread>
    #include <chrono>
    
    // 延迟函数,单位为毫秒
    void delay(int ms) {
        std::this_thread::sleep_for(std::chrono::milliseconds(ms));
    }
    
    // 后台鼠标拖动函数
    void mouse_drag(HWND hwnd, int startX, int startY, int endX, int endY, int steps = 10, int delayMs = 20) {
        // 将屏幕坐标转换为客户区坐标(如果需要)
        // POINT pt;
        // pt.x = startX;
        // pt.y = startY;
        // ScreenToClient(hwnd, &pt);
        // startX = pt.x;
        // startY = pt.y;
    
        // 将鼠标移动到起始位置并按下左键
        LPARAM lParamStart = MAKELPARAM(startX, startY);
        SendMessage(hwnd, WM_MOUSEMOVE, MK_LBUTTON, lParamStart);
        SendMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, lParamStart);
    
        // 计算每步的移动增量
        double deltaX = (endX - startX) / (double)steps;
        double deltaY = (endY - startY) / (double)steps;
    
        for(int i = 1; i <= steps; ++i) {
            int currentX = startX + static_cast<int>(deltaX * i);
            int currentY = startY + static_cast<int>(deltaY * i);
            LPARAM lParamMove = MAKELPARAM(currentX, currentY);
            SendMessage(hwnd, WM_MOUSEMOVE, MK_LBUTTON, lParamMove);
            delay(delayMs); // 添加延迟,使拖动过程更自然
        }
    
        // 在结束位置松开左键
        LPARAM lParamEnd = MAKELPARAM(endX, endY);
        SendMessage(hwnd, WM_LBUTTONUP, 0, lParamEnd);
    }
    
    int main() {
        // 示例用法
        // 获取目标窗口句柄(例如,通过窗口标题)
        HWND hwnd = FindWindow(NULL, L"Mumu 模拟器窗口标题");
        if(hwnd == NULL) {
            MessageBox(NULL, L"未找到目标窗口!", L"错误", MB_ICONERROR);
            return -1;
        }
    
        // 定义拖动的起点和终点坐标(客户区坐标)
        int startX = 100;
        int startY = 100;
        int endX = 200;
        int endY = 200;
    
        // 执行拖动操作
        mouse_drag(hwnd, startX, startY, endX, endY);
    
        return 0;
    }
    

    代码详解

    1. 延迟函数

      void delay(int ms) {
          std::this_thread::sleep_for(std::chrono::milliseconds(ms));
      }
      
      • 使用 C++11 的线程库实现延迟,以便在发送移动消息时有时间间隔,使拖动过程更自然。
    2. **鼠标拖动函数 mouse_drag**:

      void mouse_drag(HWND hwnd, int startX, int startY, int endX, int endY, int steps = 10, int delayMs = 20)
      
      • hwnd:目标窗口句柄。
      • startX, startY:拖动起点坐标(客户区)。
      • endX, endY:拖动终点坐标(客户区)。
      • steps:拖动过程中发送的 WM_MOUSEMOVE 消息数量,数值越大拖动越平滑。
      • delayMs:每步之间的延迟时间,单位为毫秒。
    3. 发送鼠标按下消息

      LPARAM lParamStart = MAKELPARAM(startX, startY);
      SendMessage(hwnd, WM_MOUSEMOVE, MK_LBUTTON, lParamStart);
      SendMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, lParamStart);
      
      • 首先发送 WM_MOUSEMOVE 消息,将鼠标移动到起始位置。
      • 然后发送 WM_LBUTTONDOWN 消息,模拟鼠标左键按下。
    4. 发送一系列 WM_MOUSEMOVE 消息

      for(int i = 1; i <= steps; ++i) {
          int currentX = startX + static_cast<int>(deltaX * i);
          int currentY = startY + static_cast<int>(deltaY * i);
          LPARAM lParamMove = MAKELPARAM(currentX, currentY);
          SendMessage(hwnd, WM_MOUSEMOVE, MK_LBUTTON, lParamMove);
          delay(delayMs); // 添加延迟,使拖动过程更自然
      }
      
      • 计算每步的坐标增量,逐步发送 WM_MOUSEMOVE 消息,模拟鼠标拖动过程。
    5. 发送鼠标松开消息

      LPARAM lParamEnd = MAKELPARAM(endX, endY);
      SendMessage(hwnd, WM_LBUTTONUP, 0, lParamEnd);
      
      • 在终点位置发送 WM_LBUTTONUP 消息,模拟鼠标左键松开,完成拖动。

    重要注意事项

    1. 坐标系

      • 确保发送的坐标是相对于目标窗口的客户区坐标。如果你有屏幕坐标,需要先转换为客户区坐标。
      • 使用 ScreenToClient 函数进行转换:
        POINT pt;
        pt.x = screenX;
        pt.y = screenY;
        ScreenToClient(hwnd, &pt);
        startX = pt.x;
        startY = pt.y;
        
    2. 权限问题

      • 某些应用程序可能需要以管理员权限运行才能接收和处理发送的消息。
      • 尝试以管理员身份运行你的 C++ 程序。
    3. 消息发送方式

      • 使用 SendMessage 确保消息按顺序处理。如果 PostMessage 无法正常工作,可以尝试切换为 SendMessage
    4. 应用程序响应

      • 某些应用程序可能不会正确处理直接发送的鼠标消息,特别是使用自定义输入处理或 DirectX 等技术绘制界面的应用程序。
      • 如果目标应用程序不响应,可能需要考虑其他方法,如模拟全局鼠标事件(SendInput),但这将影响整个系统的鼠标状态。

    进一步优化

    1. 模拟更多的移动步骤

      • 增加 steps 参数的值,使拖动过程更加平滑。
    2. 动态调整延迟

      • 根据需要调整 delayMs 的值,找到合适的延迟时间以平衡速度和自然性。
    3. 错误处理

      • 添加更多的错误检查和处理,例如确保 hwnd 有效,坐标在窗口范围内等。
    4. 调试

      • 使用调试工具(如 Spy++)监视目标窗口接收到的消息,确保消息按预期发送和处理。

    备选方案:使用 SendInput

    如果直接发送 WM_* 消息无法满足需求,可以考虑使用 SendInput 函数来模拟鼠标事件。这种方法会影响全局鼠标状态,但有时更有效。

    #include <windows.h>
    #include <thread>
    #include <chrono>
    
    // 延迟函数,单位为毫秒
    void delay(int ms) {
        std::this_thread::sleep_for(std::chrono::milliseconds(ms));
    }
    
    // 使用 SendInput 模拟鼠标拖动
    void send_mouse_drag(int startX, int startY, int endX, int endY, int steps = 10, int delayMs = 20) {
        // 设置鼠标位置
        SetCursorPos(startX, startY);
        delay(100);
    
        // 模拟鼠标左键按下
        INPUT inputs[1] = {0};
        inputs[0].type = INPUT_MOUSE;
        inputs[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
        SendInput(1, inputs, sizeof(INPUT));
        delay(delayMs);
    
        // 模拟鼠标移动
        for(int i = 1; i <= steps; ++i) {
            int currentX = startX + ((endX - startX) * i) / steps;
            int currentY = startY + ((endY - startY) * i) / steps;
            SetCursorPos(currentX, currentY);
            delay(delayMs);
        }
    
        // 模拟鼠标左键松开
        inputs[0].mi.dwFlags = MOUSEEVENTF_LEFTUP;
        SendInput(1, inputs, sizeof(INPUT));
    }
    
    int main() {
        // 示例用法
        int startX = 100;
        int startY = 100;
        int endX = 200;
        int endY = 200;
    
        send_mouse_drag(startX, startY, endX, endY);
    
        return 0;
    }
    

    注意SendInput 会影响整个系统的鼠标状态,因此不适用于后台操作,但在某些情况下可能更可靠。

    总结

    通过正确发送一系列 WM_LBUTTONDOWNWM_MOUSEMOVEWM_LBUTTONUP 消息,并确保坐标系和消息发送方式正确,可以在后台实现鼠标拖动操作。上述示例代码提供了一个基本框架,你可以根据具体需求进行调整和优化。如果目标应用程序仍然不响应,可能需要探索其他方法,如使用更高级的输入模拟技术或与应用程序开发者沟通以了解其消息处理机制。

    如果有进一步的问题或需要更详细的解释,请随时提问!

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(29条)

报告相同问题?

问题事件

  • 已采纳回答 11月12日
  • 创建了问题 11月11日

悬赏问题

  • ¥30 STM32 INMP441无法读取数据
  • ¥100 求汇川机器人IRCB300控制器和示教器同版本升级固件文件升级包
  • ¥15 用visualstudio2022创建vue项目后无法启动
  • ¥15 x趋于0时tanx-sinx极限可以拆开算吗
  • ¥500 把面具戴到人脸上,请大家贡献智慧
  • ¥15 任意一个散点图自己下载其js脚本文件并做成独立的案例页面,不要作在线的,要离线状态。
  • ¥15 各位 帮我看看如何写代码,打出来的图形要和如下图呈现的一样,急
  • ¥30 c#打开word开启修订并实时显示批注
  • ¥15 如何解决ldsc的这条报错/index error
  • ¥15 VS2022+WDK驱动开发环境