AutoHotkey 初学者向导

AutoHotKey

AutoHotkey 初学者向导 by tidbit

1 - 基础

在我们开始我们的旅程之前, 让我给你一些建议吧. 在本向导中, 你会看到大量的文字和大量代码. 为了更有效的学习, 建议你阅读这些文字并尝试这些代码. 然后再深入学习这些代码.
你可以复制并粘贴此页上的大多数示例.
如果你弄糊涂了, 试着再看一遍.

a. 下载并安装 AutoHotkey

在学习使用 AutoHotkey(AHK) 之前, 你需要下载它. 下载后, 你可能会需要安装它. 但这取决于你下载的版本. 对于本指南, 我们将使用安装版, 因为对于新手来说, 它最容易设置.

文字说明:

  1. 访问 AutoHotkey 主页. https://autohotkey.com/
  2. 点击下载. https://autohotkey.com/download/ahk-install.exe
  3. 在安装 Autohotkey 过程中, 需要你选择安装 UNICODE 或者 ANSI 版本. 你很可能需要 UNICODE 版本, 简单点说, 如果你想要它支持非英文字符和数字, 那就选择 UNICODE 版. 版本选择之后, 一直点下一步直到你看到安装(Install)按钮.
  4. 装完了? 棒极了! 我们接着往下看.

视频介绍:
Frankie 的 "Install and Hello World"
http://www.autohotkey.com/forum/viewtopic.php?t=77674

b. 如何创建一个脚本

装完 Autohotkey 后, 你也许会想它能做些什么. AutoHotkey 不是魔法, 虽然我也想. 所以需要我们告诉它要去干什么. 而这个过程叫做"写脚本".

文字说明:

  • 1. 右键点桌面空白处.
  • 2. 点击"新建"菜单.
  • 3. 点击里面的"AutoHotkey Script"新建一个脚本.
  • 4. 给脚本命名. 备注: 文件名必须带 .ahk 后缀, 例如 MyScript.ahk
  • 5. 找到刚刚新建的脚本并右键点击它.
  • 6. 点击"Edit Script".
  • 7. 一个新窗口被弹出, 也许是记事本. 如果是这样就成功了!

    现在你已经创建了一个脚本, 我们需要加点内容到脚本中. 如果需要用到内置命令, 函数和变量, 请查看 第5节.

    这是一个使用 Send 命令创建的一个包含热键的简单脚本, 当你按下热键后, 它会向窗口发送一段文字.

    ^j::
       Send, My First Script
    Return
    

    稍后我们将进行更深入的研究. 在此之前, 我们先解释一下上面的代码.
    - 第一行. ^j:: 是热键. ^ 代表 CTRL, j 是字母 j. 它在 :: 左边 表示你将按下这个热键.
    - 第二行. Send, My First Script 表示如何发送按键. Send 是命令, 在逗号(,)之后的内容将会被发送.
    - 第三行. Return. Return 将会成为你最好的朋友. 它将停止执行之后的代码. 当你的脚本包含越来越多的东西时, 使用 Return 会避免很多问题.


  • 8. 保存文件.
  • 9. 双击桌面上的文件来运行它, 打开记事本或者其它可以输入文字的地方然后按下Ctrl+J.
  • 10. 太好了! 你的第一个脚本完成了. 给自己一些奖励, 然后返回阅读本教程的其余部分.

视频介绍:
Frankie 的"Install and Hello World"
http://www.autohotkey.com/forum/viewtopic.php?t=77674

c. 不要合并命令

当你在写代码的时候, 你可能有强烈的冲动想在同一行放下几个命令, 或者在一个命令中嵌套多个命令, 千万不要这样做. 在第5节 我们将会告诉你为什么不要这么做, 同时, 我们还将告诉你应该怎么做.

d. 其它基本信息

如何在你的计算机上找到帮助文件:
有几种方法可以做到这一点, 假设你已将 AutoHotkey 安装到默认位置.

方案1:
  1. 找到开始菜单或屏幕上的开始按钮, 通常在左下角.
  2. 点击程序或者所有程序.
  3. 在列表中查找 AutoHotkey.
  4. 你会看到 AutoHotkey Help File. 点击它.
  5. 完成!
方案2:
  1. 去你的桌面.
  2. 找到我的电脑或者计算机. 然后打开它.
  3. 进入安装 AutoHotkey 的硬盘. 可能是 C:\ 盘.
  4. Program Files 文件夹里找到 AutoHotkey.
  5. 查找 AutoHotkey.chm 或者找一个名叫 Autohotkey 并带有黄色问号图标的文件.
  6. 完成!

其它链接:
文档
命令列表
函数
变量

2 - 快捷键 & 热字串

什么是热键? 热键是一个发热的按键, 开个玩笑. 热键是用来触发某些动作的按键或组合按键.
什么是热字串? 热字串主要用于扩展你的缩写库(自动替换). 当然, 它也可以用来映射任何脚本动作.

