Encapsulate AutoHotkey Code for Multi-Script Integration and Portability (Scripting Techniques)

Run Your AutoHotkey Scripts as Standalone Apps or Add Them Unaltered to Master Scripts by Creating Hermetically Sealed Auto-Execute Modules

If you’re anything like me, you’ve written a number of scripts which you use regularly. You can run each app separately assuring that they don’t conflict but that tends to load the Windows System Tray with too many icons—making it a little difficult to track all of them.

Cover 250 BorderOn the other hand, combining the scripts into a single file presents problems of its own. Any script which contains its own auto-execute section must run this part of the code when it first loads. Simply using the #Include directive won’t do the job. Place it in the top of the file and the other script modules, such as Hotkeys, Hotstrings, and Label subroutines, stop the processing of the original auto-execute section. Yet, if you place the #Include at the bottom, AutoHotkey never sees or runs the new auto-execute section. The novice scriptwriter often solves the problem by breaking apart the script and separately adding the auto-execute section to the top of the master file while locating all the other code modules toward the end of the file.

Plus, if you tend to use the standard variable names in your subroutines, they may cause conflicts with variables in the combined script. If your master script includes multiple GUI pop-up windows from different client scripts, they require differentiation to prevent AutoHotkey from using or altering the wrong app pop-up.

You might find it difficult to add a particular script to an omnibus script—unless you first encapsulate your script to run either unaltered on its own or as part of another longer script. If you keep these robust, flexible design recommendations in mind when writing any script, you will encounter very few problems when combining scripts—plus each runs independently without alteration.

The Modularity of AutoHotkey

For the most part, AutoHotkey scripts consist of independent modules. Hotkeys, Hotstrings, Label subroutines, and functions all exist and operate without interfering with each other. With exception of the auto-execute section at the beginning of the script, these snippets of code may appear almost anywhere within the AutoHotkey file without causing problems. This makes combining many scripts easy.

For example, if your script contains nothing but Hotkeys, Hotstrings, functions, and Label subroutines sealed with the Return command (no auto-execute section), you can add it to any other script by simply using the #Include directive with the path and name of the script at any location following the last line of the auto-execute section. With the exception of possible variable name conflicts and/or GUI pop-up confusion, these modules should run without problem. Most complications arise when combining the auto-execute sections from various scripts. However, you can hermetically seal the auto-execute sections of your scripts to run both independently or as part of a larger script.

Writing Portable Auto-Execute Sections

The auto-execute section may provide two purposes for an AutoHotkey script: (1) one-time-only setup of variables and GUIs and  (2) initialization or refresh of app features. The one-time setup only occurs when first loading the script. In fact, running it again without deleting the results of the first load might generate an error. The initialization (or refresh) phase processes the primary features of the app. This portion may need to run again—whenever the process must update values or features.

Using the QuickLinks.ahk script as an example, the auto-execute section of the script includes two parts. The first checks for the default folders and, if missing, sets them up:

IfNotExist, C:\Users\%A_UserName%\QuickLinks\
  FileCreateDir, C:\Users\%A_UserName%\QuickLinks\

IfNotExist, C:\Users\%A_UserName%\QuickLinks\Tools\
  FileCreateDir, C:\Users\%A_UserName%\QuickLinks\Tools\

; These two lines add the Windows Tools menu item (God Mode)—if missing. Delete if you don't want it.

IfNotExist, C:\Users\%A_UserName%\QuickLinks\Tools\Windows Tools.{ED7BA470-8E54-465E-825C-99712043E01C}
  FileCreateDir, C:\Users\%A_UserName%\QuickLinks\Tools\Windows Tools.{ED7BA470-8E54-465E-825C-99712043E01C}

This section only needs to run one time when first loaded.

The second part builds the QuickLinks menus. In addition to running when first loaded, AutoHotkey must process the code again every time you make a change to the menu items. In the original version of the script, the Reload command updated the menus by reprocessing the entire script. However, if added to a larger collection of scripts, Reload would force the entire omnibus to reset—possibly losing important values and settings in other client scripts. We deal with these problems by adding two Label names and sealing the auto-execute portion of the script, then replacing the Reload command with a menu reset.

Encapsulating Scripts with Label Names

By adding two different Label names to the auto-execute section of the script, we create an independent app which we can easily add to any other AutoHotkey script. In the QuickLinks script, I added QuickLinksSetup: for code running only once and QuickLinksReset: multiple use code. The Label names point to specific locations in the code for accessing routines:

QuickLinksSetup:

IfNotExist, C:\Users\%A_UserName%\QuickLinks\
  FileCreateDir, C:\Users\%A_UserName%\QuickLinks\

IfNotExist, C:\Users\%A_UserName%\QuickLinks\Tools\
  FileCreateDir, C:\Users\%A_UserName%\QuickLinks\Tools\

; These two lines add the Windows Tools menu item (God Mode)—if missing. Delete if you don't want it.

IfNotExist, C:\Users\%A_UserName%\QuickLinks\Tools\Windows Tools.{ED7BA470-8E54-465E-825C-99712043E01C}
  FileCreateDir, C:\Users\%A_UserName%\QuickLinks\Tools\Windows Tools.{ED7BA470-8E54-465E-825C-99712043E01C}

QuickLinksReset:

.
.
.
; This section includes the code for setting up the QuickLinks menus.
.
.
.

Return

Now I can add the script to any other script by merely inserting two new lines in the compilation file: one for running the auto-execute section and the other for adding all of the code.

Note: In many cases, the initialization phase (e.g. QuickLinksReset:) may activate through a called function or subroutine not requiring the use of a second Label name within the auto-execute section. By far the key to making it easy to combine script lies in adding the first Label name enclosing the auto-execute section.

