AutoHotkey BlockInput Command May Cause Stuck Keys! Fix It with the KeyWait Command
In the last blog, we dealt with the issue of setting the privilege level required to use the BlockInput command. In the BackupText.ahk and IncrementalSaveText.ahk scripts, the AutoHotkey command prevents user mouse/keyboard input while the script selects and copies text to the Windows Clipboard, but it doesn’t work without Administrator privileges. After raising the script to a higher level, we demonstrated how to use Windows Task Manager to bypass the User Account Control (UAC) warning window.
At the end of the blog, I mentioned an additional problem where BlockInput causes keys (usually one or more from the Hotkey combination) to stick in the down position. Here’s the trouble.
If BlockInput goes into effect before releasing any of the keys in the Hotkey combination, Windows cannot receive the KeyUp message. When the key gets released during the mouse/keyboard hiatus (before turning off BlockInput), the key remains logically in the down position. Not good!
The relatively simple fix involves adding the KeyWait command to the script for each key in the Hotkey combination:
KeyWait, Control ; avoid sticky key KeyWait, Alt ; avoid sticky key KeyWait, b ; avoid sticky key
The online documentation states that KeyWait command “waits for a key or mouse/joystick button to be released or pressed down.” Since the script does not begin until you press down all of the keys in the combination simultaneously, they necessarily start in the down position. By adding the KeyWait lines of code, the script pauses (before executing BlockInput) until each key reaches the up position.
When I first added BlockInput to the scripts, I didn’t encounter this stuck key problem because (as I pointed out last time) the BlockInput command wasn’t actually working—at least not until I upped the privilege level of the EXE file. After that, I started noticing the dead keys. However, the warning does appear in the documentation:
If BlockInput becomes active while the user is holding down keys, it might cause those keys to become “stuck down.”
That gives us two basic rules of thumb for properly implementing the BlockInput command:
- For the BlockInput command to take effect, compile the script, and elevate the privilege of the EXE file to Administrator—as discussed in the last blog. (If you want to bypass the pop-up warning window, use Windows Task Manager to run the EXE file.)
- Use the KeyWait command to ensure that no keys get stuck in the down position.
The full BackupText.ahk script:
^!B:: KeyWait, Control ; avoid sticky key KeyWait, Alt ; avoid sticky key KeyWait, b ; avoid sticky key BlockInput, On ; turns off mouse/keyboard Send, ^a ; select all Sleep, 100 Send, ^c ; copy selection to keyboard Sleep, 100 Click ; deselect all select BlockInput, Off IfExist, C:\Users\%A_UserName%\Documents\SaveEdit.txt { FileDelete, C:\Users\%A_UserName%\Documents\SaveEdit.txt } FileAppend , %clipboard%, C:\Users\%A_UserName%\Documents\SaveEdit.txt Return
The order of the key releases does not matter. The script pauses only as long as one of the group remains down—regardless of which one.
Alternative Use for KeyWait
It seemed obvious that the KeyWait command could offer another method for pausing a script. KeyWait gives you a tool for creating an indefinite pause in a script until you press a specific key. Yet, initially, I had trouble getting it to work. Based upon the documentation, I thought that the command by default waited for a key change of state. Not so!
More precisely, KeyWait pauses for a key release (default) only if already pressed. If detected in the up position, AutoHotkey ignores the command and moves on. The command only waits for a key press when you add the Down (D) option. In other words, the default mode of KeyWait (with no options) looks for a key release when it’s in the down position at the time of the command.
By adding a SplashText command for notification, KeyWait creates an indefinite pause:
SplashTextOn , 200, 40, Continue!, Press F to continue…
KeyWait, f, D
SplashTextOff
At first, the script didn’t pause because I had assumed that AutoHotkey waited for a change in key state. Once I added the D option, it worked.
Caution: Take care when selecting an activation (resume) key. The F key used above causes the insertion of the letter f into any editing window active at the time you execute the script or routine. In fact, any key exhibits its usual behavior—depending upon the active window. Maybe, choose an innocuous key such as LEFT SHIFT (LShift). That way it’s unlikely that ending the pause will cause any collateral damage.
A better approach might be to assign a fake Hotkey combination using more than one key:
SplashTextOn , 200, 40, Continue!, Press CTRL+D to continue… KeyWait, Ctrl, D KeyWait, d, D SplashTextOff
While it appears that CTRL+D acts as a Hotkey combination, in truth, AutoHotkey waits first for the CTRL key down, then the D key. Doing them as if a Hotkey combination works, while, unlike the default form of KeyWait (no options), pressing them separately in the wrong order does not. (Whichever keys you choose, ensure that they don’t comprise a real Hotkey combination or Windows shortcut.) This approach prevents random letters from appearing in your editing windows.
Tip (November 14, 2017): I can’t overemphasize the importance of picking a non-Windows shortcut. I ran this program as a test of other activity after selecting the file from Windows Explorer (File Explorer). When I used the CTRL+D combination, I accidentally deleted the script itself. I had to recover it from Windows 10 File History. It turns out that CTRL+D deletes files in Windows Explorer.
If no Hotkeys or Hotstrings appear in your script, you need to install the Keyboard Hook (#InstallKeybdHook) to make KeyWait operational. (Hotkeys and Hotstrings automatically install the Keyboard Hook.)
* * *
Next time, I’m moving on to take another look at an old script which I highlighted a number of years ago. While I add a couple of things to update it and implement a few tricks, the script highlights an important technique for tracking windows data on the fly.
* * *
Like anybody else, I have expenses and a need to make ends meet. As “Jack’s AutoHotkey Blog” increases in popularity, coding the test scripts and writing the blogs takes up more of my time. That means I’ve less time to pursue other income-earning opportunities. I don’t plan to ever move “Jack’s AutoHotkey Blog” behind a paywall, but if you think my efforts are worth a bit of your hard-earned cash, then you can offer a token of your appreciation by purchasing some of my AutoHotkey books. You may not need the references yourself, but you might know someone who can benefit from one or two of them.
Thank you,
[…] more information on how to run Task Scheduler see the blog “Stop Accidental Deletions with the BlockInput Command (AutoHotkey Tip—Part Two).” I previously used Task Scheduler to resolve a privilege issue with a script. That blog […]
LikeLike
Here’s some forum code I modified a bit with a suspend. DisplayName is the Title set or use ahk_id.
KeyCombination:=””
ExcludeKeys := “{Shift Up}{Control Up}{Alt Up}{WheelUp Up}{WheelDown Up}”
; Save active window
WinGet, Wswitch, ID, A
Suspend, On
; Battleeye workaround. Make the script the active window with hotkeys disabled. (Battleeye blocks autohotkey if it’s the active window)
WinWait, %DisplayName%,
IfWinNotActive, %DisplayName%, , WinActivate, %DisplayName%,
WinWaitActive, %DisplayName%,
ControlFocus,, %DisplayName%
; Battleeye workaround. Delay to disengage, you can take this out otherwise.
Sleep, 50
; Get all the keys currently pressed
Loop, 0xFF
{
If GetKeyState(Key:=Format(“VK{:02X}”,0x100-A_Index))
{
If !InStr(ExcludeKeys,Key:=”{” GetKeyName(Key) ” Up}”)
KeyCombination .= RegexReplace(Key,”Numpad(\D+)”,”$1″)
}
}
BlockInput, On
SendInput, %KeyCombination%
Suspend, Off
; ….. Do whatever here, all keys are blocked and up.
; This is as simple as I could get while getting around the Battleeye problem in Steam.
LikeLike