这是一个热键:

^j::
   Send, My First Script
Return

这是一个热字串:

::ftw::Free the whales

这两个例子的区别在于热键CTRL & J将会触发一个事件而热字串会将你输入的"ftw"转换为"Free the whales".

"那么, 该如何创建一个热键?" 好问题. 热键是通过一对 :: 创建的. 按键名或组合按键名必须在 :: 左边. 代码则跟在后面, 以 Return 结束.

备注: 也有例外情况, 但要解释这些例外情况为时尚早, 所以我们不打算在向导页中介绍它.

esc::
   MsgBox Escape!!!!
Return

热字串在要触发的文本两边各有一对::. 替换后的文本在第二对 :: 的右边.

如上所述, 热字串也可以启动脚本动作. 和热键一样能干好多事, 真棒!

::btw::
   MsgBox You typed "btw".
Return

有一个好消息是: 你可以为每一个热键, 热字串, 标签等 写上多行的代码.

^j::
   MsgBox Wow!
   MsgBox this is
   Run, Notepad.exe
   winactivate, Untitled - Notepad
   WinWaitActive, Untitled - Notepad
   send, 7 lines{!}{enter}
   sendinput, inside the ctrl{+}j hotkey
Return

a. 键和神秘符号

你可能会问"我怎么知道 ^ 代表CTRL?". 好问题! 为了帮助你学习 ^ 和其它符号的意思, 注意看这个表:
符号描述
#Win(Windows 徽标键)
!Alt
^Control
+Shift
& 用于连接两个按键(含鼠标按键) 合并成一个自定义热键.

(想要获悉更详细的符号列表, 查看热键页)

此外, 这里有一个常用的按键列表:
按键列表

你可以通过在两个按键(除手柄键)之间 使用 & 来定义一个组合热键. 在下面的例子中, 你要按下 Numpad0, 再按下第二个键, 才能触发这个热键:

Numpad0 & Numpad1::
   MsgBox You pressed Numpad1 while holding down Numpad0.
Return

Numpad0 & Numpad2::
	Run Notepad
Return

如果你想知道热字串有没有什么酷酷的修饰符, 答案是有!
热字串的修饰符在第一对::之间, 例如:

:*:ftw::Free the whales

想要看更多关于热键和热字串修饰符的信息和实例, 请访问:
热键
热字串

b. 窗口特殊热键/热字串

有时候你也许想要热键或热字串只在某些特定窗口上工作(或禁用). 你可以使用#指令.

#IfWinActive
#IfWinExist

这些特殊的命令(技术上称为"指令")可以创建对上下文敏感的热键和热字串. 只需指定一个窗口标题. 但在某些情况下, 你可能需要指定一个窗口句柄, 组, 或类. 如果想深入了解这些高级内容, 点这里: #IfWinActive.

#IfWinActive Untitled - Notepad
#space::
   MsgBox You pressed Win+Spacebar in Notepad.
Return
#IfWinActive

想要关闭上下文敏感, 需要将 #ifwin 的所有参数设置成空. 例如:

; Notepad
#IfWinActive untitled - Notepad
!q::
   MsgBox, You pressed Alt and Q in Notepad.
Return
#IfWinActive

; Any window that isn't Untitled - Notepad
!q::
   MsgBox, You pressed Alt and Q in any window.
Return

当 #ifwin 命令关闭(或在脚本中从未使用过), 所有的热键和热字串对所有窗口生效.


当 #ifwin 命令开启: 它将影响脚本中 #ifwin 之间的热键和热字串.

; Notepad
#IfWinActive ahk_class Notepad
#space::
   MsgBox, You pressed Win+Spacebar in Notepad.
Return
::msg::You typed msg in Notepad
#IfWinActive

; MSPaint
#IfWinActive untitled - Paint
#space::
   MsgBox, You pressed Win+Spacebar in MSPaint!
Return
::msg::You typed msg in MSPaint!
#IfWinActive

想要了解更多信息以及相似的命令, 查看:
#IfWinActive

c. 一个文件包含多个热键/热字串

为了满足一些人的特殊想法, 我在这里声明一下: AutoHotkey 支持将 任意多 的热键和热字串放在一个文件中 不管是 1 个, 还是 3253 个(或者更多).

#i::
   run, http://www.google.com/
Return

^p::
   run, notepad.exe
Return

~j::
   send, ack
Return

:*:acheiv::achiev
::achievment::achievement
::acquaintence::acquaintance
:*:adquir::acquir
::aquisition::acquisition
:*:agravat::aggravat
:*:allign::align
::ameria::America

上面的代码是完全可以接受的. 多个热键, 多个热字串, 都包含在一个大的脚本文件里.

d. 示例

