# 脚本

相关话题:

- 程序的使用: 在一般情况下, 如何使用 AutoHotkey.
- 概念和约定: AutoHotkey 使用的各种概念的通俗解释.
- 脚本语言: 有关语法的具体详细信息(如何编写脚本).

## 目录

- 简介
- 脚本顶部(自动执行段): 当脚本启动时, 这部分会自动执行.
- 把过长的行分割成一系列短行: 这样可以提高脚本的可读性和可维护性.
- 把脚本转换成 EXE(Ahk2Exe): 把 .ahk 脚本转换成可以在任何 PC 上运行的 .exe 文件.
- 向脚本传递命令行参数: %1%, %2% 等变量包含了传入的参数.
- 脚本文件代码页: 在脚本中可靠地使用非 ASCII 字符.
- 调试脚本: 如何找出无法正常工作的脚本中的问题.

## 简介

每个脚本都是纯文本文件, 其中包含了可由程序(AutoHotkey.exe) 执行的文本行. 脚本中还可以包含热键和热字串或者甚至完全由它们组成. 不过, 在不包含热键和热字串时, 脚本会在启动后从上往下按顺序执行其中的命令.

程序会把脚本逐行加载到内存中, 每行最多可以包含 16,383 个字符. 在加载过程中, 脚本会被优化和检查. 将列出所有的语法错误, 更正它们后脚本才能运行.

## 脚本顶部(自动执行段)

脚本加载完成后, 它会从顶行开始执行, 直到遇到 Return, Exit, 热键/热字串标签或脚本的底部(无论最先遇到哪个). 脚本的这个顶端部分被称为 自动执行段.

注意: 虽然脚本的 第一个 热键/热字串标签与 return(返回) 的效果相同, 但其他热键和标签却没有.

如果脚本不是持续运行的, 它将在自动执行部分完成后终止. 否则, 它会以空闲状态持续运行, 从而对诸如热键, 热字串, GUI 事件, 自定义菜单项和计时器等事件做出响应. 如果一个脚本包含了热键, 热字串, OnMessage() 或 GUI, 以及一些其他情况, 它就会自动变为持续运行的. 也可以用 #Persistent 指令来显式地使脚本持续运行.

每个由热键, 热字串, 菜单项, GUI 事件或计时器启动的线程都以在自动执行段设置的下列属性值作为默认值开始. 如果没有设置, 则使用标准的默认值(与下面每个页面中注明的一样): AutoTrim, CoordMode, Critical, DetectHiddenText, DetectHiddenWindows, FileEncoding, ListLines, SendLevel, SendMode, SetBatchLines, SetControlDelay, SetDefaultMouseSpeed, SetFormat, SetKeyDelay, SetMouseDelay, SetRegView, SetStoreCapsLockMode, SetTitleMatchMode, SetWinDelay, StringCaseSense 和 Thread.

如果自动执行段执行了很长时间才结束(或永不结束), 上面这些设置的默认值将在 100 毫秒后自动生效. 当自动执行段最终结束(如果可能) 时, 在自动执行段末尾生效的那些设置才更新为默认值. 因此, 通常最好在包含热键, 热字串, 计时器或自定义菜单项的脚本顶部设置自己想要的默认值. 还要注意, 每个线程会保存它自己的上述设置的集合. 在一个线程中对这些设置的改变不会影响其他线程.

## 把过长的行分割成一系列短行

可以把过长的行分割成一系列较短的行来提高可读性和可维护性. 这样不会降低脚本的执行速度, 因为在脚本启动时这些短行会在内存中合并起来.

