InputHook() [v1.1.31+]

创建一个对象, 该对象可用于收集或拦截键盘输入.

InputHook := InputHook(Options, EndKeys, MatchList)

参数

Options

由零个或多个下列选项组成的字符串(可任意顺序, 中间可选空格):

B: 设置 BackspaceIsUndo 为 0(false), 这会导致 Backspace 被忽略.

C: 设置 CaseSensitive 为 true, 使 MatchList 区分大小写.

I: 设置 MinSendLevel 为 1(true) 或给定值, 使任何输入级别低于该值的输入被忽略. 例如, I2 将忽略级别为 0(默认值) 或 1 的任何输入, 但将捕获级别为 2 的输入.

L: 长度限制(例如 L5). 输入的最大允许长度. 当文本达到这个长度时, 输入被终止, EndReason 被设置为单词 Max(除非文本匹配 MatchList 中的一个短语, 在这种情况下 EndReason 被设置为单词 Match). 如果未指定, 则长度限制为 1023.

指定 L0 禁用文本的收集和长度限制, 但并不影响按键生成的文本的统计(请参阅 VisibleText). 这可以与 OnChar, OnKeyDown, KeyOptEndKeys 参数组合使用.

M: 将修饰键击对应于真正的 ASCII 字符, 识别并转录修饰键击(如 Ctrl+ACtrl+Z). 参考这个例子, 它识别 Ctrl+C:

CtrlC := Chr(3) ; 将 Ctrl-C 对应的字符存储在 CtrlC 变量中.
ih := InputHook("L1 M")
ih.Start()
ih.Wait()
if (ih.Input = CtrlC)
    MsgBox, You pressed Control-C.

注意: 字符 Ctrl+ACtrl+Z 对应于 Chr(1)Chr(26). 此外, M 选项可能会导致某些键盘快捷键(如 Ctrl+) 在输入正在进行时出现异常.

T: 设置 Timeout(例如 T3T2.5).

V: 设置 VisibleTextVisibleNonText 为 1(true). 通常, 用户的输入会被阻止(对系统隐藏). 使用此选项可将用户的击键发送到活动窗口.

*: 通配符. 设置 FindAnywhere 为 1(true), 允许在用户键入的任何位置找到匹配项.

E: 按字符代码而不是键码处理单字符结束键. 如果活动窗口的键盘布局与脚本的键盘布局不同, 则可以提供更一致的结果. 它还可以防止实际上不会产生给定结束字符的键组合结束输入; 例如, 如果 @ 是结束键, 则在美式键盘中 Shift+2 将触发它, 但 Ctrl+Shift+2 不会触发(在使用 E 选项时). 如果还使用 C 选项, 则结束字符区分大小写.

EndKeys

一个由零个或多个按键组成的列表, 其中任何一个键在按下时终止输入(结束键本身不会写入输入缓冲). 当 Input 以这种方式终止时, EndReason 设置为单词 EndKey, EndKey 属性设置为键的名称.

EndKeys 使用类似于 Send 命令的格式. 例如, 指定 {Enter}.{Esc} 将使 Enter, .Esc 任一一个都能终止输入. 使用大括号本身作为结束键, 指定 {{} 和/或 {}}.

要使用 Ctrl, AltShift 作为结束键, 请指定键的左和/或右的版本, 而不是中性版本. 例如, 指定 {LControl}{RControl} 而不是 {Control}.

尽管不支持诸如 Alt+C(!c) 这样的修饰键, 而非-字母数字字符(如 ?!:@&{}) 默认情况下需要 ShiftAltGr 按键按下与否, 取决于字符的正常输入方式. 如果有 E 选项, 则将单个字符键名解释为字符, 在这种情况下, 修饰符键必须处于正确的状态才能生成该字符. 当同时使用 E 选项M 选项时, 通过在 EndKeys 中包括相应的 ASCII 控制字符来支持 Ctrl+ACtrl+Z.

还可以指定明确的虚拟按键代码, 例如 {vkFF}{sc001}. 这在键没有名称且按下时不产生可见字符的罕见情况下非常有用. 它的键码可以通过按键列表页面底部的步骤来确定.