::btw::By the way                          ; 替换"btw"为"By the way", 当你按下结束符的时候.
:*:btw::By the way                         ; 替换"btw"为"By the way"而不需要按下结束符.

^n::                                       ; Ctrl & n 热键.
   run, notepad.exe                        ; 当你按下Ctrl & n, 将启动记事本.
Return                                     ; 热键内容结束, 这之后的内容将不会触发.

^b::                                       ; Ctrl & b 热键
   send, {ctrl down}c{ctrl up}             ; 复制选定的文本. 也可以使用^c, 但这种方法更加可靠.
   SendInput, [b]{ctrl down}v{ctrl up}[/b] ; 粘贴所复制的文本, 并在文本前后加上加粗标签.
Return                                     ; 热键内容结束, 这之后的内容将不会触发.

3 - 发送按键

现在你决定发送一些按键到一个程序中. 你可以使用 Send 命令. Send 表示发送按键, 模拟打字或按键操作.

在我们准备使用 Send 之前, 还有一些常见问题要注意:
就像其它热键一样, Send 也有一些特殊键. Send 的更多特殊键.
下面列出4个最常见的特殊键:


!: 发送 ALT 键击. 例如, Send This is text!a 将发送按键序列 "This is text" 并接着按下 ALT+a. : 在某些程序中 !A 和 !a 会产生不同的效果. 这是因为 !A 表示按下 ALT+SHIFT+A 而 !a 表示按下 ALT+a. 如果不确定, 请使用小写字母.

+: 发送 SHIFT 键击. 例如, Send +abC 会发送文本 "AbC", 而 Send !+a 会按下 ALT+SHIFT+a.

^: 发送 CONTROL (Ctrl) 键击. 例如, Send ^!a 会按下 CTRL+ALT+a, 而 Send ^{Home} 则发送 CONTROL+HOME. : 在某些程序中 ^A 和 ^a 会产生不同的效果. 这是由于 ^A 按了 CONTROL+SHIFT+A 而 ^a 按了 CONTROL+a. 如果不确定, 请使用小写字母.

#: 发送 WIN 键击, 因此 Send #e 会在按住 Windows 键时按下字母 "e".

下面一些章节主要介绍可以发送的特殊按键列表, 更多详细信息可以查看 send 页上的特殊按键列表.

注:
这个表并不适用于热键. 也就是说, 当你使用 CTRLENTER (或其它按键) 时, 不要使用 {}.


下面一个例子展示了创建一个热键时的错误做法:
; 当你创建热键时...
; 以下是错误的脚本
{LCtrl}::
   send, AutoHotkey
Return

; 以下是正确的脚本
LCtrl::
   send, AutoHotkey
Return

上面提到的大表展示了几乎所有 AHK 内置的特殊按键. 比如: {enter}{space}.
有人常常认为文档中用花括号仅仅是为了好玩. 而实际上花括号是必须的. 它将告诉 AutoHotkey {!} 表示 "感叹号" , 而不是要 "发送 Alt 键击". 所以要仔细查看 send 页上的特殊按键列表, 确保在合适的地方加上花括号.

; 注意到 {} 中的 ! 没? 这是因为, 如果没有 {}, AHK 将
; 发送 ALT 键击.
send, This text has been typed{!}
; 跟上面的例子类似. AHK 将会打印单词 "enter" ...
; ... 如果 enter 没有加上 {} 的话.
send, Multiple enter lines have enter been sent. ; 错误
send, Multiple{enter}lines have{enter}been sent. ; 正确
另一个常见的错误是, 人们认为当使用send命令时, 所有内容都需要 加上花括号. 这种想法是不对的. 如果不在特殊按键列表中, 没必要加花括号. 你 需要给普通字符, 数字加上括号, 甚至像.(句点)这些符号也不需要加 {}.
而且, 当你在使用 Send 命令时, 你可以一次性发送多个字符, 数字或符号, 没必要为每一个字符写上一条 Send 命令.
; 不要在普通单词或普通字母上加上花括号.
send, {a}       ; 错误
send, {b}       ; 错误
send, {c}       ; 错误
send, {a}{b}{c} ; 错误
send, {abc}     ; 错误
send, abc       ; 正确
想要表示按住或松开某个按键, 可以将这个键用花括号围起来, 同时加上 UP 或 DOWN.
; 下面这个例子表示按下一个键的时候再按下另一个键(或多个键).
; 如果其中一个方法不奏效, 试试另外的方法.
send, ^s                     ; 都表示发送 CTRL+s 键击
send, {ctrl down}s{ctrl up}  ; 都表示发送 CTRL+s 键击
Send, {ctrl down}c{ctrl up}
Send, {b down}{b up}
Send, {TAB down}{TAB up}
Send, {Up down}  ; 按下向上键.
Sleep, 1000      ; 保持1秒.
Send, {Up up}    ; 然后松开向上键.
现在你可能会想 "怎样才能让我在发送超长文本时保证文本的可读性?" 很简单. 只需要把超长文本分割成一系列短行. 具体的做法是: 在 send 后面增加一个新行, 这个新行只包含一个左圆括号, 然后你可以分多行写下要发送的内容. 最后另起一行, 用右圆括号结束. 想了解更多信息, 请查看 Continuation 页.
send,
(
Line 1
Line 2
Apples are a fruit.
)