In the master script somewhere in the auto-execute portion, I add a GoSub command with the Label name pointing to the top of the included script:

GoSub, QuickLinksSetup

This runs when loading the master script, setting up up the QuickLinks app, but before it can work, I must insert the #Include directive into the master script—probably somewhere toward the end of the file:

#Include C:\AutoHotkey\QuickLinks\QuickLinks.ahk

The #Include directive inserts the code from the QuickLinks.ahk file into the master script at that exact location. When the GoSub command runs, it jumps to the QuickLinksSetup: point and starts processing the code as if the auto-execute section of QuickLinks.ahk were integrated with the top of the master file.

Since AutoHotkey ignores Label names (unless called), the code after the Label name QuickLinksReset: runs as part of the auto-execute section. AutoHotkey later uses the QuickLinksReset: access point to update the menus after modifications, however, we can no longer use the Reload command. In place of Reload, we add code to destroy the old menu, then rerun the script starting at the QuickLinksReset: point:

QL_ReloadHandler:
  Menu, QuickLinks, Delete
  Loop, C:\Users\%A_UserName%\QuickLinks\*.*, 2 , 0 
  { 
  Menu, %A_LoopFileName%, Delete
  }
  GoSub, QuickLinksReset
Return

This code deletes the top-level menu, then uses the Loop command to delete the remaining submenus. Without this menu destroying step, the old menus might include possibly non-functional menu items with new menu items merely added to the end of the list.

Calling QuickLinksReset: with the GoSub command, reruns the menu setup portion of the QuickLinks auto-execute renewing the menu structure.

By using this two-Label-name approach to encapsulating the auto-execute section of a script, you can make almost any AutoHotkey app run without major changes as either a standalone script or as an add-on to another script. But, you can make your scripts even more robust and less susceptible to conflict with a few more best practices.

Preventing Conflicts Between Scripts

When integrating diverse scripts in one file, the risk of conflicts increases. Depending on how you write the code, you may either generate strange behavior or encounter obvious errors. These foibles usually occur due to conflicting variables, subroutines, GUIs (Graphical User Interfaces) or functions. Also, duplicate Hotkeys and Hotstrings may pop up—in which case the last one takes precedence. Taking steps to prevent these problems (or, at least, reduce their likelihood) can save a great deal of trouble and debugging work.

Variable Names

As creatures of habit, we tend to use certain variable names for particular actions within our code. Although rarely a problem in a single application script, we might encounter major complications when combining two or more such scripts. To protect against this eventuality, assign unique tags to variable names.

For example, I concatenated the script identifier QL (QuickLinks) to the front of each variable. The variable NewName changed to QL_NewName (NewName ⇒ QL_NewName). Doing this makes a conflict with another script which also uses unique variable names very unlikely.

Label Names

Similar to variable names, common Label subroutine names can also cause conflicts between apps. (For that matter, you can add menu names to the list of possible problems.) Bulletproof your Label (and menu) names by adding that same identifier to the front of each Label and menu or submenu:

 Menu, QuickLinks, Add, Add Links, :QL_AddLinksMenu
 Menu, QuickLinks, Add, Reload QuickLinks, QL_ReloadHandler
 Menu, QuickLinks, Add, QuickLinks Help, QL_HelpHandler
 Menu, Tray, Add, Reload QuickLinks, QL_ReloadHandler

The subroutines and menus now have names with a low probability of appearing in any other script.

GUI (Graphical User Interface) Names

If you don’t distinguish between different GUI pop-ups in your script then AutoHotkey does everything in the same window object. Only one basic GUI without special naming may appear in a single script. We solve the problem of combining multiple GUI scripts by adding names to each pop through the GUI command. As an example for discussion next time, I make the ScreenDimmer.ahk script a more portable app by adding the name Dimmer: to each GUI command:

Gui Dimmer:+AlwaysOnTop
Gui, Dimmer:Add, Text, vDim x0 y0, Dimmer
Gui, Dimmer:Add, Text, vBright x0 y0, Brighter
Options := "Range60-180 NoTicks Buddy1Dim Buddy2Bright vMySlider gSD_Dimmer"
Gui, Dimmer:Add, Slider
    , W200 x50 y5 AltSubmit Tooltip Reverse %options% , 128
Gui, Dimmer:Add, StatusBar, gSD_Reset
    , Default Brightness 128 `t`t(Click Status Bar to Reset)

The GUI name Dimmer: distinguishes it from any other GUI pop-ups in the script. This technique along with those discussed above make the ScreenDimmer.ahk script more portable.

Although the Gui, New command automatically differentiates GUI pop-ups, the hardcoding of unique names as shown makes the script more portable and robust with less complication.

Function Names (Possibly)

You may, or may not, want to change function names. If the function returns standard value for a number of different scripts, then you could reuse the same function for multiple apps. However, only one function by the same name may appear in any script. You should keep your standard functions in a separate .ahk file for inclusion (#Include) in the main script at runtime. If a conflicting function does appear in the combined script, you need to deactivate that function in your current script to prevent encountering an error—which likely disables the standalone capability.

Bottomline, the safest approach involves adding the same unique identifiers to function names:

AddIcon(menuitem,submenu) ⇒ QL_AddIcon(menuitem,submenu)

On the bright side, you won’t need to alter variable names within functions. In most cases, unless set as a Global variable, all function variables are local. That means they do not affect any other variables outside the function nor do outsides variable affect them—even if they use the same variable name.

Next time, I plan to encapsulate the ScreenDimmer.ahk script as a demonstration of naming GUI windows for portability to other scripts.

jack

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s