方法 #1: 以 "and", "or", ||, &&, 逗号或句点开始的行会自动合并到其前一行(在 v1.0.46+, 除了 ++ 和 -- 外其他所有的表达式运算符开头的行也会如此). 在下面的例子中, 第二行会自动附加到首行, 因为它以逗号开始:

    FileAppend, This is the text to append.`n   ; 这里可以使用注释.
        , %A_ProgramFiles%\SomeApplication\LogFile.txt  ; 注释.

同样地, 下列几行也会合并成单行, 因为最后两行以 "and" 或 "or" 开始:

    if (Color = "Red" or Color = "Green"  or Color = "Blue"   ; 注释.
        or Color = "Black" or Color = "Gray" or Color = "White")   ; 注释.
        and ProductIsAvailableInColor(Product, Color)   ; 注释.

三元运算符也是个不错的选择:

    ProductIsAvailable := (Color = "Red")
        ? false  ; 我们没有任何红色产品, 所以不用那么麻烦去调用函数.
        : ProductIsAvailableInColor(Product, Color)

尽管在上面的例子中缩进不是必须的, 但它可以显示出哪些行属于上一行从而提高代码清晰度. 并且, 可以不必在单词 "AND" 和 "OR" 开始的行加上额外的空格; 程序会自动处理这些. 最后, 可以在上面例子中任意行的末尾或行与行之间添加空行或注释.

方法 #2: 这种方法用于合并大量的行或不适合方法 #1 处理的行. 此方法对自动替换热字串特别有用, 但它也可以用于命令或表达式中. 例如:

    ; 示例 #1:
    Var =
    (
    一行文本.
    默认情况下, 前一行和这一行之间的硬回车(Enter) 将被存储为换行符(`n).
        默认情况下, 这一行左边的空格也会被存储(制表符也是如此).
    默认情况下, 变量引用如 %Var% 会被解析为该变量的内容.
    )

    ; 示例 #2 - 表达式语法(推荐):
    Var := "
    (
    和上面一样, 除了变量引用如 %Var% 不会被解析.
    取而代之的是, 以如下方式指定变量:" Var "
    )"

    ; 示例 #3:
    FileAppend,  ; 此时逗号是不能缺少的.
    (
    第 1 行文本.
    第 2 行文本. 默认情况下, 行与行之间有一个换行符(`n).
    ), C:\My File.txt

在上面的例子中, 几行代码被一对圆括号包围起来. 这被称为 延续片段. 注意在底行的闭括号后面跟着 FileAppend 的最后一个参数. 这种做法是可选的; 这样做是为了确保逗号被视为参数分隔符而不是原义的逗号.

通过在延续片段的括号右侧包含一个或多个下列选项, 可以覆盖它的默认特性. 如果含有多个选项, 那么选项之间使用空格分开. 例如: `( LTrim Join| %`.

Join: 指定行与行之间连接的方式. 如果省略此选项, 那么除最后一行外的其他行后面都会跟一个换行符(`n). 如果指定单词 Join 自身, 则行与行之间直接连接而不添加任何字符. 或者在单词 Join 后可以紧跟着多达 15 个字符. 例如, `Join`s` 会在除最后一行外的每行末尾插入一个空格("`s"表示原义的空格, 这是一个只能被 Join 识别的特殊转义序列). 另一个例子是 `Join`r`n`, 它会在行与行之间插入 CR+LF. 同样地, `Join|` 会在行之间插入管道符. 要让延续片段的最后一行也以连接字符串结尾, 需要在它的闭括号上一行添加一个空行.

已知限制: 如果单词 Join 后面紧跟一个冒号, 那么冒号不能是这一行的最后一个. 因为 `(Join:` 将被当做一个名为 Join 的标签对待, 所以不支持 `(LTrim Join:` 这种写法, 而 `(Join: C` 这种写法没有问题.

LTrim: 删除每行开头的空格和 tab. 主要用来允许延续片段使用缩进. 此外, 通过在一行中指定 `#LTrim` 自身可以为多个延续片段打开此选项. `#LTrim` 跟位置有关: 会影响它下面的所有延续片段. 使用 `#LTrim Off` 可以关闭此设置.

RTrim0(RTrim 后跟着一个零): 关闭自动删除每行末尾的空格和 tab 的设置.

Comments(或 Comment 或 Com 或 C) [v1.0.45.03+]: 允许在延续片段中使用分号注释(但不支持 `/*..*/`). 这样的注释(以及它们左边的空格和 tab) 会在连接时完全被忽略而不是当成原义文本处理. 每个注释可以放在一行的右侧或单独一行.

%(百分号): 把百分号视为原义字符而不是变量引用. 这样就不需要把每个百分号转义成原义字符. 在百分号已经为原义的地方不需要使用此选项, 例如, 自动替换热字串.

,(逗号): 把逗号作为分隔符而不是原义逗号. 这个非常少用的选项只有在命令的参数之间才需要, 因为在函数调用中逗号的类型没有影响. 并且, 此选项只会转换那些真正的分隔参数的逗号. 换句话说, 一旦到了命令的最后一个参数(或者命令没有参数), 那么会忽略此选项而把后续的逗号当成原义逗号.

`(重音符): 把每个反引号视为原义字符而不是转义符. 此选项同时也避免了需要分别对逗号和百分号进行明确地转义. 此外, 它还阻止对任何特定的转义序列例如 `r 和 `t 进行转义.

) [v1.1.01+]: 如果一个右括号出现在延续代码的参数中(除了作为 Join 的选项参数), 那么这一行将重新解释为一个表达式, 而不是作为一段延续代码的开始. 这将避免类似 `(x.y)[z]()` 的表达式需要对左括号进行转义.

备注

当没有指定重音符(`) 选项时, 支持在延续片段中使用转义序列, 例如 `n(换行) 和 `t(tab).

没有使用注释选项时, 不支持在延续片段中使用分号和 /*..*/ 添加注释, 因为它们会被视为原义文本. 不过, 可以在片段的底行和顶行添加注释. 例如:

    FileAppend,   ; 注释.
    ; 注释.
    ( LTrim Join    ; 注释.
         ; This is not a comment; it is literal. Include the word Comments in the line above to make it a comment.
    ), C:\File.txt   ; 注释.

由上面可知, 延续片段中的分号不需要进行转义.

使用延续片段无法生成总长度超过 16,383 字符的行(如果尝试这么做, 那么程序在启动时会弹出警告). 解决此问题的一种方法是把一系列内容连接到变量中. 例如:

    Var := "
    (
    ...
    )"
    Var .= "`n  ; 通过另一个延续片段添加更多文本到此变量中.
    (
    ...
    )"
    FileAppend, %Var%, C:\My File.txt

因为闭括号表示延续片段的结束, 所以要让某一行以原义的闭括号开头, 需要在其前面加上重音符/反引号: ``)`.

一个延续片段后面可以紧跟着包含另一个延续片段的开括号的一行. 这样使得上面提到的选项可以在创建单行的过程中进行改变.

不支持通过 #Include 的方法把延续片段各部分连接起来.

## 把脚本转换成 EXE(Ahk2Exe)

程序中已包含了脚本编译器(感谢 fincs 提供, 以及 TAC109 的补充).

脚本编译完成后, 就成了一个独立的可执行文件; 也就是说, 可以在没有安装 AutoHotkey 的机器上运行. 编译过程中会创建一个包含下列文件的可执行文件: AutoHotkey 解释器, 脚本, 脚本包含的任何文件以及通过 FileInstall 命令内嵌的任何文件. [v1.1.33+]: 可以使用编译器指令包含其他文件.

使用 Ahk2Exe 有下列几种方式:

- GUI 界面: 运行开始菜单中的 "Convert .ahk to .exe" 菜单项. (调用 GUI 后, 在窗口显示之前可能会有一个停顿; 有关详情, 请参阅背景信息.)

- 右键点击: 在资源管理器中, 在任何一个 .ahk 上右键点击并选择 "Compile Script"(只有在安装 AutoHotkey 时选择了脚本编译器时才可用). 这样创建了与脚本具有相同的主文件名的 EXE 文件, 它会在稍后出现在相同目录中. 注: EXE 文件的生成使用的是 上述方法 #1 中最后保存的相同的自定义图标, .bin 文件和压缩设置, 或者由脚本中任何相关的编译器指令指定.

- 命令行: 编译器可以通过以下参数从命令行运行. 如果使用了任何的命令行参数, 除非使用的是 `/gui`, 否则脚本会立即被编译. 除了必须的 `/gui` 或 `/in` 参数外, 所有参数都是可选的.

  参数对 | 意义
  --- | ---
  /in script_name | 要编译的脚本的路径和名称. 如果使用任何其他参数, 这是强制性的, 除非使用了 `/gui`.
  /out exe_name | 要创建的 .exe 的路径\名称. 默认值是输入文件的 directory\base_name 加上 .exe 的扩展名, 或脚本中任何相关的编译器指令.
  /icon icon_name | 要使用的图标文件. 默认是 GUI 界面中最后保存的图标, 或脚本中任何 SetMainIcon 编译器指令.
  /base file_name | [v1.1.33.10+]: 要使用的基文件(一个 .bin 文件或 [v1.1.34+] 中可以是 .exe 文件). 默认是 GUI 界面中保存的最后一个基文件名, 或脚本中的任何 Base 编译器指令.
  /resourceid name | [v1.1.34+]: 指定一个非标准的资源 ID, 用于使用 .exe 基文件编译的主脚本(请参阅嵌入式脚本). 数字型资源 ID 应该由一个哈希符号(#) 和一个十进制数字组成. 默认是 #1, 或脚本中的任何 ResourceID 编译器指令.
  /cp codepage | [v1.1.23.01+]: 覆盖用于读取脚本文件的默认代码页. 关于可能的值列表, 请参阅代码页标识符. 注意, Unicode 脚本应该以字节顺序标记(BOM) 开始, 因此不需要使用这个参数.
  /compress n | [v1.1.33+]: 是否 Compress(压缩) exe 文件? 0 = 不压缩, 1 = 使用 MPRESS(如果存在), 2 = 使用 UPX(如果存在). 默认值是 GUI 界面中最后保存的设置.
  /gui | [v1.1.33+]: 显示 GUI 而不是立即编译. 其他参数可以用来覆盖上次在 GUI 中保存的设置. `/in` 在这种情况下是可选的.
  /silent [verbose] | [v1.1.33.10+]: 禁用所有的消息框并将错误输出到标准错误流(stderr); 如果 stderr 失败, 则输出到标准输出流(stdout). 其他信息也会输出到 stdout. 可以选择输入 `verbose` 这个单词, 把状态信息也输出到 stdout.
  过时的: /ahk file_name | [v1.1.33+]: 编译脚本时使用的 AutoHotkey.exe 的 path\name.
  过时的: /mpress 0or1 | 使用 MPRESS 压缩 exe 文件? 0 = 不压缩, 1 = 压缩. 默认值是 GUI 界面中使用的最后一个设置.
  过时的: /bin file_name | 要使用的 .bin 文件. 默认值是 GUI 界面中最后保存的 .bin 文件名.

  例如:

      Ahk2Exe.exe /in "MyScript.ahk" /icon "MyIcon.ico"

注意:

- 包含空格的参数必须用双引号括起来.
- 进行编译通常并不会提升脚本的性能.
- 从 v1.1.01 开始, 不支持密码保护和 /NoDecompile 选项.
- 命令 #NoTrayIcon 和 "Menu, Tray, MainWindow" 会影响编译后脚本的行为.
- 如果脚本以编译的形式运行, 则内置变量 A_IsCompiled 的值为 1. 否则为空.
- [v1.0.43+]: 传递参数到 Ahk2Exe 后, 它会在标准输出中写入表示编译过程成功与否的消息. 尽管此消息不会显示在命令提示符中, 但可以通过像重定向输出到文件的方法 "捕获".
- [v1.1.22.03+]: 此外, 当编译出错时, Ahk2Exe 还能返回退出代码, 它可以指示编译的错误类型. 这些错误代码可以在 GitHub (ErrorCodes.md) 上找到.

编译器的源码和新版本可以在 GitHub 找到.

### 基础可执行文件

每个编译的脚本 .exe 都是基于一个实现解释器的可执行文件. 包含在编译器目录中的基础文件的扩展名是 ".bin"; 这些版本的解释器不包含加载外部脚本文件的能力. 相反, 程序会寻找一个名为 ">AUTOHOTKEY SCRIPT<" 的 Win32(RCDATA) 资源并加载它, 如果没有找到, 则会失败.

[v1.1.34+]: 标准的 AutoHotkey 可执行文件也可以作为编译脚本的基础, 通过嵌入一个 ID 为 1 的 Win32(RCDATA) 资源. 这使得编译后的脚本 .exe 可以与 /script 开关一起使用, 以执行除主嵌入脚本之外的其他脚本. 有关详情, 请参阅嵌入式脚本.

### 脚本编译器指令

[v1.1.33+]: 脚本编译器指令允许用户指定如何编译脚本的细节. 其中的一些功能是:

- 能够改变版本信息(如名称, 描述, 版本...).
- 能够为编译后的脚本添加资源.
- 能够调整编译的一些杂项.
- 能够从编译后的脚本中删除代码部分.

更多细节, 请参阅脚本编译器指令.

### 压缩编译脚本

Ahk2Exe 可以选择使用 MPRESS 或 [v1.1.33+] UPX 免费软件来压缩编译后的脚本. 如果 MPRESS.exe 和/或 UPX.exe 已被复制到安装 AutoHotkey 的 "Compiler" 子文件夹中, 则可以通过 `/compress` 参数或 GUI 设置来压缩 .exe 文件.

MPRESS 官网(介绍与下载): http://www.matcode.com/mpress.htm
MPRESS 镜像: https://www.autohotkey.com/mpress/

UPX 官网(介绍与下载): https://upx.github.io/

注意: 压缩编译脚本可以保护脚本不被诸如记事本或 PE 编辑器这类的工具随意查看, 但并不能保护脚本源码不被专用的提取工具提取.

### 背景信息

在 [v1.1.33.10+] 中, 支持以下文件夹结构, 其中 `Ahk2Exe.exe` 的运行版本在下面所示的第一个 \Compiler 目录中:

    \AutoHotkey
       AutoHotkeyA32.exe
       AutoHotkeyU32.exe
       AutoHotkeyU64.exe
       \Compiler
          Ahk2Exe.exe                   ; Ahk2Exe 的主版本
          ANSI 32-bit.bin
          Unicode 32-bit.bin
          Unicode 64-bit.bin
       \AutoHotkey v2.0-a135
          AutoHotkey32.exe
          AutoHotkey64.exe
          \Compiler
       \v2.0-beta.1
          AutoHotkey32.exe
          AutoHotkey64.exe

当 Ahk2Exe 启动时, 基文件搜索算法会运行很短的时间, 其工作原理如下:

符合的 AutoHotkey .exe 文件和所有的 .bin 文件在编译器的目录, 编译器的父目录和任何编译器同级的以 `AutoHotkey` 或 `V` 开头的目录中被搜索, 但不是以 `AutoHotkey_H` 开头. 所选的目录将被递归搜索. 任何找到的 AutoHotkey.exe 文件都被排除在外, 只留下诸如 AutoHotkeyA32.exe, AutoHotkey64.exe, 等文件以及所有发现的 .bin 文件. 所有被包括在内的 .exe 文件必须有一个以 `AutoHotkey` 开头的名称和一个包含 `AutoHotkey` 一词的文件描述, 并且必须有一个 `1.1.34+` 或 `2.0-a135+` 的版本.

一个成功的编译还需要一个 AutoHotkey 解释器的版本(作为一个工具), 并且使用类似的算法来选择一个版本. 在大多数情况下, 所使用的解释器的版本将与用户为编译所选择的基文件的版本相匹配.

## 向脚本传递命令行参数

脚本支持命令行参数. 格式为:

    AutoHotkey.exe [Switches] [Script Filename] [Script Parameters]

对于已编译脚本, 格式为:

    CompiledScript.exe [Switches] [Script Parameters]

Switches: 零个或多个下列开关:

开关 | 释义 | 编译脚本是否支持?
--- | --- | ---
/f 或 /force | 无条件强制启动, 忽略任何警告对话框. 和 #SingleInstance Off 的效果相同. | 是
/r 或 /restart | 脚本将会执行 Reload 命令. | 是
/ErrorStdOut /ErrorStdOut=Encoding | 把阻止脚本运行的语法错误发送到标准错误流(stderr) 而不显示在对话框中. 请参阅 #ErrorStdOut 了解详情. 这可以联合 /iLib 在不运行脚本的情况下验证脚本. [v1.1.33+]: 可以选择指定编码. 例如, `/ErrorStdOut=UTF-8` 在将消息写入 stderr 之前将其编码为 UTF-8. | 是
/Debug | [AHK_L 11+]: 连接到调试客户端. 想了解更多细节, 请参阅交互调试. | 否
/CPn | [AHK_L 51+]: 覆盖用于读取脚本文件的默认代码页. 想了解更多信息, 请参阅脚本文件代码页. [v1.1.33+]: 如果在安装程序中启用了 "Default to UTF-8" 选项, 那么 ".ahk" 文件类型就会在包括 `/CP65001` 在内的命令行中注册. 这将导致所有通过 shell(Explorer(资源管理器)) 启动的脚本在没有 UTF-16 字节顺序标记的情况下默认为 UTF-8. 通过直接运行 AutoHotkey.exe 启动的脚本仍然默认为 `CP0`, 因为没有 `/CP65001` 开关. | 否
/iLib "OutFile" | [v1.0.47+]: AutoHotkey 加载但不运行脚本. 通过库机制自动 include 每个脚本, 两行代码会写到 OutFile 指定的文件里面. 这些行以以下格式编写, 其中 LibDir 是 Lib 文件夹的完整路径, LibFile 是库的文件名:
    #Include LibDir\
    #IncludeAgain LibDir\LibFile.ahk
如果输出文件存在会被覆盖. OutFile 可以是 `*` 从而将输出写到 stdout 中. 如果脚本语法错误, 那么输出文件可能为空. 程序退出代码可以检测这种情况; 如果这里有语法错误, 退出代码就是 2. /ErrorStdOut 开关可用于抑制或捕获错误消息. | 否
/include "IncFile" | [v1.1.34+]: 在主脚本之前 Includes 一个文件. 这个方法只能包含一个文件. 当脚本被重新加载时, 这个开关会自动传递给新的实例. | 否
/script | [v1.1.34+]: 当与基于 .exe 文件的编译脚本一起使用时, 这个开关会使程序忽略主嵌入脚本. 这允许编译后的脚本 .exe 执行外部脚本文件或除主脚本以外的嵌入式脚本. 其他通常不被编译脚本支持的开关也可以使用, 但必须列在这个开关的右边. 例如:
    CompiledScript.exe /script /ErrorStdOut MyScript.ahk "Script's arg 1"
如果当前的可执行文件没有嵌入脚本, 这个开关是允许的, 但没有效果. 基于 .bin 文件的编译脚本不支持这个开关. 另请参阅: 基础可执行文件(Ahk2Exe) | N/A

Script Filename: 如果不含 Script Parameters, 那么此参数可以省略. 省略时, 它会运行(或提示您创建) 下列默认位置的其中一个文件:

- AutoHotkey 执行程序目录.
- 我的文档.

`AutoHotkey.ahk` 的文件名取决于 Autohotkey 的文件名. 例如重命名 AutoHotkey.exe 为 MyScript.exe, 它会尝试打开 `MyScript.ahk`. 如果你运行 AutoHotkeyU32.exe 没有参数则会运行 AutoHotkeyU32.ahk.

注意: 在早期版本 revision 51, 程序会在工作目录寻找 AutoHotkey.ini 或者我的文档下的 AutoHotkey.ahk.

[v1.1.17+]: 星号(*) 加在文件名上会打开标准输入通道(stdin) 的脚本. 例如 ExecScript().

Script Parameters: 你想要传递给脚本的参数用空格分隔. 任何包含空格的参数需要在两端加上引号("). 如果你想传递一个空字符串作为参数, 请指定两个连续的引号. 如果参数本身包含引号(") 则需要转义(\"). 因此, 加引号参数(例如 "C:\My Documents\") 末尾的斜杠(\) 都被当作原义的引号(就是说, 脚本将接收到字符串 C:\My Documents"). 要移除这样的引号, 请使用 `StringReplace, 1, 1, ",, All`.