备注: Send命令有多种形式. 每种形式有其特性. 如果一种形式的 send 命令不能满足你的需要, 可以试试另一种形式. 只需要将"send"命令替换成"sendPlay"或其它形式.
下面列出了一些常见的发送文本的命令:
Send
SendRaw
SendInput
SendPlay
SendEvent
想要了解每一个命令的详细内容, 请点这里.

a. 游戏

非常重要!
很多游戏, 尤其是新出的游戏, 都有反作弊系统, 例如 GameGuard, Hackshield, PunkBuster 等. 如果一个游戏的反作弊系统导致你的热键, 热字串和 send 命令统统失效, 就说明你不太走运.
且不说绕开反作弊系统是违反游戏规定的, 绕开反作弊本身也不太容易实现. 有一些方法也许能提高在某些游戏中使用热键的可能性, 但没人能打包票一定能行 . 所以, 尽可能尝试所有 你能想到的办法, 不要轻易放弃.

还有一个关于 DirectX 的问题要注意. 当你在 DirectX 游戏中使用 AutoHotkey 碰到问题时, 试试下面这个办法. 如果程序允许, 尝试用窗口模式运行游戏. 这样做能够解决一些 DirectX 问题.
当你使用 pixel 或 image 命令时, 你可能会碰到更多关于 DirectX 的问题. 画面颜色可能会变成黑色(0x000000), 不管你设置的是什么颜色. 这是一个非常棘手的问题. 如果程序允许, 试试用窗口模式运行程序.

没有万能办法能确保 AutoHotkey 能运行在所有程序里. 如果你试了所有的办法还是不行, 也许 AutoHotkey 暂时无法满足你的需要.


下面这个问题选自常见问题页:
有些游戏专用使用 DirectInput 模式. 带来的负面影响是它们可能会忽略所有的模拟键盘和鼠标操作. 为了解决这个问题, 可以从下面这些方法中选一个来试试(或者同时运用这些方法):
  • 通过下面 3 个方法使用 SendPlay命令: 1) 直接使用 SendPlay 命令; 2) 用 SendMode 切换到 Play 模式; 和/或 3) 将热字串发送键击的选项设置为 SP(Sendplay) 模式.
  • 使用 SetKeyDelay 增加键击延时. 例如:
  • SetKeyDelay, 0, 50
  • SetKeyDelay, 150, 150, Play
  • 使用 ControlSend 命令, 在其它 Send 模式都失效情况下使用, 也许它能起作用.

4 - 运行程序和网页

想要运行画图, 计算器, 脚本.ahk 等程序或要打开一个文件夹, 你可以使用 Run 命令. 你还可以用这个命令打开一个网址, 比如打开 https://autohotkey.com/ . 如果你想打开一个已经安装好的程序, 也很简单, 就像这样:
; 运行一个程序. 注: 大部分的程序可能需要完整路径.
Run, %A_ProgramFiles%\Some_Program\Program.exe

; 打开一个网址
Run, https://autohotkey.com
还有其它一些高级特性, 比如命令行参数和 CLSID(Windows 类标识符).
如果你想了解更多有关 Run 命令的信息, 可以点击 Run 命令页.
下面是一些关于 Run 命令的示例:
; 一些程序并不需要完整路径, 如 Windows 标准程序.
Run, Notepad.exe
Run, MsPaint.exe

; 使用 AHK 内置参数来打开"我的文档"
Run, %A_MyDocuments%

; 打开一些网页 
Run, https://autohotkey.com
Run, http://www.google.com

想深入了解更多信息和示例, 可以点击:
Run 命令页.

5 - 命令和函数

AutoHotkey 有两个重要的工具供开发者使用:
命令和函数

有用的链接:
所用命令列表: commands/index.htm
所有内置函数列表: Functions.htm#BuiltIn

命令

你可以通过语法来分辨命令和函数. 命令后面的参数无需使用圆括号, 而函数需要使用. 所以命令就像下面这样:
Command, 参数1, 参数2, 参数3

当你使用命令时, 不能将几条命令放在同一行( ifEqual 除外).
也不能将一个命令作为参数插入到另一个命令.

Msgbox, Hello Run, Notepad.exe   ; 错误
Msgbox, Hello, Run, Notepad.exe  ; 错误

