很多脚本在自己电脑上能正常点击,发给别人以后就点歪了。这个问题不一定是坐标写错,也不一定是 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,中间按比例换算。
相关专题
参考来源
- AutoHotkey 文档:DPI Scaling
- Microsoft 文档:GetDpiForMonitor
- Microsoft 文档:MonitorFromWindow

评论(0)