# Hotkey

在脚本运行时创建, 修改, 启用或禁用热键.

    Hotkey, KeyName , Label, Options
    Hotkey, IfWinActive/Exist , WinTitle, WinText
    Hotkey, If , Expression
    Hotkey, If, % FunctionObject

## 参数

KeyName(按键名称)

  热键的按键的名称, 包括所有修饰符. 例如, 指定 `#c` 来触发 `Win`+`C` 热键.

  如果 KeyName 是现有的热键, 那么将使用此命令的其他参数值更新原来的热键.

  KeyName 也可以是现有的热键标签的名称(即双冒号标签), 这样会使用此命令的其他参数值更新原来的热键.

  指定 现有的 热键时, KeyName 是不区分大小写的. 但是, 按键名称必须和现有的热键拼写相同(例如, 在这种情况下 Esc 与 Escape 是不同的). 此外, 修饰符(例如 ^!+#) 的顺序是无关紧要的. GetKeyName() 可用于获取正常的按键名称.

  首次创建一个热键时(通过 Hotkey 命令或脚本中的双冒号标签), 其键名以及修饰符的顺序成为此热键的固定名称, 像 A_ThisHotkey 包含的那样. 这个名称由热键的所有变体共享, 并且即使之后 Hotkey 命令使用不同的修饰符顺序操作此热键, 此名称也不会改变.

  [v1.1.15+]: 如果已存在该热键变体, 则根据 KeyName 是否包含颚化符前缀(~) 更新它的行为. 但在 [v1.1.19] 之前, 如果 Label 被省略时, 热键不会更新.

  [v1.1.19+]: 使用钩子前缀($) 可添加到现有热键上. 这个前缀影响所有热键的变体且不能被删除. 在 [v1.1.19] 之前, 当修改一个现有热键时, 这个前缀被直接忽略.

Label(标签)

  当按下对应热键时, 该标签区块中的所有内容都会被执行(作为一个新的线程运行). 可以使用普通标签和热键/热字串标签, 但如果脚本中包含多个同名的标签, 则只会使用第一个. 不要包含尾随的冒号(: 或 ::). 如果 标签 是动态的(比如 %VarContainingLabelName%), 可用 IsLabel(VarContainingLabelName) 事先检查标签是否存在.

  [v1.1.20+]: 如果不是一个有效的标签名称, 这个参数可以是一个函数的名称, 或者是一个函数对象单个引用变量. 比如, `Hotkey #z, %funcObj%, On` 或 `Hotkey #z, % funcObj, On`. 其他返回对象的表达式目前暂不支持. 当热键被触发, 函数就会被调用且无需任何参数. 热键还可以直接定义为函数而无需使用 Hotkey 命令.

  如果 KeyName 已经作为热键, 那么参数可以为空, 这种情况下标签不会被改变. 这在需要仅改变热键 Options(选项) 时很有用.

  注意: 如果指定了标签或函数, 但热键之前已经使用此命令禁用了, 那么热键会保持禁用状态. 要启用它, 请在 Options 中包含单词 ON.

  此参数还可以是下列特定值的其中一个:

  On: 启用热键. 如果热键已经处于启用状态, 则不进行操作.

  Off: 禁用热键. 如果热键已经处于禁用状态, 则不进行操作.

  Toggle: 设置热键到相反的状态(启用或禁用).

  AltTab(及其他): 这些是这里描述的特殊的 Alt-Tab 热键动作.

  警告: 在定义标签名称为 On, Off, Toggle 或 AltTab(或是任何被这个命令支持的名称) 时可能会导致矛盾的行为. 强烈建议不使用这些值作为标签的名称.

Options(选项)

  由零个或多个下列字母组成的字符串, 字母间可以用空格分隔. 例如: `UseErrorLevel B0`

  UseErrorLevel: 如果命令遇到问题, 使用此选项会跳过警告对话框, 设置 ErrorLevel 为下表中的一个代码, 然后让当前线程继续执行.

  On: 如果热键当前是禁用的则启用它.

  Off: 如果热键当前是启用的则禁用它. 此选项常用来创建初始状态为禁用的热键.

  B 或 B0: 指定字母 B 将按照 #MaxThreadsBuffer 描述的方法缓冲热键. 指定 B0(B 后跟着数字 0) 来禁用这种类型的缓冲.

  Pn: 指定字母 P 后面跟着热键的线程优先级. 如果创建热键时省略 P 选项, 则设置优先级为 0.

  Tn: 指定字母 T 后面跟着一个表示此热键允许的线程数, 如同 #MaxThreadsPerHotkey 中描述的那样. 例如: `T5`.

  In(表示: InputLevel 输入级别) [v1.1.23+]: 指定字母 I(或 i) 跟随热键的 input level(输入级别). 例如: `I1`.

  如果任一选项字母被忽略并且热键已经存在, 这些选项将不会起作用. 但如果热键还没创建(将由这条命令创建), 这些选项将会影响那些最近创建的热键. 例如, 即使 #MaxThreadsBuffer 出现在脚本底部的情况下也会被采用. 而如果脚本中没有出现 #MaxThreadsBuffer, 则会采用其默认值(这里是 OFF). 这种特性也适用于 #IfWin: 脚本最底部出现的 #IfWin 会适用于最近创建的热键除非 "Hotkey IfWin" 在脚本开始时就已执行.

IfWinActive
IfWinExist
IfWinNotActive
IfWinNotExist
If, Expression
If, % FunctionObject

  这些子命令都让此后创建的所有热键都是上下文相关的. 请参考后面关于 Hotkey, If 的备注.

  了解详情
WinTitle
WinText

  在这两个参数中, 任何变量引用(例如 %var%) 会在命令执行结束时被固定下来. 换句话说, 此后对这些变量内容的改变不会影响现有的 IfWin 热键.

  与 #IfWinActive/Exist 一样, WinTitle 和 WinText 为 SetTitleMatchMode 和 DetectHiddenWindows 使用在自动执行段中的默认设置. 请参阅 #IfWinActive/Exist 了解详情.

If, Expression(表达式)

  [AHK_L 4+]: 关联后续创建的热键与 #If 表达式. 表达式 必须是在脚本其他任何地方用 #If 指令开头的的表达式. 通过这个命令不能创建新的表达式, 而只能用已经存在的表达式创建新的热键定义. 请参考 #If 例子 4.

  注意: 这个 Hotkey 只能用你传递给它的文本, 而不是原始代码. 逗号和变量符号(百分号) 是在命令调用 之前 被解析的, 所以如果这些符号是原始表达式中的一部分, 则应该先转换一下. 转义序列是脚本加载的时候就进行转换的, 所以只产生字符; 例如: `Hotkey, If, x = "`t"` 和 `Hotkey, If, % "x = """ A_Tab """"` 都对应于 `#If x = "`t"`.

  已知限制: If 表达式 包含 `and`/`or` 运算符的时候, 不会被识别为表达式. 为了避免此问题, 请在原来的 #If 表达式和传递给 Hotkey 命令的表达式中使用等价的 `&&`/`||` 运算符.

If, % FunctionObject

  [v1.1.25+]: 关联后续创建的热键与给定的函数对象. 这样的热键将会在如果调用给定的函数对象产生一个非零的数时执行. 这类似于 `Hotkey, If, Expression(表达式)`, 除了每个热键能有多个变体(每个对象一个). 函数对象 必须是包含带有 call 方法的对象的单个变量(不是表达式). 函数 或 call 方法能带有一个参数, 热键名称 .

  一旦传递到热键命令, 对象将永远不会被删除(但是进程退出时操作系统会回收内存).

  下面的 "三键组合" 示例使用此子命令.

## 错误处理

[v1.1.04+]: 此命令失败时会抛出异常. 想了解更多信息, 请参阅运行时错误.

只有在出现下列情况时 ErrorLevel 才会被改变:
1) 首个参数为 IfWin[Not]Active/Exist, 此时如果遇到问题它会被设置为 1 否则为 0; 或 2) 在 Options 参数中使用了单词 UseErrorLevel.