Msgbox, Hello      ; 正确
Run, Notepad.exe
跟函数不同的是, 命令使用"传统语法". 也就是说: 当你使用参数时, 你需要在参数的前后加上%, 比如%variable%. 而文本和数字则不需要加双引号. This is some text. 另外, 不像函数, 命令的参数不能进行运算.

注:如果确实想要在参数中进行运算, 可以使用单个 % 来强制定义一个表达式, 但我们这里将不涉及这些内容.

函数

就像上文提到的, 函数不同于命令的地方在于函数需要使用圆括号. 一个典型的函数就像下面一样:
Function(参数1, 参数2, 参数3)

与命令相比, 函数有下面几个主要差异:
  1. 你可以使用运算.
    -- SubStr(37*12, 1, 2)
    -- SubStr(A_Hour-12, 2)
  2. 参数前后不需要加上百分号.
    -- SubStr(A_Now, 7, 2)
  3. 函数可以嵌套另一个函数.
    -- SubStr(A_AHKPath, inStr(A_AHKPath, "AutoHotkey"))
  4. 文本前后需要加上双引号.
    -- SubStr("I'm scripting, awesome!", 16)
函数通常会返回一个值, 这一点与命令也不一样. 命令需要设置 OutputVar 参数来保存结果, 而函数不需要. 要将函数的返回值分配给一个变量, 只需要这样做:
MyVariable:=Function(Parameters)
						MyVariable:=SubStr("I'm scripting, awesome!", 16)

这不是赋值的唯一方法, 但这是最常用的. 通过将函数(这个例子中的函数是 SubStr(...))的内容写在 := 右边这种方式, 将函数的返回值分配给变量 MyVariable.
函数的更多信息

简言之:
; 这些是命令
Msgbox, This is some text.
StringReplace, Output, Input, AutoHotKey, AutoHotkey, ALL
SendInput, This is awesome{!}{!}{!}

; 这些是函数
SubStr("I'm scripting, awesome!", 16)
FileExist(VariableContainingPath)
Output:=SubStr("I'm scripting, awesome!", 16)

a. 代码块

代码块就是用一对花括号 ({}) 包围起来的一段代码, AutoHotkey 以此来判断这些代码是一个整体. 代码块经常用于 IfLoop 中. 如果不使用花括号, 整块代码中只有第一行代码能被执行.

下面的代码中, 当 var 等于 5 时, 所有行都会执行.
if (var=5)
{
   MsgBox, var equals %var%!!
   Exitapp
}

下面的代码中, 当 var 等于 5 时, 才能显示消息框. 但不管 var 是否等于 5, 脚本都会终止.
if (var=5)
   MsgBox, var equals %var%!!
   Exitapp

If 后面只有一行与它有关的代码, 下面的例子做了一个非常好的代码编写示范. 它实现的功能跟上面的代码一样, 但是我将第二行代码进行了缩进排版, 这样写让我们很容易就知道这行代码是跟 If 相关的.
if (var=5)
   MsgBox, var equals %var%!!
MsgBox, We are now 'outside' the if. We did not need {}'s since there was only 1 line below it.

6 - 变量

变量就好像一个包含信息的便利贴. 它可以用于函数, 命令或数学表达式中, 充当存储文本, 数字, 数据的作用. 如果没有变量, 程序和脚本将会非常乏味.


给变量赋值有很多方法, 我们将会讨论最常见的几种方法. 请特别留意等号(=).
  1. variable=text
    这是给变量赋值最简单的方法, 也是传统的赋值方法. 只需要在等号后面输入文本就行了.
  2. variable=%variable2%
    和上面的方法类似, 只是你将一个变量所对应的值赋给了另一个变量.
  3. variable:="text"
    这里通过一个表达式给变量赋值, 因为 = 前面有一个 :. 任何文本都须包含在双引号内.
  4. variable:=variable2
    如果用表达式的方法赋值, 变量不需要使用%.
  5. variable:=6+8/3*2-sqrt(9)
    你还可以在表达式中使用运算.
第1条和第2条可以合并, 就像:var=%var2% some text %var3%.
第3, 4, 5条也可以合并, 就像:var:="The value of 5+ " Variable " is: " 5+Variable

我们把(=)前面的符号称为赋值运算符, 一般都是表达式. 例如:= += -= .= 等等.

a. 什么时候使用百分号

关于变量一个最常见的问题是什么时候使用百分号(%). 希望下面这些内容能够解决一些困扰.

什么时候使用百分号:
  1. 当你使用命令时(见前文), 需要使用百分号.
    -- 输入变量和输出变量除外.
  2. 当你使用传统模式(等号前面没有其它符号)给一个变量赋值时, 需要使用百分号.