[v1.1.27+]: 传入参数, 如果存在, 作为数组存储在内置变量 A_Args 中, 可以使用数组语法来访问. `A_Args[1]` 包含第一个参数. 下面的示例在传递给它的参数太少时退出脚本:

    if A_Args.Length() < 3
    {
        MsgBox % "脚本需要至少 3 个参数, 但实际只接收到" A_Args.Length() "个."
        ExitApp
    }

如果传递给脚本的参数数目不确定(可能是由于用户将一组文件拖放到脚本中), 则可以使用以下示例逐个提取它们:

    for n, param in A_Args  ; 对每个参数进行循环:
    {
        MsgBox Parameter number %n% is %param%.
    }

如果参数是文件名, 则可以使用以下示例将它们转换为大小写校正的长名称(与文件系统中存储的一致), 包括完整/绝对路径:

    for n, GivenPath in A_Args  ; 对每个参数(或拖放到脚本上的文件) 进行循环:
    {
        Loop Files, %GivenPath%, FD  ; 包含文件和目录.
            LongPath := A_LoopFileFullPath
        MsgBox The case-corrected long path name of file`n%GivenPath%`nis:`n%LongPath%
    }

已知限制: 如果 NTFS 文件系统中关闭了 8.3(短) 文件名, 那么拖拽文件到 .ahk 脚本上可能无法正常工作. 一种解决方法是编译脚本, 然后拖拉文件到生成的 EXE 文件上.

传统方法: 命令行参数也存储在变量 %1%, %2% 等中, 如 [v1.1.27] 之前的版本. 另外, %0% 包含传递参数的数量(如果没有则为 0). 但是, 这些变量不能在表达式中直接引用, 因为它们会被视为数字而不是变量. 下面的示例在传递给它的参数太少时退出脚本:

    if 0 < 3  ; 非表达式 if-语句的左边始终是变量的名称.
    {
        MsgBox 脚本需要至少 3 个参数, 但实际只接收到 %0% 个.
        ExitApp
    }

如果传递给脚本的参数数目不确定(可能由于用户拖放一组文件到脚本上), 可以使用下面的示例逐个提取这些参数:

    Loop, %0%  ; 对每个参数进行循环:
    {
        param := %A_Index%  ; 取得名称为 A_Index 的值的变量中的内容.
        MsgBox, 4,, Parameter number %A_Index% is %param%.  Continue?
        IfMsgBox, No
            break
    }

如果这些参数是文件名, 那么可以使用下列的例子把它们转换到大小写正确的长名称(与文件系统中存储的一致), 其中包含完整/绝对的路径.

    Loop %0%  ; 对每个参数 (或拖放到脚本上的文件) 进行循环:
    {
        GivenPath := %A_Index%  ; 取得名称为 A_Index 变量中的内容.
        Loop %GivenPath%, 1
            LongPath := A_LoopFileLongPath
        MsgBox The case-corrected long path name of file`n%GivenPath%`nis:`n%LongPath%
    }

