很多脚本在自己电脑上能正常点击,发给别人以后就点歪了。这个问题不一定是坐标写错,也不一定是 AHK 不稳定,更多时候是 Windows 缩放比例不同导致的 DPI 坐标差异。

最常见的情况是:你在 150% 缩放的屏幕上记录了按钮坐标,对方电脑是 100% 缩放;或者你自己有两个显示器,一个 150%,一个 100%。这时同一个窗口、同一个按钮,看起来位置差不多,但脚本里的坐标已经不是同一个尺度了。

一、先理解 DPI 和缩放比例

Windows 里 100% 缩放通常对应 96 DPI,150% 对应 144 DPI,200% 对应 192 DPI。你可以简单理解为:DPI 越高,同样的界面元素在逻辑坐标和物理像素之间的换算差异越明显。

缩放比例 DPI 换算系数
100% 96 1.00
125% 120 1.25
150% 144 1.50
175% 168 1.75
200% 192 2.00
225% 216 2.25

如果你在 144 DPI 下记录了坐标,直接拿到 96 DPI 环境里用,点击点自然会偏。反过来也一样。

二、记录坐标时要有“基准 DPI”

我更推荐把坐标和记录时的 DPI 一起保存。比如你在 150% 缩放,也就是 144 DPI 下记录了按钮坐标 x=67, y=494,那么脚本执行时应该先获取目标窗口当前 DPI,再做比例换算。

#Requires AutoHotkey v1.1

baseDpi := 144
targetDpi := WinGetDpi("A")

x := Round(67 * targetDpi / baseDpi)
y := Round(494 * targetDpi / baseDpi)
Click, %x%, %y%

WinGetDpi(winTitle := "A") {
    DllCall("SetThreadDpiAwarenessContext", "Ptr", -3, "Ptr")
    WinGet, hwnd, ID, %winTitle%
    hMonitor := DllCall("MonitorFromWindow", "Ptr", hwnd, "UInt", 2, "Ptr")
    DllCall("Shcore.dll\GetDpiForMonitor"
        , "Ptr", hMonitor
        , "Int", 0
        , "UInt*", dpiX
        , "UInt*", dpiY)
    return dpiX
}

这里的 baseDpi 是你记录坐标时的 DPI,targetDpi 是脚本运行时目标窗口所在显示器的 DPI。只要这两个值搞清楚,坐标就能按比例修正。

三、更稳的做法:统一保存 96 DPI 坐标

如果脚本以后要发给很多人用,我建议不要保存“当前电脑的裸坐标”,而是统一换算成 96 DPI 坐标保存。执行时再按目标窗口 DPI 放大。

; 记录时,把当前 DPI 坐标换算成 96 DPI 坐标
currentDpi := WinGetDpi("A")
savedX := Round(realX * 96 / currentDpi)
savedY := Round(realY * 96 / currentDpi)

; 执行时,再从 96 DPI 坐标换算到目标窗口 DPI
targetDpi := WinGetDpi("A")
clickX := Round(savedX * targetDpi / 96)
clickY := Round(savedY * targetDpi / 96)
Click, %clickX%, %clickY%

这样做的好处是,脚本数据不再绑定某一台电脑。你在 125%、150%、200% 缩放下记录的坐标,最后都能变成统一基准。

四、Window Spy 看到的坐标不要直接迷信

Window Spy 很方便,但它显示的坐标也受坐标模式、窗口位置、DPI 感知状态影响。尤其是多显示器环境里,主屏和副屏缩放不同,直接复制坐标到脚本里,很容易只在当前电脑有效。

如果只是自己用的小脚本,硬编码坐标没问题;如果准备分享给别人,或者要长期维护,就应该把 DPI 换算这一步补上。

五、ImageSearch 也会受 DPI 影响

DPI 不只影响点击坐标,也会影响截图和找图。比如你在 150% 缩放下截出来的按钮图片,拿到 100% 缩放环境里做 ImageSearch,图片尺寸、字体抗锯齿、边缘像素都可能不同。

遇到这种情况,可以考虑几种办法:

  • 按目标 DPI 准备不同尺寸的模板图。
  • 缩小 ImageSearch 搜索区域,减少误匹配。
  • 能用控件、窗口消息、UIA 的地方,尽量不要只靠图片。
  • 对字体、按钮变化明显的软件,可以考虑 FindText 这类更适合文字/图形特征的方案。

我的经验是:纯坐标点击适合临时脚本,DPI 换算适合可复用脚本,控件和 UIA 更适合长期维护。不要一上来就把所有坐标写死,后面排查起来会很痛苦。

六、什么时候不用处理 DPI

如果脚本只在自己电脑上用,显示器缩放也不变,那可以先不管 DPI。写脚本要看场景,不是每个小工具都要做成通用框架。

但只要你的脚本满足下面任意一种情况,就建议提前考虑 DPI:

  • 脚本要发给别人使用。
  • 你自己有多个显示器,而且缩放比例不同。
  • 脚本依赖大量固定坐标点击。
  • 脚本需要配合截图、找图、找色。
  • 窗口可能在不同显示器之间移动。

坐标问题看起来很玄,其实核心就是一句话:记录坐标时知道自己在哪个 DPI,执行坐标时知道目标窗口在哪个 DPI,中间按比例换算。

相关专题

参考来源

声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。