MatchList

以逗号分隔的关键词列表, 其中任何一个都将导致终止输入(在这种情况下, EndReason 将被设置为单词 Match). 用户输入的内容必须完全匹配匹配列表中的某个词组(除非有 * 选项). 此外, 分隔符逗号周围的任何空格或制表符都是有意义的, 这意味着它们是匹配字符串的一部分. 例如, 如果 MatchListABC , XYZ, 则用户必须在 ABC 之后或 XYZ 之前键入空格以形成匹配.

两个连续的逗号产生单个原义逗号. 例如, 后面的匹配列表会在 string1 的末尾产生单个原义逗号: string1,,,string2. 类似的, 后面的匹配列表仅包含其中有一个原义逗号的单个项目: single,,item.

因为 MatchList 中的项目不被视为单独的参数, 所以列表可以完全包含在一个变量中. 事实上, 如果此列表的长度超过 16383, 那么列表的全部或部分必须包含在变量中, 因为这个长度是任何脚本行的最大长度. 例如, MatchList 可能由 %List1%,%List2%,%List3% 组成 -- 其中每个变量都包含匹配词组的子列表.

Input 堆栈

任何数量的 InputHook 对象都可以在任何时候创建和进行, 但是它们启动的顺序会影响 Input 的收集方式.

当每个 Input 开始时(通过 Start 方法或 Input 命令), 它被推到堆栈的顶部, 只有当输入终止时才从堆栈中删除. 键盘事件按最近开始到最早的顺序传递给每个输入. 如果一个输入抑制了一个给定的键盘事件, 那么它就不会再向下传递.

如果击键的发送级别低于 InputHook 的 MinSendLevel, 则忽略 Send 的击键. 在这种情况下, 击键仍然可以由堆栈中较低的输入来处理.

多个 InputHook 都可以与 MinSendLevel 一起使用, 以分别收集发送的击键和实际击键.

调用 Input 命令将终止任何由先前的 Input 命令启动的输入, 但保留任何活动的 InputHook. 如果输入 visible(不可见), 它中断的任何 InputHook 通常不会收集任何输入, 直到 Input 命令返回.

InputHook 对象

InputHook 函数返回一个 InputHook 对象, 该对象具有以下方法和属性.

方法

KeyOpt

设置按键或按键列表的选项.

InputHook.KeyOpt(Keys, KeyOptions)

参数

Keys