## 脚本文件代码页 [AHK_L 51+]

为了使非 ASCII 字符能够正确地从文件中读取, 文件保存时使用的编码(通常由文本编辑器) 必须与 AutoHotkey 读取文件时使用的编码一致. 如果不匹配, 字符将被错误地解码. AutoHotkey 使用以下规则来决定使用哪种编码:

- 如果文件以 UTF-8 或 UTF-16(LE) 字节顺序标记开头, 则使用相应的代码页并且忽略 /CPn 开关.
- 如果在命令行中传递了 /CPn 开关, 则使用代码页 n. 有关可能值的列表, 请参阅 Code Page Identifiers.

  注意: AutoHotkey [v1.1.33+] 安装程序中的 "Default to UTF-8" 选项为所有通过 shell(资源管理器) 启动的脚本在命令行中添加 `/CP65001`.

- 在其他所有情况中使用系统默认的 ANSI 代码页.

注意这仅适用于 AutoHotkey 加载脚本的时候, 而不包括脚本自身的文件 I/O. FileEncoding 决定了脚本读取或写入文件时使用的默认编码, 然而 IniRead 和 IniWrite 总是使用 UTF-16 或 ANSI.

由于所有的文本都被转换(必要时) 为原生的字符串格式, 所以无效的或不存在于原生代码页中的字符会被替换为占位符: ANSI '?' 或 Unicode '�'. 在 Unicode 版本中, 这种情况只可能在脚本文件编码错误或用于保存和读取脚本的代码页不一致时发生.