[v1.1.25+]: 如果第一个参数是 "If", 如果第二个参数无效或内存分配失败, 就会引发异常. 在这些情况下 ErrorLevel 不被设置, 但成功时仍设置为 0.

错误 | 说明
--- | ---
1 | Label 参数指定了不存在的标签名.
2 | KeyName 参数指定了一个或多个当前键盘布局/语言无法识别或不支持的键.
3 | 不支持的前缀键. 例如, 不支持使用鼠标滚轮作为热键(如 `WheelDown & Enter`) 的前缀.
4 | KeyName 参数不适合用于 AltTab 或 ShiftAltTab 功能. 这种情况下要求热键为两个键的组合. 例如: `RControl & RShift::AltTab`.
5 | 命令试图修改不存在的热键.
6 | 命令试图修改现有热键的不存在的变体. 要解决此问题, 请使用 Hotkey IfWin 来设置匹配那些需修改的热键变体的条件.
98 | 创建此热键后将超过每个脚本热键数量的限制(不过, 每个热键的变体数目不受限制, 并且对于热字串的数目也没有限制). 该限制在 [v1.0.48] 中从 700 提高到 1000, 在 [v1.1.30] 中提高到 32762.
99 | 内存不足. 这种情况极少, 通常仅在操作系统变得不稳定时才会发生.

