Clearing Up the Confusion about AutoHotkey’s IfWinActive Command Versus the #IfWinActive Directive
Using regular AutoHotkey commands versus AutoHotkey #Directives is a major source of confusion for novice script writers. I know this because when I first picked up AutoHotkey a number of years ago I encountered the same befuddlement. Some individual commands and #Directives are very similar in form and function (e.g. IfWinActive and #IfWinActive), yet how and when each can be used is very different.
* * *
New to AutoHotkey? See “Introduction to AutoHotkey: A Review and Guide for Beginners.”
* * *
In the last blog the IfWinActive command was employed isolating the action of Hotkeys to particular active windows. A similar type of context-sensitive action can be set up with the #IfWinActive directive. While the #IfWinActive directive looks very much like the IfWinActive command, #IfWinActive works in a different manner. Indeed all of the commands which are preceded with the hash mark (#), called directives, are processed separately from the regular AutoHotkey commands. It’s important to understand what AutoHotkey does if it encounters a #directive—and when it does it. Many beginners get confused about these two types of instructions (standard commands and #Directives) making script debugging more difficult.
For the purpose of writing and debugging scripts, it’s necessary to understand how AutoHotkey reads and processes AHK scripts. For simple scripts, it may appear that AutoHotkey just starts running the commands. However, there is a very specific two-part procedure engaged every time an AutoHotkey script launches. This two-phase process occurs when a script is first loaded.
Phase One of AutoHotkey File Processing
In the first phase, AutoHotkey reads the entire file loading all of the Hotkeys, Hotstrings, #Directives, functions, and Label subroutines. At this point, none of the standard AutoHotkey commands are executed. AutoHotkey sweeps through the entire script file implementing the setup sequence which prepares the script for running. Although, there may be some activity which is very similar to the execution of a program, no regular commands run. During this first sequence, external files may be loaded (#Include directive), context-sensitive conditions for Hotkeys and Hotstrings may be created (e.g. #IfWinActive, #If), and various other conditions may be set using #Directives.
The #Directives are only read and acted upon only during this first processing scan. The entire script file is loaded implementing every #Directive in the order found in the file. After this initial phase, #Directives can no longer be introduced or altered. Their sole purpose is to set up conditions for the app before any single standard AutoHotkey command runs. Once this first phase is completed, the auto-execute section at the top of the script (containing standard AutoHotkey commands) runs.
Phase Two of AutoHotkey File Processing
Once phase one is completed, AutoHotkey starts running any commands found in the auto-execute section at the beginning of the script. An auto-execute section is not required. For example, a script containing only Hotkey routines and Hotstring AutoCorrect text replacements would not need any auto-execute code. But, if there are actions which must be performed when the AutoHotkey script is first loaded, then they must be positioned first in the AHK file in this auto-execute section—before any Hotkey or Hotstring definitions, or any Return command. (#Directives may appear before or inside the auto-execute code as long as they are non-positional and do not enclose any Hotkeys or Hotstrings.)
Commands Versus #Directives
While AutoHotkey commands and #Directives may look very similar (e.g. IfWinActive versus #IfWinActive, respectively) and may even provide similar features, they serve a different purpose. Commands are action instructions which run in the auto-execute section, launch with Hotkey (and possibly Hotstring) activation, execute inside functions, or make up the working part of Label subroutines. Commands only run after the loading of all #Directives, Hotkey definitions, and Hotstring definitions; or once the entire script is loaded—as is the case with code launched by Hotkey combination activation. #Directives set conditions either for the entire app or based upon their position in the script before any standard AutoHotkey command ever runs.
In my last blog, the following command-driven code was used to isolate the Hotkey to a Google Chrome window:
$^W:: IfWinActive ahk_class Chrome_WidgetWin_1 Return Send ^W Return
Since it uses the IfWinActive command, it executes only after the script is fully loaded and the Hotkey combination CTRL+W (^W) is pressed. But there is an alternative approach to restricting Hotkeys to specific programs which use the #IfWinActive directive.
Here a similar effect is achieved with the preloaded #IfWinActive directive:
#IfWinActive ahk_class Chrome_WidgetWin_1 ^W::MsgBox Oops! You pressed CTRL+W #IfWinActive
In this case, before any command action is executed, the CTRL+W Hotkey combination is defined as only available when a Google Chrome window is active (ahk_class Chrome_WidgetWin_1). Each line of code (the two #Directives and one Hotkey) is set up during the phase one processing as AutoHotkey loads the file. These #Directives and the Hotkey are positional within the script file.
There are only a handful of #Directives which can be called positional. That means “they affect all hotkeys and hotstrings physically beneath them in the script. They are also mutually exclusive; that is, only the most recent one will be in effect.” These positional directives are #IfWinActive, #IfWinExist, #IfWinNotActive, #IfWinNotExist, #If [,expression], the group Hotstring option setting form of #Hotstring NewOptions, and #Include/#IncludeAgain for inserting external AHK script files into a loading script.
In the example above, the first #IfWinActive directive turns on the context-sensitive condition for the enclosed Hotkey combination while the second #IfWinActive directive (with no parameters) turns off the context-sensitive condition for the remainder of the script. (Tip: If the second #IfWinActive were not included, the first condition would apply to every other Hotkey or Hotstring which may follow (not precede) the first #IfWinActive directive in the AutoHotkey script.)
The #Directive code I originally used to block the shortcuts discussed in the last blog is as follows:
#IfWinActive ahk_class Chrome_WidgetWin_1 ^W::MsgBox Oops! You pressed CTRL+W !F4::MsgBox Oops! You pressed ALT+F4 ^F4::MsgBox Oops! You pressed CTRL+F4 #IfWinActive ahk_class MozillaWindowClass ^W::MsgBox Oops! You pressed CTRL+W !F4::MsgBox Oops! You pressed ALT+F4 ^F4::MsgBox Oops! You pressed CTRL+F4 #IfWinActive
#Directives such as #IfWinActive and #IfWinExist only apply to Hotkeys and Hotstrings following the #directive in the file—all of which are loaded first by AutoHotkey. A common novice error is placing regular AutoHotkey commands (e.g. Send, Gui) between these types of conditional directives expecting them to run. Since #Directives are only read during the first phase of AutoHotkey file processing and commands only run in the second phase, commands placed between two #Directives will not be affected by the directive conditionals and likely never processed by AutoHotkey. These conditional #Directives only work for Hotkey and Hotstring definitions.
When using these conditional #Directives, location in the script is very important. There are many other #Directives used for app settings where the location in the script does not matter.
With the exception of the #Directives listed above, the other #Directives are non-positional. That means each can be placed anywhere in the script and its setting will affect the entire script. Plus, the directive need only appear once. Generally, these non-positional #Directives tweak specific AutoHotkey settings. With a couple of exceptions, I’ve found very few reasons for new script writers to implement most non-positional #Directives. The most important ones which I’ve included at some point in my scripts are #SingleInstance, #Persistent, #NoTrayIcon, and #Hotstring EndChars NewChars.
Note: Non-positional #Directives cannot be conditionally applied because AutoHotkey ignores the regular conditional commands (If, IfWinActive, etc.) during the first processing phase when directives are loaded and the #IfWinActive and #If directives only work with Hotkey and Hotstring definitions.
I use #SingleInstance to ensure that either only one instance of a script is loaded or allow multiple copies of the same script to load. For example, the InstantHotkey.ahk script, available as a free download at the ComputorEdge AutoHotkey download site in the InstantHotkey.zip file, allows an unlimited number of temporary Hotkeys to be set up by turning #SingleInstance off. Each temporary Hotkey setup is a separate running of the same script.
I primarily use the #Persistent directive in the early stages of script development to prevent a script from automatically terminating after the auto-execute section runs. This saves the relaunching of the script each time I need to test it. (Then, I’m only required to Reload from the right-click context menu from the System Tray icon.) But, eventually, when a Hotkey, Hotstring, or GUI command is added to the script, the script automatically defaults to persistent.
I rarely use the #NoTrayIcon directive since I like the visual display of an icon in the Windows System Tray telling me when I have a script loaded. Plus, you need to add an alternative method for exiting the app since there will be no right-click menu to offer an easy out.
Unlike #Hotstring NewOptions, which resets specific options for all Hotstrings following the directive in the file, #Hotstring EndChars NewChars sets new activating end characters for all Hotstrings in the script. It need appear only once in a script and is global in nature affecting all Hotstrings—regardless of its location in the script.
I hope this clears up any confusion about the purpose and implementation of #Directives. Next time, we’ll look at adding a couple more tricks to Hotkeys and #Directives.