可以使用 RegWrite 为资源管理器中运行的脚本设置默认代码页(例如双击脚本文件时):

    ; 取消对下面适当的行的注释或让它们都保留注释
    ;   以重新设置为当前版本的默认代码页.  需要时进行修改:
    ; codepage := 0        ; 系统默认的 ANSI 代码页
    ; codepage := 65001    ; UTF-8
    ; codepage := 1200     ; UTF-16
    ; codepage := 1252     ; ANSI Latin 1; 西欧 (Windows)
    if (codepage != "")
        codepage := " /CP" . codepage
    cmd="%A_AhkPath%"%codepage% "`%1" `%*
    key=AutoHotkeyScript\Shell\Open\Command
    if A_IsAdmin    ; 为所有用户进行设置.
        RegWrite, REG_SZ, HKCR, %key%,, %cmd%
    else            ; 仅为当前用户进行设置.
        RegWrite, REG_SZ, HKCU, Software\Classes\%key%,, %cmd%

这里假定已经安装了 AutoHotkey. 如果没有, 则结果可能不理想.

## 调试脚本

ListVars 和 Pause 等命令可以帮助您调试脚本. 例如, 把下面这两行临时插入精心选择的位置时, 可以在脚本中创建 "断点":

    ListVars
    Pause

当脚本执行到这两行时, 会显示所有变量当前包含的内容供你检查. 当你准备恢复时, 可以通过 File 或托盘菜单取消暂停. 然后脚本会继续执行, 直到遇到下一个 "断点"(如果有).

通常最好把这些 "断点" 插入到活动窗口对当前脚本没有影响的位置, 例如 WinActivate 命令的前一行. 这样当您取消暂停时脚本才可以正确恢复操作.

下列命令也可以用于调试: ListLines, KeyHistory 和 OutputDebug.

一些常见错误, 例如拼写错误或忘记 "global" 声明时, 可以使用启用警告检测到.

### 交互调试 [AHK_L 11+]

通过受支持的 DBGp 客户端可以进行交互调试. 一般支持下列操作:

- 设置和移除断点 - 遇到断点时暂停执行.
- 单步调试代码 - 逐语句, 逐过程或跳出函数和子程序.
- 检查所有变量或特殊变量.
- 查看正在运行的子程序和函数的堆栈.

注意在已编译脚本中没有提供此功能.

要启用交互调试, 首先要运行受支持的调试器客户端, 然后使用命令行开关 /Debug 运行脚本.

    AutoHotkey.exe /Debug=SERVER:PORT ...

SERVER 和 PORT 可以省略. 例如, 下面的方式是等同的:

    AutoHotkey /Debug "myscript.ahk"
    AutoHotkey /Debug=localhost:9000 "myscript.ahk"

[AHK_L 59+]: 要向已经在运行的脚本附加调试器, 请向脚本发送消息, 如下所示:

    ScriptPath := "" ; 设置此变量为脚本的完整路径
    DetectHiddenWindows On
    if WinExist(ScriptPath " ahk_class AutoHotkey")
        ; 可选参数:
        ;   wParam  = 调试器客户端的 IPv4 地址, 相当于 32 位整数.
        ;   lParam  = 调试器客户端的监听端口.
        PostMessage DllCall("RegisterWindowMessage", "str", "AHK_ATTACH_DEBUGGER")

当调试器连接后, 通过发送 "detach" DBGp 命令可以在不终止脚本的情况下分离调试器.

## 脚本展示

请参阅此页面了解一些有用的脚本.

dftg