什么时候不要使用百分号:
  1. 参数中的输入或输出变量, 不要使用百分号, 例如: StringLen, OutputVar, InputVar
  2. 赋值时左边的变量, 不要使用百分号, 例如: Var = 123abc
  3. 在If语句中, 传统赋值方式(不包括表达式)中左边的变量, 不要使用百分号: If Var1 < %Var2%
  4. 在表达式中的变量, 不要使用百分号:
    If (Var1 != Var2)
       Var1 := Var2 + 100
    

b. 获取用户输入

有时候你想让用户来选择某些值. 这可以有很多种方法, 但其中最简单的办法就是使用 Inputbox 命令. 下面的例子展示了如何向用户提出一堆问题并根据用户的输入完成一些事情.
InputBox, OutputVar, Question 1, What is your first name?
if (OutputVar="Bill")
   MsgBox, That's an awesome name, %OutputVar%.

InputBox, OutputVar2, Question 2, Do you like AutoHotkey?
if (OutputVar2="yes")
   MsgBox, Thank you for answering %OutputVar2%`, %OutputVar%! We will become great friends.
else
   MsgBox, %OutputVar%`, That makes me sad.

c. 其它示例

MsgBox, 4, , Would you like to continue?
IfMsgBox, No
   Return                ; 如果选择 No, 脚本将会终止.
MsgBox You pressed YES.  ; 用户选择了YES.
; 下面一些例子展示了什么时候该使用百分号, 什么时候不该.
Variable=text                      ; 采用"传统"方式将一个文本赋值给一个变量.
VariableNumber:=6                  ; 采用"表达式"方式将一个数字赋值给一个变量.
Variable2=%Variable%               ; 采用"传统"方式将一个变量的值赋值给一个变量.
Variable3:=Variable                ; 采用"表达式"方式将一个变量的值赋值给一个变量.
Variable4.=Variable                ; 采用"表达式"方式将一个变量的值添加到另一个变量的后面.
Variable5+=VariableNumber          ; 采用"表达式"方式, 将两个变量的值相加, 然后把结果保存到第一个变量中.
Variable5-=VariableNumber          ; 采用"表达式"方式, 将第一个变量的值减去第二个变量的, 然后把结果保存到第一个变量中.
Variable6:=SubStr(Variable, 2, 2)  ; 在函数中的变量. 它们永远都是表达式.
Variable7=%Variable% Text          ; 采用"传统"方式, 将一个变量的值加上一个文本然后赋值给另一个变量.
Variable8:=Variable " Text"        ; 采用"表达式"方式, 将一个变量的值加上一个文本然后赋值给另一个变量.
MsgBox, %Variable%                 ; 在命令中的变量. 
StringSplit, Variable, Variable, x ; 在命令中的变量, 但是它们作为输入或输出变量.
if (VariableNumber=6)              ; 如果IF采用括号的方式, 那么括号中的内容就是一个表达式, 所以不需要百分号.
If (Variable != VariableNumber)    ; 如果IF采用括号的方式, 那么括号中的内容就是一个表达式, 所以不需要百分号.
if VariableNumber=6                ; 如果 IF 不使用括号, 那么 IF 后面的内容将采用传统方式. 不过, 只有赋值语句"右边"的变量需要百分号. 
If Var1 < %Var2%                   ; 如果 IF 不使用括号, 那么 IF 后面的内容将采用传统方式. 不过, 只有赋值语句"右边"的变量需要百分号. 

7 - 对象

对象其实是一种更具效率的数据组织方式, 它有很多用处. 有时候对象被当作数组, 在这重点声明一下所有的数组都是对象. 我们可以根据对象的使用范围区分不同的对象, 但这些对象都有同样的特性.


一个对象可以看成是变量的集合. 变量的名字叫做"键", 变量的内容叫做"值".

当你听到人们把一个对象叫做 数组索引数组, 说明这个对象的键是从 1 开始递增的连续数字.
当你听到人们把一个对象叫做 关联数组, 说明这个对象的键要么使用字符串 (或文本), 要么使用不连续的数字. 有时可能是两者的混合也有可能是数列!

一个对象的键和值没有什么限制, 它们甚至可以是另一个数组!
如果一个对象的值也是数组, 我们把它叫做 嵌套数组, 这个我们以后再解释.
有很多地方需要使用数组, 比如:
  1. 当你需要描述一组有序列表时, 比如杂货店列表(这种情况最好使用索引数组)
  2. 当你需要描述一个图形方格时, 比如一个棋盘游戏(这种情况最好使用嵌套对象)
  3. 当你需要描述一组事物而每样事物都有自己的名字时, 比如描述水果的特性(这种情况最好使用关联数组)

a. 创建对象