提示: 使用 UseErrorLevel 选项可以检查一个热键的变体是否存在. 例如:

    Hotkey, ^!p,, UseErrorLevel
    if ErrorLevel in 5,6
        MsgBox 热键定义不存在 或者 它不是当前 IfWin 的条件的变体.

## 备注

当前的 IfWin 设置决定了 Hotkey 命令将操作热键的哪个变体.

如果需要根据活动窗口的类型自动禁用选择的热键或热字串, 使用 `Hotkey, ^!c, Off` 通常不如 #IfWinActive/Exist(或它们下面的动态副本 "Hotkey IfWinActive/Exist") 来的方便.

通过双冒号创建热键比使用 Hotkey 命令执行地更好, 因为在脚本启动时会批量启用它们(而非一个一个地启用). 因此, 最好使用此命令创建那些在脚本开始运行后才知道键名的热键. 一种这样的情况是通过 INI 文件为热键配置了多个不同的动作.

给定的标签可以是多个热键的目标. 如果某个热键调用了标签, 您可以通过检查内置变量 A_ThisHotkey 来确定是哪个热键.

如果脚本是挂起的, 那么新增加/启用的热键也将是挂起的, 直到挂起状态被关闭(除非它们像 Suspend 命令中描述的那样被免除了).

由此命令做出的改变使得键盘和/或鼠标钩子被安装或移除是正常的.

尽管 Hotkey 命令不能直接启用或禁用脚本中不是它创建的热键, 但在大多数情况下它可以通过创建或启用相同的热键来覆盖它们. 这样是否有效取决于下列因素: 1) 在其他脚本中需要被覆盖的热键是否为钩子热键(非钩子热键总是可以成功覆盖); 2) 最近启动的热键通常优先于其他脚本中的相同热键(因此, 如果要覆盖其他脚本中的脚本是最近启动的, 覆盖总是会成功); 3) 此热键的启用或创建是否会重新激活键盘或鼠标钩子(如果是, 那么覆盖总是会成功).

每当脚本含有至少一个热键时, 它会变成持续运行的, 这意味着应该使用 ExitApp 而不是 Exit 来终止它. 同时热键脚本自动为 #SingleInstance 的单实例属性, 不过可以指定 `#SingleInstance Off` 来关闭.

## 关于 Hotkey, If 的备注

