AHK 脚本重复运行,轻则托盘里多出一排 H 图标,重则两个脚本同时响应热键、重复点击、重复写文件。避免重复运行,最常用的是 #SingleInstance;更复杂的场景,还要加进程检测、锁文件或窗口检测。
本文以 AHK v1 为主,重点讲三个问题:#SingleInstance 怎么选;为什么用了 Force 还会弹出 Could not close the previous instance of this script. Keep waiting?;以及 ChangeWindowMessageFilterEx 在权限不同的实例之间有什么作用。
一、最常用:#SingleInstance Force
#Requires AutoHotkey v1.1 #NoEnv #SingleInstance Force SetBatchLines, -1 MsgBox, 当前脚本只保留一个实例。 return
#SingleInstance Force 的意思是:如果旧实例已经运行,再启动一次脚本时,新实例会尝试让旧实例退出,然后自己继续运行。它适合大多数日常脚本。
二、Force、Ignore、Off、默认提示 怎么选
| 写法 | 行为 | 适合场景 |
|---|---|---|
| #SingleInstance Force | 自动替换旧实例 | 开发调试、常驻热键脚本 |
| #SingleInstance Ignore | 如果旧实例存在,新实例直接退出 | 只允许旧实例继续运行,不希望被重启 |
| #SingleInstance Off | 允许多个实例同时运行 | 多窗口工具、临时任务、需要并行运行的脚本 |
| 默认提示 | 重复运行时弹窗询问 | 新手测试可以,长期脚本不推荐依赖弹窗 |
三、为什么 Force 也会弹等待提示
有时新实例会提示:上一个实例没有及时关闭,是否继续等待。它表示新脚本已经找到旧脚本,也发送了关闭请求,但旧脚本没有按预期退出。弹窗内容:Could not close the previous instance of this script. Keep waiting?
常见原因包括:旧脚本正在忙于循环或定时器任务,旧脚本正在显示对话框,两个实例启动权限不同,或者退出清理过程耗时太久。
四、权限问题:ChangeWindowMessageFilterEx 的作用
如果旧实例以管理员权限运行,新实例以普通权限运行,Windows 的消息过滤机制可能阻止新实例通知旧实例退出。这种情况下,单实例替换就容易失败。
你提到的调用用于给当前脚本隐藏主窗口放行相关窗口消息,让不同权限实例之间的关闭通知更容易送达。
DllCall("ChangeWindowMessageFilterEx", "ptr", A_ScriptHwnd, "uint", 0x44, "uint", 1, "ptr", 0)
其中 A_ScriptHwnd 是脚本隐藏主窗口句柄,0x44 是相关消息编号,1 表示允许该消息通过。它不是强制结束旧进程。
这条命令可以避免用了 Force 还会弹出 Could not close the previous instance of this script. Keep waiting? 弹窗的问题
五、进程检测:#SingleInstance 的补充
如果你想自己控制发现旧实例后的处理方式,可以使用 PID 锁文件。启动时写入当前 PID,下次启动时先读取旧 PID,确认旧进程是否还存在。
lockFile := A_Temp "\AHK66_MyScript.lock"
thisPid := DllCall("GetCurrentProcessId")
if FileExist(lockFile) {
FileRead, oldPid, %lockFile%
oldPid := Trim(oldPid)
Process, Exist, %oldPid%
if ErrorLevel {
MsgBox, 旧实例仍在运行,PID:%oldPid%
ExitApp
}
}
FileDelete, %lockFile%
FileAppend, %thisPid%, %lockFile%
六、处理建议
- 普通脚本优先使用
#SingleInstance Force。 - 不想被新实例打断的后台脚本使用
Ignore。 - 统一启动权限,不要一个管理员一个普通权限。
- 让循环和退出清理都尽量快速返回。
- 复杂场景再加 PID 锁文件或隐藏窗口检测。

评论(0)