25 - AHK v1 与 v2 差异
2026/7/16大约 5 分钟
25 - AHK v1 与 v2 差异
为什么有 v1 和 v2?
AHK v1 从 2003 年开始发展,语法以命令式为主,经历了多年累积,存在不少不一致的设计。AHK v2 从 2017 年开始重构,目标是建立一套更一致、更现代、更易用的语法体系。
当前状态:
- AHK v2 已正式发布,是官方推荐版本
- AHK v1 维护模式(只修 bug,不再添加新功能)
- 新项目应该使用 v2
语法核心差异
赋值
; ===== v1 =====
var = value ; 传统赋值(字符串,不含表达式)
var := value ; 表达式赋值
; v1 中两种赋值并存,容易混淆
name = Hello ; name = "Hello"(传统赋值)
name := "Hello" ; name = "Hello"(表达式赋值)
count = 5 ; count = "5"(传统赋值,字符串!)
count := 5 ; count = 5(表达式赋值,数字)
; ===== v2 =====
var := value ; 统一用 :=,只有表达式赋值
name := "Hello"
count := 5
; v1 的 var = value 在 v2 中不再支持
变量引用
; ===== v1 =====
MsgBox, %var% ; 百分号变量引用(命令参数中)
MsgBox % "Value: " var ; 强制表达式模式
; ===== v2 =====
MsgBox var ; 变量直接使用,不需要 %var%
MsgBox "Value: " var ; 表达式自动拼接
函数调用
; ===== v1 =====
; 命令式调用(参数用逗号分隔,不用括号)
MsgBox, Hello World
FileRead, content, C:\test.txt
; 函数式调用(用括号)
result := Add(1, 2)
; ===== v2 =====
; 统一函数式调用(所有命令变成函数)
MsgBox "Hello World"
content := FileRead("C:\test.txt")
result := Add(1, 2)
字符串拼接
; ===== v1 =====
; 自动拼接(相邻项)只在表达式模式中有效
msg := "Hello" . " " . "World" ; . 运算符拼接
msg = Hello %name% ; 传统赋值中用 %var%
; ===== v2 =====
msg := "Hello" " " "World" ; 表达式中自动拼接
msg := "Hello" . " " . "World" ; . 运算符也可用
msg := "Hello " name ; 变量自动拼接
热键定义
; ===== v1 =====
^j::
Send, Hello
MsgBox, Done
return ; v1 热键块必须用 return 结尾
; 单行热键
^k:: Send, Hello
; ===== v2 =====
^j:: {
Send "Hello"
MsgBox "Done"
} ; v2 用花括号,不需要 return
; 单行热键
^k:: Send "Hello"
if/else 语法
; ===== v1 =====
if (x > 0) {
MsgBox, Positive
} else {
MsgBox, Negative
}
; v1 还支持命令式 if(不推荐)
IfExist, C:\test.txt
MsgBox, File exists
; ===== v2 =====
if (x > 0) {
MsgBox "Positive"
} else {
MsgBox "Negative"
}
; 命令式 if 不再支持,全部用函数
if FileExist("C:\test.txt")
MsgBox "File exists"
命令 vs 函数对照表
| v1 命令 | v2 函数 |
|---|---|
MsgBox, text | MsgBox text |
Send, text | Send text |
Run, program | Run program |
WinActivate, title | WinActivate title |
WinClose, title | WinClose title |
WinWait, title | WinWait title |
FileRead, var, path | var := FileRead(path) |
FileWrite, text, path | FileWrite text, path |
FileAppend, text, path | FileAppend text, path |
FileDelete, path | FileDelete path |
Clipboard := text | A_Clipboard := text |
Sleep, ms | Sleep ms |
SetTimer, label, ms | SetTimer func, ms |
Click, x, y | Click x, y |
MouseMove, x, y | MouseMove x, y |
ControlSend, keys, ctrl, win | ControlSend keys, ctrl, win |
InputBox, var, prompt | var := InputBox(prompt) |
IniRead, var, file, sec, key | var := IniRead(file, sec, key) |
IniWrite, value, file, sec, key | IniWrite value, file, sec, key |
剪贴板变量
; ===== v1 =====
Clipboard := "Hello" ; 变量名 Clipboard
var := Clipboard ; 读取
; ===== v2 =====
A_Clipboard := "Hello" ; 变量名 A_Clipboard(加A_前缀)
var := A_Clipboard ; 读取
对象系统
; ===== v1 =====
; v1 的 Object 是旧式键值对象
obj := {}
obj.key := "value"
obj := Object("key", "value")
; 数组
arr := []
arr.Push(1)
; ===== v2 =====
; v2 有更规范的类型
arr := [1, 2, 3] ; Array
m := Map("key", "value") ; Map(字典)
obj := {key: "value"} ; Object(简化语法)
obj := Object() ; Object
v2 的
Map替代了 v1 中 Object 的字典用法,Array更规范。
错误处理
; ===== v1 =====
; v1 没有 try/catch,错误处理非常有限
; 用 ErrorLevel 变量检查
Run, notepad.exe
if (ErrorLevel = "ERROR")
MsgBox, Failed
; ===== v2 =====
; v2 有完整的 try/catch/finally
try {
Run "notepad.exe"
} catch Error as e {
MsgBox "Failed: " e.Message
}
#HotIf vs #If
; ===== v1 =====
#If WinActive("Notepad")
^c:: MsgBox, In Notepad
#If
; ===== v2 =====
#HotIf WinActive("Notepad")
^c:: MsgBox "In Notepad"
#HotIf
迁移策略
1. 从 v1 到 v2 的迁移步骤
- 确保脚本开头有
#Requires AutoHotkey v2.0 - 把所有命令式调用改为函数式调用
- 把
var = value改为var := value - 把
%var%改为直接使用变量名 - 把热键的
return改为花括号{} - 把
Clipboard改为A_Clipboard - 把
#If改为#HotIf - 把
ErrorLevel改为 try/catch - 把 Object 的字典用法改为 Map
2. 常见迁移陷阱
; 陷阱1: 等号 vs 冒号等号
; v1:
x = Hello ; 字符串赋值
; v2 必须改为:
x := "Hello" ; 加引号!
; 陷阱2: 百分号变量
; v1:
MsgBox, %x%
; v2:
MsgBox x ; 不需要百分号
; 陷阱3: 命令参数顺序
; v1:
FileRead, OutputVar, FilePath
; v2:
OutputVar := FileRead(FilePath) ; 返回值,不是输出参数
; 陷阱4: if 后面的逗号
; v1:
IfEqual, x, 1, MsgBox, Equal
; v2:
if (x = 1)
MsgBox "Equal"
; 陷阱5: 热键 return
; v1:
^j::
Send, Hello
return
; v2:
^j:: {
Send "Hello"
}
; 陷阱6: GoSub / Label
; v1 支持 GoSub 跳转到标签
GoSub, MyLabel
MyLabel:
MsgBox, Hello
return
; v2 不支持 GoSub,改为函数调用
MyLabel() {
MsgBox "Hello"
}
MyLabel()
3. 不再支持的 v1 特性
| v1 特性 | v2 替代 |
|---|---|
var = value 传统赋值 | var := value |
%var% 百分号变量 | 直接变量名 |
命令式语法 MsgBox, text | 函数式 MsgBox text |
GoSub / Labels | 函数调用 |
ErrorLevel | try/catch + 返回值 |
#If | #HotIf |
Clipboard | A_Clipboard |
SetTimer, Label | SetTimer FuncObj |
| 单行 if 无括号 | 建议使用括号 |
Loop, Read | Loop Read |
Loop, Files | Loop Files |
SplashImageOn | Gui 方式实现 |
共存与兼容
同时使用 v1 和 v2
; v1 脚本开头
#Requires AutoHotkey v1.1
; v2 脚本开头
#Requires AutoHotkey v2.0
; AHK 安装程序可以选择安装 v1 或 v2 或两者
; .ahk 文件默认用 v2 打开
; .ah1 文件扩展名用于 v1 脚本
; .ah2 文件扩展名用于 v2 脚本
迁移辅助工具
AHK 官方提供了迁移辅助工具:
- AHK v1 to v2 Converter — 自动转换部分 v1 语法为 v2
- 不完美,需要手动检查和调整
学习建议
- 新项目直接用 v2 — 不要从 v1 开始
- 已有 v1 脚本 — 按需迁移,不必一次性全部转换
- v1 脚本可以和 v2 脚本并存 — 不冲突
- 本教程全程基于 v2 — 按此教程学习即可
恭喜! 你已完成 AutoHotkey 完整教程的学习。现在你已经具备了 AHK v2 的基础知识到进阶能力的完整知识体系。回到 目录 查看所有章节。
