# 脚本

相关话题:

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

## 目录

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

## 简介

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

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

## 脚本启动(自动执行线程)

脚本加载完成后, 自动执行线程在脚本的顶行开始执行, 直到被指示停止为止, 例如 Return, ExitApp 或 Exit. 脚本的物理结束也作为 Exit.

没有热键, 热字串, 可见的 GUI, 活动的计时器, 剪贴板监视器和 InputHook, 并且没有调用 Persistent 函数的脚本, 将在完成启动后终止. 否则, 它会以空闲状态继续运行, 从而对热键, 热字串, GUI 事件, 自定义菜单项目和计时器这些事件做出响应. 如果这些条件在启动完成后发生变化(例如, 禁用了最后一个计时器), 脚本可能会在最后一个正在运行的线程完成后或最后一个 GUI 关闭后退出.

每当任何新的线程被启动时(不管是由热键, 热字串, 计时器还是其他事件启动的) 以下设置都会从自动执行线程中复制. 如果自动执行线程中没有设置, 则使用标准的默认值(与下面每个页面中注明的一样): CoordMode, Critical, DetectHiddenText, DetectHiddenWindows, FileEncoding, ListLines, SendLevel, SendMode, SetControlDelay, SetDefaultMouseSpeed, SetKeyDelay, SetMouseDelay, SetRegView, SetStoreCapsLockMode, SetTitleMatchMode, SetWinDelay 和 Thread.

每个线程都会保留其自身的上述设置的集合, 此对这些设置所做的更改不会影响其他线程.

上述某个函数的 "默认设置" 通常指的是自动执行线程的当前设置, 该线程的起始值与程序定义的默认设置相同.

传统上, 脚本的顶部被称为 自动执行段. 然而, 自动执行线程并不限于脚本的顶部. 在自动执行线程上调用的任何函数也可能影响默认设置. 由于在执行过程中遇到指令和函数, 热键, 热字串和类定义时都会被跳过, 因此有可能将启动代码放在每个文件中. 例如, 一组热键使用的全局变量可能会被初始化在这些热键的上方(甚至下方), 而不是在脚本的顶部.

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

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

有三种方法, 通常可以组合使用:

- 延续运算符(延续行): 以表达式运算符开始或结束一行, 将其连接到前或下一行.
- 延续闭环(延续表达式): 在大多数情况下, 括在 (), [] 或 {} 中的子表达式可以自动跨越多行.
- 延续片段: 标记一组要合并在一起的行, 并添加其他选项, 例如在行之间插入什么文本(或代码).

延续运算符: 以逗号或任何其他表达式运算符(除了 ++ 和 --) 开头的行会自动与其正上方的行合并. 同样, 以表达式运算符结尾的行会自动与它下面的行合并. 在以下示例中, 第二行被附加到第一行, 因为它以逗号开头:

    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)

下面的例子等同于上面的例子:

    FileAppend "This is the text to append.`n",   ; 这里允许注释.
        A_ProgramFiles "\SomeApplication\LogFile.txt"  ; 注释.

    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)

虽然上面示例中使用的缩进是可选的, 但它可以显示出哪些行属于上一行从而提高代码清晰度. 此外, 可以在上述示例中的任何行的末尾或行与行之间添加空行或注释.

延续操作符不能与自动替换热字符或 #HotIf 以外的指令一起使用.

延续闭环: 如果一行中包含一个带有未闭合 `(`/`[`/`{` 的表达式或函数/属性定义, 那么它将与后续的行连接, 直到左, 右括号的数量平衡为止. 换句话说, 在大多数情况下, 用小括号(圆括号), 中括号(方括号), 大括号(花括号) 括起来的子表达式可以自动跨越多行. 例如:

    myarray := [
      "item 1",
      "item 2",
    ]
    MsgBox(
        "The value of item 2 is " myarray[2],
        "Title",
        "ok iconi"
        )

延续表达式可以包含两种类型的注释.