按键列表. 大括号用于括起按键名称, 虚拟键码或扫描码, 类似于 Send 命令. 例如, {Enter}.{{} 将应用于 Enter, .{ 按键. 按名称, {vkNN}{scNNN} 指定按键可能会产生三种不同的结果; 有关详情, 请参阅下文.

单独指定字符串 {All}(不区分大小写) 以便将 KeyOptions 应用于所有 VK 和所有 SC. 然后可以再次调用 KeyOpt 从特定按键中删除选项.

KeyOptions

下列单字符选项中的一个或多个(空格和制表符将被忽略).

-(减号): 删除 - 后面的任何选项, 直到下一个 +.

+(加号): 取消任何先前的 -, 否则无效.

E: 结束键. 如果启用, 则按下键终止 Input, 将 EndReason 设置为单词 EndKey, 将 EndKey 设置为键的标准名称. 与 EndKeys 参数不同, ShiftAltGr 键的状态将被忽略. 例如, @2 在美式键盘布局中都相当于 {vk32}.

I: 忽略文本. 通常由该键生成的任何文本都将被忽略, 并且该键被视为非文本键(请参阅 VisibleNonText). 如果键通常不产生文本, 则没有效果.

N: 通知. 在每次按下键时调用 OnKeyDownOnKeyUp 回调.

S: 处理后抑制(阻塞) 键. 这将覆盖 VisibleTextVisibleNonText 直到使用 -S. +S 意味着 -V.

V: 可见. 防止键被抑制(阻塞). 这将覆盖 VisibleTextVisibleNonText 直到使用 -V. +V 意味着 -S.

备注

选项可以通过虚拟键码和扫描码来设置, 并且是累加的.

当按名称指定一个键时, 选项通过 VK 或 SC 来设置. 如果两个物理键共享相同的 VK, 但 SC 不同(如 UpNumpadUp), 则由 SC 处理. 相反, 如果使用 VK 码, 则它将应用于产生该 VK 的任何物理键(这可能会随着时间的推移而变化, 这取决于活动键盘布局).

通过 VK 码删除选项不会影响 SC 设置的任何选项, 反之亦然. 但是, 当按键名删除一个选项并且该名称由 VK 处理时, 对应的 SC 也会删除该选项(根据脚本的键盘布局). 这允许在对所有按键应用一个选项后按名称排除按键.

如果通过 VK 设置 +V, 而通过 SC 设置 +S(反之亦然), 则 +V 优先.

Start

启动收集输入.

InputHook.Start()

如果 Input 已经在进行中, 则没有效果.

新启动的 Input 放在 InputHook 堆栈的顶部, 这允许它覆盖任何先前启动的 Input.

此方法安装键盘钩子(如果还没有安装).

Stop

终止 Input 并将 EndReason 设置为单词 Stopped.

InputHook.Stop()

如果 Input 不在进行中, 则没有效果.

Wait

等待, 直到 Input 终止(InProgress 为 false).

EndReason := InputHook.Wait(MaxTime)

参数

MaxTime

如果省略, 将无期限等待. 否则, 请指定等待的最大秒数. 如果 Input 在 MaxTime 秒后仍在进行中, 则该方法返回且不终止 Input.

返回值

此方法返回 EndReason.

常规属性

EndKey

返回结束键的名称, 按下结束键以终止 Input.

KeyName := InputHook.EndKey

注意, EndKey 返回键的 "标准" 名称, 而不管它在 EndKeys 参数中是如何编写的. 例如, {Esc}{vk1B} 都产生 Escape. GetKeyName() 可用于检索标准名称.

如果使用 E 选项, EndKey 返回输入的实际字符(如果合适). 否则, 键名将根据脚本的活动键盘布局来确定.

如果 EndReason 不是 "EndKey", EndKey 返回一个空字符串.

EndMods

返回在 Input 终止时逻辑上是按下的修饰符的字符串.

Mods := InputHook.EndMods

如果所有修饰符在逻辑上都按下, 则完整的字符串为:

<^>^<!>!<+>+<#>#

这些修饰符与热键的具有相同的含义. 每个修饰符总是用 <(左) 或 >(右) 限定. 对应的键名是: LCtrl, RCtrl, LAlt, RAlt, LShift, RShift, LWin, RWin.

InStr() 可以用来检查给定的修饰符(如 >!^) 是否存在的. 下面的行可以用来将 Mods 转换成中性修饰符的字符串, 如 ^!+#:

Mods := RegExReplace(Mods, "[<>](.)(?:>\1)?", "$1")

由于这是瞬间发生的, 此属性可能比 GetKeyState 更可靠, 即使在 Input 终止后立即调用 GetKeyState, 或者在 OnEnd 回调中.

EndReason

返回 EndReason 字符串, 该字符串表明了 Input 是如何终止的.

Reason := InputHook.EndReason

如果 Input 正在进行, 则返回空字符串.

InProgress

如果输入正在进行, 返回 1(true), 否则返回 0(false).

Boolean := InputHook.InProgress

Input

返回自上次 Input 启动以来收集的任何文本.

String := InputHook.Input

此属性可在输入进行中或输入结束后使用.

Match

返回导致 Input 终止的 MatchList 项目.

String := InputHook.Match

此属性返回匹配项(原始的大小写), 如果省略了 C 选项, 则返回的大小写可能与用户键入的大小写不同. 如果 EndReason 不是 "Match", 则返回空字符串.

OnEnd

检索或设置在 Input 终止时调用的函数对象.

CurrentFunc := InputHook.OnEnd
InputHook.OnEnd := NewFunc

如果已经赋值, CurrentFuncNewFunc, 否则, 为空字符串.

NewFunc 是要调用的函数对象, 例如 Func("MyCallback"). 空字符串意味着没有要调用的函数对象.

回调接受一个参数, 定义如下:

MyCallback(InputHook) { ...

尽管为参数指定的名称无关紧要, 但它被赋值了一个对 InputHook 对象的引用.

如果不需要相应的信息, 可以省略回调的参数.

该函数作为一个新线程调用, 因此使用 SendModeDetectHiddenWindows 等设置的默认值重新开始.

OnChar

检索或设置将字符添加到输入缓冲后调用的函数对象.

CurrentFunc := InputHook.OnChar
InputHook.OnChar := NewFunc

如果已经赋值, CurrentFuncNewFunc, 否则, 为空字符串.

NewFunc 是要调用的函数对象, 例如 Func("MyCallback"). 空字符串意味着没有要调用的函数对象.

回调接受两个参数, 定义如下:

MyCallback(InputHook, Char) { ...

尽管为参数指定的名称无关紧要, 以下值依次赋值给它们:

  1. 一个对 InputHook 对象的引用.
  2. 一个包含该字符(或多个字符, 有关详情, 请参阅下文) 的字符串.

如果不需要相应的信息, 你可以从回调函数参数列表的末尾省略一个或多个参数.

多个字符的存在表明在最后一次按键之前使用了一个死键, 但是这两个键不能转换为单个字符. 例如, 在一些键盘布局中 `e 产生 è, 而 `z 产生 `z.

当按下结束键时, 该函数不会被调用.

OnKeyDown

检索或设置当按下启用通知的按键时调用的函数对象.

CurrentFunc := InputHook.OnKeyDown
InputHook.OnKeyDown := NewFunc

键-按下通知必须首先由 KeyOptNotifyNonText 启用.

如果已经赋值, CurrentFuncNewFunc, 否则为空字符串.

NewFunc 是要调用的函数对象, 例如 Func("MyCallback"). 空字符串表示没有函数对象.

回调函数接受三个参数, 可以定义如下:

MyCallback(InputHook, VK, SC) { ...

虽然你给参数的名字并不重要, 但是下面的值会依次赋值给它们:

  1. 一个对 InputHook 对象的引用.
  2. 一个代表按键的虚拟键码的整数.
  3. 一个代表按键的扫描码的整数.

如果不需要相应的信息, 你可以从回调参数列表的末尾省略一个或多个参数.

要检索按键的名称(如果有), 请使用 GetKeyName(Format("vk{:x}sc{:x}", VK, SC)).

该函数作为一个新线程调用, 因此使用设置(如 SendModeDetectHiddenWindows) 的默认值重新开始.

当按下结束键时, 该函数不会被调用.

OnKeyUp [v1.1.32+]

检索或设置释放启用通知的按键时调用的函数对象.

CurrentFunc := InputHook.OnKeyUp
InputHook.OnKeyUp := NewFunc

按键-释放通知必须首先由 KeyOptNotifyNonText 启用. 键被认为是文本还是非文本取决于什么时候键被按下. 如果 InputHook 在没有检测到 key-down 的情况下检测到 key-up, 则认为它是非文本.

如果已经赋值, CurrentFuncNewFunc, 否则为空字符串.

NewFunc 是要调用的函数对象, 例如 Func("MyCallback"). 空字符串表示没有函数对象.

回调函数接受三个参数, 可以定义如下:

MyCallback(InputHook, VK, SC) { ...

虽然你给参数的名字并不重要, 但是下面的值会依次赋值给它们:

  1. 一个对 InputHook 对象的引用.
  2. 一个代表按键的虚拟键码的整数.
  3. 一个代表按键的扫描码的整数.

如果不需要相应的信息, 你可以从回调参数列表的末尾省略一个或多个参数.

要检索按键的名称(如果有), 请使用 GetKeyName(Format("vk{:x}sc{:x}", VK, SC)).

该函数作为一个新线程调用, 因此使用设置(如 SendModeDetectHiddenWindows) 的默认值重新开始.

选项属性

BackspaceIsUndo

控制 Backspace 是否从输入缓冲的末尾删除最近按下的字符.

CurrentSetting := InputHook.BackspaceIsUndo
InputHook.BackspaceIsUndo := NewSetting

如果已经赋值, CurrentSettingNewSetting, 否则默认为 1(true), 除非被 B 选项覆盖.

NewSetting 是一个布尔值, 用于启用或禁用此设置.

Backspace 用作 undo 时, 它被视为文本输入键. 具体来说, 是否抑制该键取决于 VisibleText 而不是 VisibleNonText.

如果 Backspace 与修饰符键(如 Ctrl) 结合使用, 则始终忽略 Backspace(检查的是修饰符的逻辑状态, 而不是物理状态).

注意: 如果输入文本是可见的(如在编辑器中) 并且使用箭头键或其他方法在其中导航, Backspace 仍然会删除最后一个字符, 而不是插入符号(插入点) 后面的字符.

CaseSensitive

控制 MatchList 是否区分大小写.

CurrentSetting := InputHook.CaseSensitive
InputHook.CaseSensitive := NewSetting

如果已经赋值, CurrentSettingNewSetting, 否则默认为 0(false), 除非被 C 选项覆盖.

NewSetting 是一个布尔值, 用于启用或禁用此设置.

FindAnywhere

控制每个匹配项是否可以是输入文本的子字符串.

CurrentSetting := InputHook.FindAnywhere
InputHook.FindAnywhere := NewSetting

如果已经赋值, CurrentSettingNewSetting, 否则默认为 0(false), 除非被 * 选项覆盖.

NewSetting 是一个布尔值, 用于启用或禁用此设置. 如果为真(true), 则可以在用户键入的任何位置找到匹配(匹配可以是输入文本的子字符串). 如果为假(false), 则用户键入的所有内容必须与 MatchList 中的一个短语匹配. 在这两种情况下, 都必须完整输入 MatchList 中的一个短语.

MinSendLevel

检索或设置要收集的输入的最小发送级别.

CurrentLevel := InputHook.MinSendLevel
InputHook.MinSendLevel := NewLevel

如果已经赋值, CurrentLevelNewLevel, 否则为 0, 除非被 I 选项覆盖.

NewLevel 应该是 0 到 101 之间的整数. 发送级别 低于 此值的事件将被忽略. 例如, 值 101 会忽略 SendEvent 生成的所有输入, 而值 1 只会忽略默认发送级别(0) 的输入.

无论这个设置如何, SendInputSendPlay 方法总是被忽略. 除了 AutoHotkey 以外的任何其他源生成的输入都不会因为这个设置而被忽略.

NotifyNonText

控制当按下非文本键时是否调用 OnKeyDownOnKeyUp 回调.

CurrentSetting := InputHook.NotifyNonText
InputHook.NotifyNonText := NewSetting

如果已经赋值, CurrentSettingNewSetting, 否则默认为 0(false).

NewSetting 是一个布尔值, 用于启用或禁用此设置. 如果为 true, 则为所有不产生文本的按键启用通知, 例如按下 Alt+F. 设置此属性不会影响按键的 options, 因为文本的生成取决于按下按键时活动窗口的键盘布局.

根据先前匹配 VK 码的 key-down 事件被归类为文本还是非文本, 考虑是否将 NotifyNonText 应用于 key-up 事件. 例如, 如果 NotifyNonText 为 true, 那么按下 Ctrl+A 将同时对 CtrlA 产生 OnKeyDownOnKeyUp 调用, 而按下 A 本身不会调用 OnKeyDown 或 OnKeyUp, 除非使用 KeyOpt 来启用该键的通知.

有关计为生成文本的键详细信息, 请参阅 VisibleText.

Timeout

检索或设置超时值(以秒为单位).

CurrentSeconds := InputHook.Timeout
InputHook.Timeout := NewSeconds

如果已经赋值, CurrentSecondsNewSeconds, 否则默认为 0, 除非被 T 选项覆盖.

NewSeconds 是一个浮点数字, 表示超时. 0 意味着没有超时.

超时周期通常在调用 Start 时启动, 但如果在 Input 进行中为该属性赋值, 则会重新启动. 如果在超时时间过去后 Input 仍在进行中, 则终止输入并将 EndReason 设置为单词 Timeout.

VisibleNonText

控制不产生文本的键或键组合是否可见(不阻塞).

CurrentSetting := InputHook.VisibleNonText
InputHook.VisibleNonText := NewSetting

如果已经赋值, CurrentSettingNewSetting, 否则默认为 1(true). V 选项 设置该选项为 1(true).

NewSetting 是一个布尔值, 用于启用或禁用此设置. 如果为真, 不产生文本的键和键组合可能触发热键或传递到活动窗口. 如果为假, 则会阻塞它们.

有关计为生成文本的键详细信息, 请参阅 VisibleText.

VisibleText

控制产生文本的键或键组合是否可见.

CurrentSetting := InputHook.VisibleText
InputHook.VisibleText := NewSetting

如果已经赋值, CurrentSettingNewSetting, 否则默认为 0(false), 除非被 V 选项.

NewSetting 是一个布尔值, 用于启用或禁用此设置. 如果为真(true), 产生文本的键和键组合可能触发热键或传递到活动窗口. 如果为假(false), 则会被阻止.

导致文本被附加到输入缓冲的任何击键都被视为生成文本, 即使在其他应用程序中通常不会这样做. 例如, 如果使用 M 选项, Ctrl+A 生成文本, 而 Esc 生成控制字符 Chr(27).

尽管死键通常不会立即产生效果, 但它们被视为产生文本. 按下死键也可能导致后面的键生成文本(如果只有死键的字符).

Backspace 仅在作为撤销时才被计为生成文本.

标准的修饰符键CapsLock, NumLockScrollLock 总是可见的(没有被阻塞).

EndReason

EndReason 属性返回以下字符串之一:

字符串 描述
Stopped Stop 方法已被调用或 Start 方法尚未被调用.
Max 输入达到允许的最大长度, 且它不匹配 MatchList 中的任何项.
Timeout Input 超时.
Match Input(输入) 匹配 MatchList 中的一项. Match 属性包含匹配的项.
EndKey

EndKeys 中的一个被按下终止了输入. EndKey 属性包含终止键的名称或字符, 不带大括号.

如果 Input(输入) 正在进行中, EndReason 为空.

备注

必须先调用 Start 方法, 然后才能收集输入.

InputHook 设计为允许脚本的不同部分都能监视输入, 并且冲突最小. 它可以连续操作, 例如观察任意的单词或其他模式. 它还可以临时运行, 例如收集用户输入或临时覆盖特定(或非-特定) 按键而不干扰热键.

当 Input 正在进行时, 键盘热键仍然有效, 但是如果任何必需的修饰符键被抑制, 或者如果热键使用 reg 方法并且其后缀键被抑制, 则不能激活热键. 例如, 热键 ^+a:: 可能 会被 InputHook 覆盖, 而热键 $^+a:: 会优先, 除非 InputHook 抑制 CtrlShift.

按键抑制(阻止) 与否取决于以下因素(按顺序):

输入正在进行时需要键盘钩子, 但如果在输入终止时, 若不再需要键盘挂钩, 则会自动卸载. 键盘钩子的存在导致脚本暂时变为持久的, 这意味着可能需要 ExitApp 而不是 Exit 来终止它.

AutoHotkey 不支持输入法编辑器(IME). 键盘钩子拦截键盘事件, 并通过使用 ToUnicodeEx 或 ToAsciiEx 将它们转换为文本(VK_PACKET 事件除外, 它封装了单个字符).

如果您使用多种语言或键盘布局, 则 InputHook 会使用活动窗口的键盘布局而不是脚本的(不论 Input 是否可见).

尽管不够灵活, 但热字串更容易使用.

InputHook vs. Input

InputHook 和 Input 命令是用于相同底层功能的两个不同接口. 以下内容大致相同:

Input, OutputVar, %Options%, %EndKeys%, %MatchList%
ih := InputHook(Options, EndKeys, MatchList)
ih.Start()
ErrorLevel := ih.Wait()
if (ErrorLevel = "EndKey")
    ErrorLevel .= ":" ih.EndKey
OutputVar := ih.Input

Input 命令终止任何先前由它启动的 Input, 而 InputHook 允许同时多个 Input.

Options 解释相同, 但默认设置不同:

Input 命令在进行时会阻塞线程, 而 InputHook 允许线程继续, 甚至退出(这允许中断的任何线程继续运行). 脚本可以注册一个 OnEnd 函数, 当输入终止时调用该函数, 而不是等待.

Input 命令只在输入终止后返回用户的输入, 而 InputHook 的 Input 属性允许在任何时候检索它. 脚本可以注册一个 OnChar 函数, 以便在字符增加时调用, 而不是不断地检查 Input 属性.

InputHook 通过 KeyOpt 方法对单个按键提供了更多控制. 这包括添加或删除结束键, 抑制或不抑制特定键, 或忽略特定键生成的文本.

与 Input 命令不同, InputHook 可用于检测不产生文本的键, 而 终止输入. 这是通过注册 OnKeyDown 函数和使用 KeyOptNotifyNonText 来指定相关键来完成的.

如果 MatchList 中的项目导致 Input 终止, 可以参考 Match 属性来确定到底是哪个匹配(当 * 选项存在时这更有用).

尽管脚本可以在输入命令返回后查询 GetKeyState, 但有时它无法准确反映输入终止时按下了哪些键. InputHook 的 EndMods 属性反映了 Input 终止时修饰键的逻辑状态.

在向后兼容性方面有一些不同之处:

Input, KeyWait, 热字串, InputBox, #InstallKeybdHook, 线程, If Var [not] in/contains MatchList

示例

等待用户按下任意一个键.

MsgBox % KeyWaitAny()

; 再来一遍, 但不阻止按键.
MsgBox % KeyWaitAny("V")

KeyWaitAny(Options:="")
{
    ih := InputHook(Options)
    if !InStr(Options, "V")
        ih.VisibleNonText := false
    ih.KeyOpt("{All}", "E")  ; 结束
    ih.Start()
    ErrorLevel := ih.Wait()  ; 将 EndReason 存储在 ErrorLevel 中
    return ih.EndKey  ; 返回按键名称
}

等待任何与 Ctrl/Alt/Shift/Win 组合的按键.

MsgBox % KeyWaitCombo()

KeyWaitCombo(Options:="")
{
    ih := InputHook(Options)
    if !InStr(Options, "V")
        ih.VisibleNonText := false
    ih.KeyOpt("{All}", "E")  ; 结束
    ; 排除修饰符
    ih.KeyOpt("{LCtrl}{RCtrl}{LAlt}{RAlt}{LShift}{RShift}{LWin}{RWin}", "-E")
    ih.Start()
    ErrorLevel := ih.Wait()  ; 将 EndReason 存储在 ErrorLevel 中
    return ih.EndMods . ih.EndKey  ; 返回字符串, 如 <^<+Esc
}

简单的自动完成: 一周中的任何一天. 除了字母双关外, 这是一个功能齐全的例子. 只需运行脚本并开始输入, 按 Tab 完成或按 Esc 退出.

global WordList := "Monday`nTuesday`nWednesday`nThursday`nFriday`nSaturday`nSunday"

global Suffix := "", SacHook

SacHook := InputHook("V", "{Esc}")
SacHook.OnChar := Func("SacChar")
SacHook.OnKeyDown := Func("SacKeyDown")
SacHook.OnEnd := Func("SacEnd")
SacHook.KeyOpt("{Backspace}", "N")
SacHook.Start()

SacChar(ih, char)  ; 当一个字符被添加到 SacHook.Input 时调用.
{
    Suffix := ""
    if RegExMatch(ih.Input, "`nm)\w+$", prefix)
        RegExMatch(WordList, "`nmi)^" prefix "\K.*", Suffix)
    
    ToolTip % Suffix, % A_CaretX + 15, % A_CaretY    
    
    ; 只在显示工具提示时拦截 Tab 键.
    ih.KeyOpt("{Tab}", Suffix = "" ? "-NS" : "+NS")
}

SacKeyDown(ih, vk, sc)
{
    if (vk = 8) ; 退格键
        SacChar(ih, "")
    else if (vk = 9) ; Tab 键
        Send % "{Text}" Suffix
}

SacEnd()
{
    ExitApp
}