我们有很多方法可以创建对象, 下面介绍最常用的几种方法:
  1. MyObject := ["one", "two", "three", 17]
    采用方括号的形式. 我们这里介绍所谓的"索引数组"对象. 索引数组表示这个对象包含一组数据, 这些数据的索引号从1开始连续递增. 在这个例子中, 值 "one" 存储在对应键 1 (又叫做索引号1), 值 17 存储在对应键 4 (又叫做索引号4).
  2. Banana := {"Shape": "Elongated", "Color": "Yellow", "Taste": "Delicious", "Price": 3}
    采用花括号形式. 这里介绍所谓的"关联数组"对象. 关联数组表示这个对象包括一组数据, 每个数据都有自己的名字. 在这个例子中, 值 "yellow" 对应键 "color". 同样的, 值 3 对应键 "Price".
  3. MyObject := Array("one", "two", "three", 17)
    采用"数组"创建函数. 这种方式跟方括号形式一样, 区别仅仅是采用了函数的形式.
  4. Banana := Object("Shape", "Elongated", "Color", "Yellow", "Taste", "Delicious", "Price", 3)
    采用对象创建函数. 这种方式跟花括号形式一样, 区别仅仅是采用了函数的形式.
请注意, 所有这些方法都创建了同一样东西(也就是对象), 区别仅仅是对象的键不一样.

b. 使用对象

使用对象有很多方式, 包括取出对象值, 设置对象值, 新增值等等.

设置对象值:
设置对象的值跟设置变量的值一样简单.
你需要做的是将方括号或句点表示法(就像下文取对象值部分那样)放在表达式赋值符号:=的左边.

例如:
Banana.Consistency := "Mushy"
Banana["Pickled"] := True ; 这个香蕉烂透了.呃...

取出对象值:
  1. Value := Banana["Color"]
    方括号表示法. 允许你使用表达式作为键从对象中获取值. 在这个例子中, 我使用表达式 "Color", 不要惊讶我仅使用键 Color 就会得到一个包含单词 "Yellow" 的值, 因为 "Yellow" 是我们事先 (在上一节) 就与键 Color 建立关联的.
  2. Value := Banana.Color
    句点表示法. 仅允许使用原样的字串作为键名. 在句点表示法中键名不能使用变量.

新增键和值:
  1. 直接新增值
    想要直接新增一对键和值, 只需要设置一个跟现有的键名不一样的新键就行了. 例如:
    MyObject.NewKey := "Shiny"
    MyObject["NewerKey"] := 3.1415
  2. 插入值
    其他新增键值到对象的方法是使用以下方法之一.

    MyObject.InsertAt(Index, Value1, Value2, Value3...)
    Index 为任意整数键. 这样将导致所有较高的整数键的索引增加插入值的数量, 即使是空缺的也一样(例如, 假设只有键 1 和 100 存在, 当插入一个值到第 50 个键的位置时, 将导致原来位于 100 的键的索引变成 101).

    MyObject.Push(Value1, Value2, Value3...)
    "追加" 值到 MyObject 数组的尾部. 换句话说, 它将插入的值放在最高整数键加一的位置.

移除键和值:
  1. 用空白填充值.
    最简单的删除值的方法就是填充为空. 你可以将其赋值为 "" (两个连续的双引号), 也就是常说的 空字符串. 这不会删除键, 但它会将键设为类似从未赋值一样.
    可以通过使用 HasKey 方法得知键依然存在, 而且键也会出现在 for 循环中. (我们一会再解释 for循环)
  2. 移除键
    有几种同时删除键 值的办法, 它们是:
    1. RemovedValue := MyObject.Delete(AnyKey)
      之前位于MyObject[AnyKey]的值将储存到 RemovedValue.
    2. NumberOfRemovedKeys := MyObject.Delete(FirstKey, LastKey)
      如果你使用这种形式, 将删除一组连续的键和对应的值, 范围从 FirstKey 开始, 到 LastKey 结束.

      然后将删除的数量返回给变量 NumberOfRemovedKeys. 当键与键之间有空隙, 返回值就很有用, 因为它可以告诉我们究竟删除了几个键.(比如, 你想要删除一组键, 从 1 到 4, 但其实这一组键中缺少键 2, 所以你一共只删除了 3 个键, 那么 NumberOfRemovedKeys 的值就是 3)
    3. MyObject.Pop()
      这种形式将删除最大的整数键, 同时返回这个键对应的值. 而且这种形式不会影响其它键的索引顺序.
    4. RemovedValue := MyObject.RemoveAt(Index)
      NumberOfRemovedKeys := MyObject.RemoveAt(Index, Length)
      这样将会移除从 IndexIndex + Length - 1 (包含) 之间的所有键. 如果 Length 省略则默认值为 1. 移除这些键之后, 更高 数字或整数的键将向下填充空缺, 所以, 如果有个值位于 Index + Length 那么它现在的位置就是 Index. 这很像 InsertAt 方法操作多个指定值的操作.

8 - 其它有用的东西