延续表达式可以包含普通的延续片段. 因此, 与任何包含表达式的行一样, 如果一行以非转义的左括号(`(`) 开头, 除非在同一行上有一个右括号(`)`), 否则它将被视为延续片段的开头.

引号括起来的字符串不能单独使用此方法跨越多行. 然而, 请参阅上面的内容.

通过括起来的方式进行延续可以与延续运算符结合使用. 例如:

    myarray :=  ; 赋值运算符会引起延续.
    [  ; 方括号括起以下两行.
      "item 1",
      "item 2",
    ]

如果程序判定行末尾的大括号(`{`) 应该被解释为块(OTB 样式) 的开头而不是对象标识符的开头, 则行末尾的大括号不会导致延续. 具体来说(按优先级降序排列):

- 如果一个大括号前面有一个未闭合的 `(`/`[`/`{`, 那么它永远不会被解释为一个块的开始, 因为这会产生一个无效的表达式. 例如, 大括号在 `If ({` 中是对象标识符的开始.
- 对象标识符不能合法地跟随 `)` 或 `]`, 因此, 如果大括号跟随这些符号中的任何一个(不包括空格), 它将被解释为块的开始(例如用于函数或属性定义).
- 对于需要正文(主体)(因此支持 OTB) 的控制流语句, 仅当大括号前面有运算符时, 大括号才能成为对象标识符的开始, 如 `:= {` 或 `for x in {`. 特别是, 大括号在 `Loop {` 中始终是块的开始, 而 `If {` 和 `While {` 始终是错误.

大括号可以安全地在任何不需要正文的函数调用, 表达式或控制流语句的行延续中使用. 例如:

    myfn() {
        return {
            key: "value"
        }
    }

延续片段: 此方法应该用于合并大量行或当这些行不适合其他方法时. 虽然这个方法对于自动替换热字符串特别有用, 但是它也可以用于任何表达式. 例如:

    ; 示例 #1:
    Var := "
    (
    一行文字.
    默认情况下, 行与行之间的硬回车(`n) 也会被储存.
    	这一行用制表符缩进; 默认情况下, 该制表符也会被储存.
    此外, "引号" 会在适当时自动转义.
    )"

    ; 示例 #2:
    FileAppend "
    (
    第 1 行的文字.
    第 2 行的文字. 默认情况下, 行与行之间存在换行符(`n).
    )", A_Desktop "\My File.txt"

在上面的示例中, 一系列的行在顶部和底部用一对括号括起来. 这被称为 延续片段. 注意, 右括号之后的任何代码也与其他行连接(没有任何分隔符), 但不包括左括号和右括号.

如果延续片段上面的行以命名字符串结束, 而延续片段没有从引号字符串开始, 则自动插入一个空格, 将名称与延续片段的内容分隔开.

如果延续片段以引号开始, 引号将自动转义(即它们被解释为原义字符), 如上面的示例所示. 否则, 引号的作用与正常情况相同; 也就是说, 延续片段可以包含带引号字符串的表达式.

默认情况下, 根据延续片段中第一行的缩进, 省略前导空格或制表符. 如果第一行混合有空格和制表符, 则只将第一种类型的字符视为缩进. 如果任何行缩进小于第一行或使用不同的字符, 则该行上的所有前导空格都保持原样.

延续片段的默认行为可以通过在该片段的左括号的右侧包含以下一个或多个选项来覆盖改写. 如果存在多个选项, 请使用空格将每个选项与前一个选项分开. 例如: `( LTrim Join|`.

