You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用SendMessage实现游戏自动喝药受鼠标干扰问题求解

SendMessage实现游戏自动喝药定位不准问题解决方案

问题描述

尝试通过Windows API SendMessage实现游戏自动喝药(autopot/autoheal)功能,核心逻辑为按顺序发送鼠标移动、右键点击药水栏位、鼠标移动、左键点击玩家自身的窗口消息,触发喝药逻辑,核心实现代码如下:

using_arrowslot = true;

// 点击药水
SendMessage(Window, WM_MOUSEMOVE, 0, MAKELPARAM(potx, poty));
SendMessage(Window, WM_RBUTTONDOWN, 0, MAKELPARAM(potx, poty));
SendMessage(Window, WM_RBUTTONUP, 0, MAKELPARAM(potx, poty));

// 点击玩家自身
SendMessage(Window, WM_MOUSEMOVE, 0, MAKELPARAM(playerx, playery));
SendMessage(Window, WM_LBUTTONDOWN, 0, MAKELPARAM(playerx, playery));
SendMessage(Window, WM_LBUTTONUP, 0, MAKELPARAM(playerx, playery));
start_manapot += std::chrono::seconds(seconds_to_cast);
using_arrowslot = false;

实际运行时存在明确问题:游戏过程中用户手动移动鼠标,SendMessage发送的点击事件会偶发定位到错误位置,导致自动喝药失效。
最初尝试的修复方案为Hook窗口过程WndProc,在自动喝药逻辑执行期间过滤无关鼠标消息,实现代码如下:

LRESULT __stdcall WndProc(const HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    if (ImGui_ImplWin32_WndProcHandler(hWnd, uMsg, wParam, lParam))
        return true; 

    if (menuShow || (using_arrowslot && lParam != 29426537 && lParam != 18024234))
        return 1;

    return CallWindowProc(oWndProc, hWnd, uMsg, wParam, lParam);
}

实测挂载Hook后功能无任何改善,鼠标操作时点击定位不准的问题依然存在,需要实现SendMessage发送的模拟消息不受本地键鼠操作干扰,稳定执行自动喝药。

问题根因

原有方案失效的核心原因有两点:

  • 绝大多数商业游戏不会直接读取鼠标消息lParam中携带的坐标参数,处理点击逻辑时会主动调用GetCursorPos、GetMessagePos等系统API获取系统层级的真实鼠标光标位置,消息里拼接的坐标根本不会被游戏读取,用户手动移动鼠标改变了真实光标位置时,自然会出现点击偏移。
  • 现有WndProc Hook逻辑存在硬伤:写死了两个lParam数值作为自身消息的判断依据,这两个值是固定坐标对应的MAKELPARAM计算结果,窗口位置、分辨率变化时对应坐标会完全改变,根本起不到过滤作用;同时多数游戏的鼠标输入走Raw Input、DirectInput通道,不经过标准窗口消息循环,Hook WndProc无法拦截这类输入。

可行修复方案

方案1:临时锁定系统光标位置(实现成本最低,适配绝大多数窗口化游戏)

整套自动喝药逻辑执行周期仅几十毫秒,用户几乎无感知,实现逻辑:

  • 触发自动喝药前,先调用GetCursorPos记录当前鼠标的真实位置
  • 每段点击操作执行前,高频调用SetCursorPos将系统光标强制固定到目标点击坐标,覆盖用户手动移动鼠标的操作
  • 整套点击流程执行完成后,调用SetCursorPos将鼠标恢复到之前记录的原始位置
    核心实现参考:
POINT original_cursor_pos;
GetCursorPos(&original_cursor_pos);

using_arrowslot = true;

// 锁定光标到药水位置,发送点击消息
for (int i = 0; i < 5; i++) {
    SetCursorPos(potx, poty);
    Sleep(1);
}
SendMessage(Window, WM_MOUSEMOVE, 0, MAKELPARAM(potx, poty));
SendMessage(Window, WM_RBUTTONDOWN, 0, MAKELPARAM(potx, poty));
SendMessage(Window, WM_RBUTTONUP, 0, MAKELPARAM(potx, poty));

// 锁定光标到玩家位置,发送点击消息
for (int i = 0; i < 5; i++) {
    SetCursorPos(playerx, playery);
    Sleep(1);
}
SendMessage(Window, WM_MOUSEMOVE, 0, MAKELPARAM(playerx, playery));
SendMessage(Window, WM_LBUTTONDOWN, 0, MAKELPARAM(playerx, playery));
SendMessage(Window, WM_LBUTTONUP, 0, MAKELPARAM(playerx, playery));

start_manapot += std::chrono::seconds(seconds_to_cast);
using_arrowslot = false;

// 恢复光标原始位置
SetCursorPos(original_cursor_pos.x, original_cursor_pos.y);

方案2:替换为SendInput硬件级模拟(适配性最高,支持全屏独占游戏)

如果游戏运行在全屏独占模式,或通过DirectInput/Raw Input读取输入,SendMessage发送的窗口消息会被直接忽略,此时需要替换为SendInput做输入模拟:

  • 该API生成的输入事件会被系统识别为真实硬件输入,无论游戏使用哪种输入接口都能正常读取
  • 实现时同样搭配临时锁光标的逻辑,避免用户手动移动鼠标干扰定位
  • 操作完成后将鼠标移回原始位置即可

方案3:直接调用游戏内部喝药函数(稳定性最高,完全无输入干扰)

如果具备基础逆向能力,可以逆向定位游戏中喝药逻辑对应的内部函数,以及药水栏位、玩家自身对象的内存地址,自动喝药触发时直接在进程内调用该函数,完全不需要模拟鼠标操作,从根本上避免键鼠操作的干扰。该方案缺点是游戏版本更新后需要重新定位函数偏移和内存地址,维护成本稍高。

原有Hook方案的修正提示

如果需要保留WndProc Hook过滤逻辑,不要写死lParam固定值判断自身消息,可以在发送消息时给wParam传入一个正常鼠标输入不会用到的自定义标记值(比如0xFFFF),Hook逻辑执行时判断wParam为自定义标记则放行,自动喝药执行期间其他鼠标消息直接丢弃即可。注意该方案仅能过滤多余的窗口消息,无法解决游戏读取真实光标位置导致的偏移问题,必须搭配锁光标逻辑或内部调用才能生效。

内容的提问来源于stack exchange,提问作者kuhi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 01:18:30