亲爱的朋友, 当你阅读到这里, 说明快要结束我们这段旅程了. 我希望你有所收获. 最后, 我将告诉你一些我认为你可能有用的东西. 希望你过的愉快!

a. 神秘的 [ ]

在帮助文档中, 你可能会发现有两个符号([]) 经常出现在几乎每一页开头的黄色代码框中. 方括号中的内容代表可选的. 也就是说, 如果你不需要这些参数你完全可以不管它. 不过要强调一点, 当你在写代码时, 千万不要把 [ ] 也写上了.

例如, 在 ControlGetText 命令页上, 你可能会看到这段代码(请忽略颜色):
ControlGetText, OutputVar [, Control, WinTitle, WinText, ExcludeTitle, ExcludeText]

所以你可以简单的写成这样:
ControlGetText, OutputVar

或者再加上一些参数:
ControlGetText, OutputVar, Control, WinTitle

如果你想只使用参数 ExcludeTitle 而不想使用参数 WinText 或 WinTitle, 怎么办? 很简单!
ControlGetText, OutputVar, Control,,, ExcludeTitle

请注意, 你不能忽略参数, 只是需要将它们的位置留空.
如果你像下面这样忽略"WinTitle, WinText", 将会产生错误:
ControlGetText, OutputVar, Control, ExcludeTitle
语句非法.
ControlGetText, OutputVar, Control,,, ExcludeTitle

b. 查找你的AHK版本

你可以运行下面的程序来获取你的 AHK 版本信息:
MsgBox, %A_AHKVersion%
你也可以到开始菜单或安装路径下的帮助文档里面去找.

c. 尝试与错误

尝试和错误是非常普遍而高效的学习方法. 与动不动就问这问那相比, 有时候花点时间(也许是长年累月)亲手尝试可能会学的更快.

如果你在尝试新东西的过程碰到错误, 不要紧, 就从解决这个错误开始. 尝试解决这个错误, 一次不行就两次. 多次尝试后还是解决不了, 可以打开帮助文件学习哪些能做哪些不能做, 然后修改你的代码再试试. 试试, 失败, 试试, 失败, 试试, 试试, 再试试, 失败, 失败, 成功!

这也是很多大师们的学习经历. 不过也不要害怕提问, 我们不会咬人(至少不会咬的太狠). 学习总需要时间慢慢积累, 大师也不是一天练成的.

"如果一开始就碰到失败, 就是要不停的尝试, 再尝试." - Hickson, William E.

d. 缩进

缩进这个事非常重要! 你的代码没有它也能正常运行, 可是如果没有缩进会让阅读代码变成一件非常痛苦的事. 也许一小段代码(少于 25 行)不用缩进也没有太大关系, 但是代码一旦增多, 缩进就非常有必要. 所以, 学习使用缩进越快越好.
缩进没有固定的风格, 但最好保持一种风格.
"什么是缩进?" 你可能会问? 简单的说就是在代码和页面边界保留一段距离, 这样可以区分这一段代码是属于哪一段代码. 有些人习惯使用 3, 4 个空格或 1 个 tab 来表示缩进, 每一级用一次缩进.

不用缩进:
if (car="old")
{
msgbox, the car is really old
if (wheels="flat")
{
msgbox, this car is not safe to drive.
Return
}
else
{
msgbox, Be careful! This old car will be dangerous to drive.
}
}
else
{
msgbox, My`, what a shiny new vehicle you have there.
}
使用缩进:
if (car="old")
{
   msgbox, the car is really old
   if (wheels="flat")
   {
      msgbox, this car is not safe to drive.
      Return
   }
   else
   {
      msgbox, Be careful! This old car will be dangerous to drive.
   }
}
else
{
   msgbox, My`, what a shiny new vehicle you have there.
}
关于缩进, 维基百科上有很多风格示例. 建议选一种你喜欢的风格来学习.
http://en.wikipedia.org/wiki/Indent_style

e. 寻求帮助

在你提问之前, 最好自己先研究一下或者自己先动手试着写代码. 如果自己实在得不到满意的结果, 请往下看.
  • 不要害怕提问, 即使是世界上最聪明的人也需要别人的帮助.
  • 不要害怕给别人看你写的代码, 就算你觉得代码有点弱智.
  • 将所有你尝试的代码都贴出来.
  • 假设其他人 都是门外汉. 把你掌握的所有信息都教给我们这些门外汉. 帮助我们就是帮助你自己.
  • 保持耐心.
  • 保持友善.
  • 保持开放.
  • 保持友善.
  • 祝你好运.
如果没有人回答你的问题, 至少等一天再提问. 我们乐于助人, 但是我们也是在自己的空余时间里免费提供帮助. 也许我们正在工作, 睡觉, 游戏或者太忙了.
在等待帮助时, 你也可以试着自己动手解决. 独立解决问题的感觉相当不错.

f. 其它链接

常见问题(FAQ)