# Hotkey

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

    Hotkey KeyName , Action, Options

## 参数

KeyName

  类型: 字符串

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

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

  KeyName 也可以是现有的热键的名称(用双冒号语法创建), 这样会使用此函数的其他参数值更新原来的热键.

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

  热键首次创建时 -- 无论是通过 Hotkey 函数还是脚本中的双冒号语法 -- 其键名以及修饰符的顺序成为此热键的固定名称, 正如 A_ThisHotkey 和 Action 的参数所反映的那样. 这个名称由热键的所有变体共享, 并且即使之后 Hotkey 函数使用不同的修饰符顺序操作此热键, 此名称也不会改变.

  如果热键变体已经存在, 则根据 KeyName 是否包含颚化符前缀(~) 更新它的行为.

  使用钩子前缀($) 可添加到现有热键上. 这个前缀影响所有热键的变体且不能被删除.

Action

  类型: 字符串或函数对象

  当按下热键时, 要执行的动作名或函数(作为一个新的线程). 它可以是函数名称, 函数对象, 或不带冒号的热键名称, 或下面列出的特殊值之一.

  如果 Action 是一个函数, 那么它的调用只有一个参数, 即热键的名称. 用双冒号语法定义的热键自动使用参数名 `ThisHotkey`. 热键也可以不使用热键函数而指定一个函数名.

  如果 Action 是热键名称, 则使用其原始函数; 具体来说, 就是对应当前 HotIf 条件的热键变体的原始函数. 这通常用于恢复热键改变后的原始函数, 但也可以用于分配不同热键的函数, 前提是两个热键使用相同的 HotIf 条件.

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

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

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

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

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

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

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

Options

  类型: 字符串

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

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

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

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

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

  S 或 S0: 指定字母 S 热键免于挂起, 这允许使用热键来关闭挂起. 指定 S0(S 和数字 0) 来移除该豁免, 允许热键被挂起.

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

  In(InputLevel): 指定字母 I(或 i) 后跟随热键的输入级别. 例如: `I1`.

  如果任一选项字母被忽略并且热键已经存在, 这些选项将不会被更改. 但是, 如果热键还不存在 -- 也就是说, 它将由这个函数创建 -- 这些选项将默认为最近生效的那些. 例如, 如果 #MaxThreadsBuffer 出现在脚本的最底部, 也会被使用. 如果脚本中没有出现 #MaxThreadsBuffer, 那么将使用它的默认设置(在本例中为 OFF).

## 错误处理

如果参数无效或内存分配失败, 则抛出异常.

如果热键无效或无法创建, 则会抛出以下异常:

错误类 | 消息 | 说明
--- | --- | ---
ValueError | Invalid key name.(无效的按键名称.) | KeyName 参数指定一个或多个当前键盘布局/语言不识别或不支持的键. Exception.Extra 包含键名; 例如 `!Entre` 中的 "Entre".
Unsupported prefix key.(不支持的前缀键.) | 例如, 不支持将鼠标滚轮作为热键 `WheelDown & Enter` 的前缀. Exception.Extra 包含前缀键.
This AltTab hotkey must have exactly one modifier/prefix. | KeyName 参数不适合与 AltTab 或 ShiftAltTab 动作一起使用. 需要两个键(至多) 的组合. 例如: `RControl & RShift::AltTab`. Exception.Extra 包含 KeyName.
This AltTab hotkey must specify which key (L or R).
TargetError | Nonexistent hotkey.(不存在的热键.) | 该函数试图修改一个不存在的热键. Exception.Extra 包含 KeyName.
Nonexistent hotkey variant (IfWin). | 该函数试图修改一个不存在的现有热键变体. 要解决这个问题, 请使用 HotIf 设置条件使其与要修改的热键的条件相匹配. Exception.Extra 包含 KeyName.
Error | Max hotkeys. | 创建这个热键将超过每个脚本 32762 个热键的限制(不过, 每个热键可以有无限数量的变体, 而且也没有对热字串数量的限制).

提示: Try-Catch 可以用来测试是否存在热键变体. 例如:

    try
        Hotkey "^!p"
    catch TargetError
        MsgBox "The hotkey does not exist or it has no variant for the current If criteria."

## 备注

当前的 HotIf 设置决定了 Hotkey 函数将操作热键的哪个变体.

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

通过双冒号语法创建的热键比使用 Hotkey 函数执行地更好, 因为在脚本启动时会批量启用它们(而非一个一个地启用). 因此, 最好使用此函数仅创建那些在脚本开始运行后才知道其键名的热键. 一种此类情况是, 当通过 INI 文件来配置脚本各种操作的热键时使用此函数.

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

如果由这个函数所做的更改是合理的将会安装或移除键盘和/或鼠标钩子.

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

脚本中至少有一个热键时, 它会变成持续运行的, 这意味着应该使用 ExitApp 而不是 Exit 来终止它.

## 变体(副本) 热键

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

    HotIfWinActive "ahk_class Notepad"
    Hotkey "^!c", MyFuncForNotepad
    HotIfWinActive  "ahk_class WordPadClass"
    Hotkey "^!c", MyFuncForWordPad
    HotIfWinActive
    Hotkey "^!c", MyFuncForAllOtherWindows

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

创建重复热键时, 修饰符的顺序(如 ^!+#) 没有关系. 例如, `"^!c"` 等同于 `"!^c"`. 但是, 按键必须拼写一致. 例如, 用于此目的时, Esc 不同于 Escape(尽管大小写无关). 最后, 任何带有通配符前缀(*) 的热键和不带通配符的热键是完全独立的; 例如, `"*F1"` 和 `"F1"` 都有他们各自的变体.

有关上下文相关的热键的更多信息, 请参阅 #HotIf 的备注.

## 相关

Hotkey Symbols, HotIf, A_ThisHotkey, #MaxThreadsBuffer, #MaxThreadsPerHotkey, Suspend, 线程, Thread, Critical, Return, Menu 对象, SetTimer

## 示例

创建 Ctrl-Alt-Z 热键.

    Hotkey "^!z", MyFunc

    MyFunc(ThisHotkey)
    {
        MsgBox "You pressed " ThisHotkey
    }

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

    Hotkey "RCtrl & RShift", "AltTab"

禁用 Shift-Win-C 热键.

    Hotkey "$+#c", "Off"

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

    Hotkey "^!a",, "T5"

创建仅在记事本中有效的热键 `Alt`+`W`.

    HotIfWinActive "ahk_class Notepad"
    Hotkey "!w", ToggleWordWrap  ; !w = Alt+W

    ToggleWordWrap(ThisHotkey)
    {
        MenuSelect "A",, "Format", "Word Wrap"
    }

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

    HkGui := Gui()
    HkGui.Add("Text", "xm", "Prefix key:")
    HkGui.Add("Edit", "yp x100 w100 vPrefix", "Space")
    HkGui.Add("Text", "xm", "Suffix hotkey:")
    HkGui.Add("Edit", "yp x100 w100 vSuffix", "f & j")
    HkGui.Add("Button", "Default", "Register").OnEvent("Click", RegisterHotkey)
    HkGui.OnEvent("Close", (*) => ExitApp())
    HkGui.OnEvent("Escape", (*) => ExitApp())
    HkGui.Show()

    RegisterHotkey(*)
    {
        Saved := HkGui.Submit(false)
        HotIf (*) => GetKeyState(Saved.Prefix)
        Hotkey Saved.Suffix, (ThisHotkey) => MsgBox(ThisHotkey)
    }

dftg