"Hotkey If" 命令允许在脚本运行时创建或修改上下文相关的热键(相比之下, #If 和 #IfWinActive/Exist 指令是与位置有关的, 在脚本开始执行之前生效). 例如:

    Hotkey, IfWinActive, ahk_class Notepad
    Hotkey, ^!e, MyLabel  ; 创建仅在记事本中有效的热键.

使用 "Hotkey If" 会把后续创建或修改的所有热键都变成上下文相关的. 此外, 每个子命令都是互斥的 ; 也就是说, 只有最新的才会生效.

要关闭上下文相关性(也就是说, 使后续创建的热键在所有窗口工作), 指定 if 命令, 但省略参数. 例如: `Hotkey, If` 或 `Hotkey, IfWinActive`.

如果 "Hotkey If" 在脚本中从未使用过, 最后使用的 #If 或 #IfWin 指令(如果有的话) 会影响热键命令.

使用 if 子命令或指令禁用鼠标或键盘热键后, 它会执行其原来的功能; 也就是说, 它传递到活动窗口, 就好像不存在热键一样. 然而, 操纵杆热键总是有效, 无论禁用与否.

## 变体(副本) 热键

只要每次定义时含有不同的 IfWin 条件, 一个特定的热键可以被创建多次. 这被称为 热键变体. 例如:

    Hotkey, IfWinActive, ahk_class Notepad
    Hotkey, ^!c, MyLabelForNotepad
    Hotkey, IfWinActive, ahk_class WordPadClass
    Hotkey, ^!c, MyLabelForWordPad
    Hotkey, IfWinActive
    Hotkey, ^!c, MyLabelForAllOtherWindows

如果有多个变体符合触发条件, 那么仅触发最早创建的那个. 这种情况的例外是全局变体(不带有 IfWin 条件的那个): 它的优先级总是最低, 仅当其他变体都不触发时它才会被触发.

创建重复热键时, 修饰符的顺序例如 ^!+# 没有关系. 例如: `^!c` 等效于 `!^c`. 但是, 按键必须拼写一致. 例如, 用于此目的时 Esc 与 Escape 是有区别的(尽管不会受大小写形式的影响). 最后, 任何带有通配符前缀(*) 的热键和不带通配符的完全不同; 例如, `*F1` 和 `F1` 将拥有各自的变体设置.

关于 IfWin 热键的更多信息, 请参阅 #IfWin 的一般说明.

## 相关

热键修饰符, #IfWinActive/Exist, #MaxThreadsBuffer, #MaxThreadsPerHotkey, Suspend, IsLabel(), 线程, Thread, Critical, Gosub, Return, Menu, SetTimer

## 示例

创建 Ctrl-Alt-Z 热键.

    Hotkey, ^!z, MyLabel
    return

    MyLabel:
    MsgBox You pressed %A_ThisHotkey%.
    return

让 RCtrl & RShift 执行 Alt-Tab 的功能.

    Hotkey, RCtrl & RShift, AltTab

重新启用 Win-C 热键.

    Hotkey, #c, On

禁用 Shift-Win-C 热键.

    Hotkey, $+#c, Off

改变热键为允许 5 个线程.

    Hotkey, ^!a, , T5

创建仅在记事本中有效的热键 Ctrl-Alt-C.

    Hotkey, IfWinActive, ahk_class Notepad
    Hotkey, ^!c, MyLabelForNotepad

创建一个界面允许你注册简单的三键组合热键.

    Gui Add, Text, xm, Prefix key:
    Gui Add, Edit, yp x100 w100 vPrefix, Space
    Gui Add, Text, xm, Suffix hotkey:
    Gui Add, Edit, yp x100 w100  vSuffix, f & j
    Gui Add, Button, Default, Register
    Gui Show
    return

    ButtonRegister() {
        global
        Gui Submit, NoHide
        local fn
        fn := Func("HotkeyShouldFire").Bind(Prefix)
        Hotkey If, % fn
        Hotkey % Suffix, FireHotkey
    }

    HotkeyShouldFire(prefix, thisHotkey) {
        return GetKeyState(prefix)
    }

    FireHotkey() {
        MsgBox %A_ThisHotkey%
    }

    GuiClose:
    GuiEscape:
    ExitApp

dftg