Join: 指定行与行之间连接的方式. 如果省略此选项, 那么除最后一行外的其他行后面都会跟一个换行符(`n). 如果指定单词 Join 自身, 则行与行之间直接连接而不添加任何字符. 否则, 单词 Join 后面可以紧跟最多 15 个字符. 例如, `Join`s` 会在除最后一行外的每行末尾插入一个空格. 另一个例子是 `Join`r`n`, 它会在行与行之间插入 CR+LF. 同样地, `Join|` 会在行之间插入管道符. 要让延续片段的最后一行也以连接字符串结尾, 请在它的右括号上方添加一个空行.

LTrim: 删除每行开头的所有的空格和制表符. 由于默认的 "智能" 行为, 这通常是不必要的.

LTrim0 (LTrim 后跟一个 0): 关闭每行开头的空格和制表符的删除.

RTrim0 (RTrim 后跟一个 0): 关闭每行末尾的空格和制表符的删除.

Comments (或 Comment 或 Com 或 C): 在延续片段中允许分号注释(但不允许 `/*..*/`). 这些注释(以及它们左边的任何空格和制表符) 将完全从连接结果中省略, 而不是作为文本处理. 每个注释可以出现在一行的右侧, 也可以单独出现在新行上.

`(重音): 按按原义处理反引号, 而不是作为转义字符. 这还可以防止任何显式指定的转义序列(``r` 和 ``t`) 的转换.

( 或 ): 如果左括号或右括号出现在初始左括号的右侧(作为 Join 选项的参数除外), 则该行将被重新解释为表达式, 而不是延续片段的开头. 这允许在行的开头使用 `(x.y)[z]()` 之类的表达式, 并且还允许多行表达式以 `((` 或 `(MyFunc(` 在行的开头.

除非指定了重音(`) 选项, 否则在延续片段内部支持诸如 `n(换行符) 和 `t(制表符) 之类的转义序列.

当 comment 选项不存在时, 分号和 /*..*/ 注释在延续片段的内部不受支持, 因为它们被视为原义文本. 但是, 注释可以包含在延续片段的底部和顶部行. 例如:

    FileAppend "   ; 注释.
    ; 注释.
    ( LTrim Join    ; 注释.
         ; 本行 不是 注释; 它是原义的文字. 上面行包含 注释 的部分都是注释.
    )", "C:\File.txt"   ; 注释.

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

因为右括号表示延续片段的结束, 所以要让某一行以原义的右括号开头, 需要在其前面加上重音符/反引号: ``)`. 然而, 这不能与重音(`) 选项结合使用.

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

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

## 脚本库文件夹

库文件夹提供了一些标准位置来保存其他脚本通过 #Include 方式利用的共享脚本. 一个库脚本通常包含一个函数或类, 它被设计成以这种方式使用和重用. 将库脚本放置在其中的一个位置, 可以更容易地编写脚本, 这些脚本可以与他人共享, 并在多个设置中工作. 库的位置是:

    A_ScriptDir "\Lib\"  ; 本地库.
    A_MyDocuments "\AutoHotkey\Lib\"  ; 用户库.
    "directory-of-the-currently-running-AutoHotkey.exe\Lib\"  ; 标准库.

库文件夹按照上面显示的顺序进行搜索.

例如, 如果一个脚本包含行 `#Include <MyLib>`, 程序就会在本地库中搜索一个名为 "MyLib.ahk" 的文件. 如果在那里没有找到, 它就会在用户库中搜索它, 直至在标准库中搜索. 如果仍然没有找到匹配, 并且库的名称包含下划线(例如 `MyPrefix_MyFunc`), 则程序只使用前缀(例如 `MyPrefix.ahk`) 再次搜索.

虽然按照惯例, 一个库文件通常只包含一个与其文件名同名的函数或类, 但它也可能包含只被它调用的私有函数. 然而, 这些函数应该有相当不同的名字, 因为它们仍然在全局命名空间中; 也就是说, 它们可以在脚本的任何地方被调用.

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

程序中包含了脚本编译器(由 fincs 提供, TAC109 的添加).

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

v1.1 和 v2 脚本使用相同的编译器. 编译器通过检查所提供的基文件的主版本来区分脚本版本.

### 编译器目录

- 运行编译器
- 基础可执行文件
- 脚本编译器指令
- 压缩编译的脚本
- 背景信息

### 运行编译器

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 | 要使用的基文件(一个 .bin 文件或 .exe 文件). 使用的基文件的主版本必须与编译的脚本的版本相同. 默认是 GUI 界面中保存的最后一个基文件名, 或脚本中的任何 Base 编译器指令指定.
  /resourceid name | 指定一个非标准的资源 ID, 用于使用 .exe 基文件编译的主脚本(请参阅嵌入式脚本). 数字型资源 ID 应该由一个哈希符号(#) 和一个十进制数字组成. 默认是 #1, 或脚本中的任何 ResourceID 编译器指令指定.
  /cp codepage | 覆盖用于读取脚本文件的默认代码页. 关于可能的值列表, 请参阅 Code Page Identifiers. 注意, Unicode 脚本应该以字节顺序标记(BOM) 开始, 因此不需要使用这个参数.
  /compress n | 是否压缩 exe 文件? 0 = 不压缩, 1 = 使用 MPRESS(如果存在), 2 = 使用 UPX(如果存在). 默认值是 GUI 界面中最后保存的设置.
  /gui | 显示 GUI 而不是立即编译. 其他参数可以用来覆盖上次在 GUI 中保存的设置. `/in` 在这种情况下是可选的.
  /silent [verbose] | 禁用所有的消息框并将错误输出到标准错误流(stderr); 如果 stderr 失败, 则输出到标准输出流(stdout). 其他信息也会输出到 stdout. 可以选择输入 `verbose` 这个单词, 把状态信息也输出到 stdout.
  不推荐: /ahk file_name | 编译脚本时使用的 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"

注意:

- 包含空格的参数必须用双引号括起来.
- 编译通常不会提高脚本的性能.
- #NoTrayIcon 和 A_AllowMainWindow 会影响已编译脚本的行为.
- 如果脚本以编译的形式运行, 则内置变量 A_IsCompiled 的值为 1. 否则为 0.
- 传递参数到 Ahk2Exe 后, 它会在标准输出中写入表示编译过程成功与否的消息. 尽管此消息不会显示在命令提示符中, 但可以通过像重定向输出到文件的方法 "捕获".
- 此外, 当编译出错时, Ahk2Exe 还能返回退出代码, 它可以指示编译的错误类型. 这些错误代码可以在 GitHub (ErrorCodes.md) 上找到.

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

### 基础可执行文件

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

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

### 脚本编译器指令

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

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

有关详情, 请参阅脚本编译器指令.

### 压缩编译的脚本

Ahk2Exe 可以选择使用 MPRESS 或 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 编辑器这类的工具随意查看, 但并不能保护脚本源码不被专用的提取工具提取.

### 背景信息

支持以下文件夹结构, 其中 `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` 或 `V` 开头的目录中搜索符合的 AutoHotkey .exe 文件和所有的 .bin 文件, 但不在以 `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(开关): 下列零个或多个:

开关 | 意义 | 支持编译脚本?
--- | --- | ---
/force | 无条件强制启动, 忽略任何警告对话框. 和 #SingleInstance Off 的效果相同. | 支持
/restart | 指示脚本正在重新启动, 并尝试关闭脚本的上一个实例(这也能在脚本内部通过 Reload 函数实现). | 支持
/ErrorStdOut /ErrorStdOut=Encoding | 把阻止脚本运行的语法错误发送到标准错误流(stderr) 而不显示在对话框中. 有关详情, 请参阅 #ErrorStdOut. 可以选择指定编码. 例如, `/ErrorStdOut=UTF-8` 在将消息写入 stderr 之前将其编码为 UTF-8. | 不支持
/Debug | 连接到调试客户端. 有关详情, 请参阅交互调试. | 不支持
/CPn | 覆盖用于读取脚本文件的默认代码页. 有关详情, 请参阅脚本文件代码页. | 不支持
/Validate | AutoHotkey 加载脚本, 然后退出而不是运行它. 默认情况下, 加载时的错误和警告会像往常一样显示. /ErrorStdOut 开关可以用来抑制或捕获任何错误信息. 如果脚本成功加载, 进程退出代码为零, 如果有错误, 则退出代码为非零. | 不支持
/iLib "OutFile" | 过时的: 请使用 /validate 代替. AutoHotkey 加载脚本但不运行. 在以前的 AutoHotkey 版本中, 自动导入文件的文件名被写入 OutFile 指定的文件中, 格式为 #Include 指令. 如果输出文件存在, 它会被覆盖. OutFile 可以是 `*` 从而将输出写到标准输出中. 如果脚本有语法错误, 那么输出文件可能为空. 程序退出代码可以检测这种情况; 如果这里有语法错误, 退出代码为 2. /ErrorStdOut 开关可用于抑制或捕获错误消息. | 不支持
/include "IncFile" | 在主脚本之前 Include 一个文件. 这个方法只能包含一个文件. 当脚本被重新加载时, 这个开关会自动传递给新的实例. | 不支持
/script | 当与基于 .exe 文件的编译脚本一起使用时, 这个开关会使程序忽略主嵌入脚本. 这允许编译后的脚本 .exe 执行外部脚本文件或除主脚本以外的嵌入式脚本. 其他通常不被编译脚本支持的开关也可以使用, 但必须列在这个开关的右边. 例如:
    CompiledScript.exe /script /ErrorStdOut MyScript.ahk "Script's arg 1"
如果当前的可执行文件没有嵌入脚本, 这个开关是允许的, 但没有效果. 基于 .bin 文件的编译脚本不支持这个开关. 另请参阅: 基础可执行文件(Ahk2Exe) | N/A

Script Filename: 如果不含 Script Parameters, 那么此参数可以省略. 如果省略, 它默认为 AutoHotkey 可执行文件的路径和名称, 将 ".exe" 替换为 ".ahk". 例如, 如果将 AutoHotkey.exe 重命名为 MyScript.exe, 它将尝试加载 MyScript.ahk. 如果你不带参数地运行 AutoHotkey32.exe, 它将尝试加载 AutoHotkey32.ahk.

为文件名指定星号(*) 以从标准输入(stdin) 读取脚本文本. 这也使以下内容生效:

- 初始工作目录用作 A_ScriptDir 并定位本地库文件夹.
- A_ScriptName 和 A_ScriptFullPath 都包含 "*".
- #SingleInstance 默认是关闭的.

有关示例, 请参阅 ExecScript().

如果当前可执行文件有嵌入脚本, 此参数可以是星号, 后面跟着嵌入脚本的资源名或 ID. 对于编译脚本(例如, 如果存在 ID 为 #1 的嵌入式脚本), 此参数前必须有 `/script` 开关.

Script Parameters(脚本参数): 要传递给脚本的字符串, 每个参数用空格分隔. 任何包含空格的参数都应该用引号括起来. 如果要将空字符串作为参数传递, 请指定两个连续的引号. 原义引号可以通过在它前面加上反斜杠(\") 来传递. 因此, 任何在加引号参数的末尾的反斜杠(如 "C:\My Documents\") 都被当作原义的引号(就是说, 脚本将接收到字符串 C:\My Documents"). 要移除引号, 使用 `A_Args[1] := StrReplace(A_Args[1], '"')`

传入参数, 如果存在, 作为数组存储在内置变量 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
    }

## 脚本文件代码页

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

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

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

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

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

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

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

## 调试脚本

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

    ListVars
    Pause

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

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

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

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

### 交互调试

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

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

注意在已编译脚本中此功能是禁用的.

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

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

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

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

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

    ScriptPath := "" ; 设置此变量为脚本的完整路径
    A_DetectHiddenWindows := true
    if WinExist(ScriptPath " ahk_class AutoHotkey")
        ; 可选参数:
        ;   wParam  = 调试器客户端的 IPv4 地址, 为 32 位整数.
        ;   lParam  = 调试器客户端正在侦听的端口.
        PostMessage DllCall("RegisterWindowMessage", "Str", "AHK_ATTACH_DEBUGGER")

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

## 脚本展示

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

dftg