Skip to content

Automation With Autohotkey

gnab-gib-se edited this page Sep 17, 2023 · 3 revisions

If you need to send keystrokes or mouse clicks to a Windows app (e.g. an installer), AutoHotkey is usually the tool for the job.

It has quite a bit of documentation, and a quick-start tutorial. The most useful documentation is its command reference. AutoHotkey for Winetricks contributors

Winetricks takes care of installing and starting autohotkey for you; all you have to do is call w_ahk_do with the desired autohotkey commands.

Hello, Autohotkey

Let's try the canonical autohotkey example: starting Notepad and typing some text. To do this, create a file named hello.verb containing these lines:

w_metadata hello apps \
    title="Hello, Autohotkey"

load_hello()
{
    w_ahk_do "
        Run notepad.exe               ; Comments start with semicolons
        WinWait Untitled - Notepad    ; Wait for a window with this exact title to appear
        Send hello, autohotkey        ; Act as if the user typed those words at the keyboard
    "
}

Then try running it with winetricks hello.verb.

You should see Notepad open, and "Hello, autohotkey" appear in its document window.

Documentation for the AutoHotkey commands used above:
Run
WinWait
Send

A Real Example: Age of Empires Demo

Part One: the basic verb

Here's a simple verb that doesn't (yet) support winetricks' -q option for silent installation. Create a file called aoe_example.verb containing:

w_metadata aoe_example games \
    title="Age of Empires Demo" \
    publisher="Microsoft" \
    year="1997" \
    media="download" \
    file1="MSAoE.exe" \
    installed_exe1="$W_PROGRAMS_X86_WIN/Microsoft Games/Age of Empires Trial/empires.exe"

load_aoe_example()
{
    w_download http://download.microsoft.com/download/aoe/Trial/1.0/WIN98/EN-US/MSAoE.exe
    cd "$W_CACHE/$W_PACKAGE"
    w_try $WINE MSAoE.exe
}

It's a good verb. You can use it with winetricks aoe_example.verb, and it will run the installer faithfully.

Part Two: supporting unattended mode

If you want your verb to obey winetricks -q (unattended install) option, though, you'll have to do a bit more.

The goal of unattended mode is to complete the installation without asking the user any questions. It's good to show progress bars during long operations even in unattended mode, so don't suppress those if you can help it.

First, check to see if the installer supports a command-line option for unattended or silent install (e.g. /S). If it does, use that instead of autohotkey; see OpenTTD for an example.

If you've looked and looked, and there's no way to script an unattended install from the command-line, you can use autohotkey to script the installer's GUI.

The basic motif of scripting an install with Autohotkey is to wait for a window with a particular name (and maybe particular body text) to show up, answer its question (if any) by clicking a button, and then click the 'next' button.

For example, to get past the welcome dialog of an installer, you might try:

WinWait, Microsoft Age of Empires ControlClick, Button1 ; Next

To figure out what commands are needed, start by changing your function so Autohotkey just runs the installer and then waits forever. Here's an example including standard boilerplate options:

load_aoe_example()
{
    w_download http://download.microsoft.com/download/aoe/Trial/1.0/WIN98/EN-US/MSAoE.exe
    cd "$W_CACHE/$W_PACKAGE"
    w_ahk_do "
        SetWinDelay 1000        ; wait extra second after each WinWait
        SetTitleMatchMode, 2    ; allow matching substrings
        Run MSAoE.exe
        WinWait nosuchwindow    ; We will change this later
    "
}

SetWinDelay is needed because installers often aren't ready for keystrokes or mouse clicks right after a window appears.

SetTitleMatchMode, 2 means future WinWait statements should match windows given only a fragment of their full title.

Run that as before with winetricks aoe_example.verb. Note that an 'H' icon will show up in your system tray; this is Autohotkey's status icon. Right-click on that 'H' icon and select "Window Spy", then click on the installer window's title bar. This will bring up a little window that tells you interesting stuff about the active window and whatever the mouse is pointing at in that window.

Go ahead and install as normal, but before you answer the installer's questions, fire up your editor, put the cursor in between the run and WinWait lines in your function, and start writing the Autohotkey commands for what you do as you do it. A few examples:

  • Do you have to press the Enter key? Then write send {Enter}.
  • Do you have to click a button? Then use the Spy utility you started above to find out the button's name, and write ControlClick, buttonName. Alternately, figure out what key to send, and use send to send it.
  • After you click 'Next', be sure to wait for the change to take effect, e.g. by writing WinWait, windowname, unique text in window. Otherwise your next click might be sent before the installer is ready!
  • Be sure to wait for the last window to close by writing WinWaitClose.

You should end up with something like this:

load_aoe_demo()
{
  w_download http://download.microsoft.com/download/aoe/Trial/1.0/WIN98/EN-US/MSAoE.exe
  cd "$W_CACHE/$W_PACKAGE"
  w_ahk_do "
      SetWinDelay 1000
      SetTitleMatchMode, 2
      Run, MSAoE.exe
      WinWait, Microsoft Age of Empires
      ControlClick, Button1
      WinWait, End User License
      ControlClick, Button1
      WinWait, Microsoft Age of Empires, Setup will install
      ControlClick Button2
      WinWait, Microsoft Age of Empires, Setup has successfully
      ControlClick Button1
      WinWaitClose
  "
}

But this will always install automatically, regardless of whether the user invokes winetricks with -q. Let's make sure it only runs the Autohotkey script when the user wants an unattended install. Here's how:

load_aoe_demo()
{
    w_download http://download.microsoft.com/download/aoe/Trial/1.0/WIN98/EN-US/MSAoE.exe
    cd "$W_CACHE/$W_PACKAGE"
    w_ahk_do "
        SetWinDelay 1000
        SetTitleMatchMode, 2
        Run, MSAoE.exe
        WinWait, Microsoft Age of Empires
        if ( w_opt_unattended > 0 ) {
            ControlClick, Button1
            WinWait, End User License
            ControlClick, Button1
            WinWait, Microsoft Age of Empires, Setup will install
            ControlClick Button2
            WinWait, Microsoft Age of Empires, Setup has successfully
            ControlClick Button1
        }
        WinWaitClose
    "
}

Finally, test your verb again with sh winetricks -q aoe_example (shouldn't require any user intervention), and and sh winetricks aoe_example (should require user to answer all questions).

Troubleshooting

Help! The installer is ignoring something I'm sending it!

The most common cause of this is the previous WinWait being too general, and returning before the previous input takes effect. Each WinWait should wait for some unique text; since titles usually don't change, that means you need to do WinWait, windowtitle, unique text from body of new screen.

Advanced Techniques

Optional Dialogs

Some installers have dialogs that only show up under certain conditions. The general technique for handling these is to have a loop which waits for either of the optional dialog(s), and handles them. The loop continues until it sees the dialog after the optional ones, at which point it exits and flow returns to normal. For examples, here's how the dotnet11 verb does it:

Loop
{
    Sleep 1000
    If WinExist, Fatal error, Failed to delay load library
    {
        WinClose, Fatal error, Failed to delay load library
        Continue
    }
    Process, exist, dotnetfx.exe
    dotnet_pid = %ErrorLevel%  ; Save the value immediately since ErrorLevel is often changed.
    If dotnet_pid = 0
    {
        Break
    }
}

Documentation for the AutoHotkey commands/control flow statements/expressions used above:
Loop
Block
Sleep
IfExpression
WinExist
WinClose
Continue
Process
ErrorLevel
Break

To find more examples, search for Loop in the winetricks script.