October 27, 2008

Top Tips for Reliable Macros

Filed under: Automation,Scripting — Marcus Tettmar @ 11:05 am

Over the years I’ve helped many people improve their automation scripts. The most common two causes of scripts failing to work correctly and consistently are timing and focus issues. Here I’ve tried to summarise the main things you should do to make your scripts more reliable.

Try to avoid mouse clicks.

Mouse clicks require screen positions and while there are ways to specify window relative positions, or find the position of objects, if you can use the keyboard instead, it’s a lot less hassle. And most of the time you CAN use the keyboard instead of the mouse. You can TAB from object to object, field to field. You can press ALT to see the shortcut keys and select menu items and click buttons. You can toggle radio boxes with the SPACE bar. You can CTRL-TAB through tab pages. You can position the cursor, highlight text and copy to the clipboard, all with the keyboard. You can often select an item in a list box or combo box simply by typing the item you want to select.

For more info on keyboard shortcuts see:
http://www.mjtnet.com/blog/2006/01/16/keyboard-shortcuts/

Don’t automate mouse clicks or keystrokes just to start an application!

I’ve seen people write code to locate a desktop icon, then double click it, and even double click on a Windows Explorer icon, click through a series of folders and finally double click on an EXE. While this may work just fine, it’s rather long winded and seems a bit pointless when you can run an app with just one line of code. A desktop shortcut is just a shortcut to another file, usually an executable, sometimes with command line parameters. Right click on the shortcut and select Properties to see the target. Copy that and paste it into a Run command. E.g.:

Run>"C:\Program Files\Mozilla Firefox\firefox.exe"

One line of code is much easier to read, easier to maintain later and easier to debug. It’s also faster.

The other day I saw a Citrix app which the user would normally start by double clicking on the desktop icon which would present an Explorer style window appear. The user would then select one of the apps from the list in the Explorer Window. Rather than use image recognition to find the icons and send a bunch of mouse clicks we found that if you right click on one of the apps you could create a desktop shortcut. We did that and examined the shortcut to see what the actual command line was to run that app. We probably could have read the docs instead but we used the desktop shortcut to tell us and simply copied the target into Macro Scheduler, before deleting the desktop shortcut. We now had one line in Macro Scheduler to run the app directly.

How to automate a Save As or File Open dialog.

Need to manipulate a Save As or File Open dialog box? It seems many people think you HAVE to use the mouse to click through a series of folders to find the location of the file. WRONG! Ignore the folder browse area of the file dialog. All you have to do is type a full path into the file name box and hit Enter. Doesn’t matter what the current folder is that the dialog is showing you. Just type the path of the file you want:

WaitWindowOpen>Save As
Send>g:\some folder\some sub folder\some file.txt
Press Enter

That’s it! No mouse clicks necessary!

Make the script wait for events to complete.

Remember – scripts run faster than humans can type! Unless you build delays into the script it’ll just issue each step milliseconds after the last one. But apps are designed for humans who type more slowly and subliminally wait for events to complete – we don’t start typing into a window until we can see the window on the screen. We know that when we select File/Save As we need to wait for the Save As box to appear before we can enter the filename. So we need to make scripts do the same thing.

The most important command you should use in EVERY script is WaitWindowOpen. Whenever we issue a command which will make a new window open, use WaitWindowOpen to make the script wait for that window to appear before it continues:

//select File/Print
Press ALT
Send>fp
Release ALT

//wait for the print dialog
WaitWindowOpen>Print

//Press enter key
Press Enter

Without that WaitWindowOpen command the Press Enter will most likely be sent long before (in computer time) the print dialog has appeared. So the document won’t print.

Equally, use WaitWindowClosed when you do something that causes the window to disappear.

Sometimes it’s not a window you need to wait for, you might want to use WaitScreenImage to wait for any visual cue, or WaitScreenText, or WaitCursorChanged. Or you might need to wait for a file to appear, so you might create a little loop with IfFileExists in it …. it could be anything. Determine what events the script needs to be aware of and make it wait for them.

Be Specific.

The window functions can take an asterisk to look for a substring match for situations where the window title is not static, or not completely known. E.g.:

WaitWindowOpen>Internet Explorer*

But try to be as specific as you can. There are many hidden windows on your system so if your text matches one of those first it’ll stop at that one, not the one you want. Make the text more specific if possible, and/or use WF_TYPE to make Macro Scheduler look for only visible windows.

As an example, the following could return too quickly:

WaitWindowOpen>Microsoft Excel*

Why? Because “Microsoft Excel” is the window of the container application. But just after Excel starts you’ll see a window title something like “Microsoft Excel – Book1”. When the app starts, for a split second, faster perhaps than you can detect with the human eye, the window title might first be “Microsoft Excel” and THEN “Microsoft Excel – Book1”. So the above WaitWindowOpen line might return too quickly. You might not know what the first workbook will be, but you could do:

WaitWindowOpen>Microsoft Excel -*

Be as specific as you can for the final window title that you expect to see before you start automating it.

Slow scripts down! You can always speed them up later.

So you’ve followed the above advice and you’ve made the script wait for events to complete. You may still need some small delays. Let’s say you’re sending a bunch of keystrokes to a form, you’re tabbing through a window sending data to each field:

Press Tab
Send>Firstname
Press Tab
Send>Surname
Press Tab
Send>address
...

It’s not working? It works sometimes but not others? Here’s what is happening. The app is being overwhelmed by those keystrokes. It might do some “OnChange” style processing – as text is entered into a field it could be doing some processing internally. As mentioned before the script runs way faster than a human can type. The text is being sent to the app too fast. In real life there’d be a short delay between fields. So slow the script down and give the app a bit of a chance to catch up:

Press Tab
Wait>0.5
Send>Firstname
Press Tab
Wait>0.5
Send>Surname
Press Tab
Wait>0.5
Send>address
...

Half a second is probably all you need. It may not even need to be that much. But I always recommend that while you are developing a script and testing it you should err on the side of caution. Use plenty of waits. Get the script working. You can always reduce the waits later once you know it’s working. And if your script is going to be run unattended, say at night, then does it matter if it takes 5 seconds longer? Who’s watching it?

You can also use SK_DELAY to slow down the key send rate where you are sending characters together. SK_DELAY takes milliseconds:

Let>SK_DELAY=20
Send>Hello World

Sometimes you need to press tab a number of times to get to a field so you might do:

Press Tab * 6

But this sends the tabs in quick succession, and as I’ve said, the app may be requiring a bit of time between fields. So you might need to do the following instead:

Let>k=1
Repeat>k
  Press Tab
  Wait>0.2
  Let>k=k+1
Until>k=6

Make sure the correct window is focused!

When you press a key on the keyboard that keystroke will land in whatever the active window is. That’s the way your computer works. If Notepad is active and you type “Hello”, you’ll see “Hello” inside Notepad. If some other app is active … I don’t know what “Hello” would do in ficticious app number 2! Macro Scheduler controls your computer, so the same thing happens. That’s the way your computer works. So when you make Macro Scheduler send some text you’re going to want to make sure the correct window is focused first:

SetFocus>Notepad*
Send>Hello World

Build scripts in chunks.

When I develop a new macro I do it a few lines at a time. The first thing I might want the macro to do is open an application and wait for it to be ready. So I’ll do that bit manually and work out what paths, window titles and keystrokes I need to make that happen. I’ll write those lines of code. Then I’ll run the script and see if it gets me to where I want to go. Once those lines are doing the right thing I can work on the next phase. I’ll do a few more steps, add those to the script, then close the app down and run the script again.

Don’t try to build everything in one go, break the routine down into chunks and work build it up slowly.

Use the debugger!

The debugger is your friend. The debugger lets you step through the script line by line and watch the script working. You can see the value of variables as they are created and modified. So you can see just what each line is doing and find problems instantly. For a tutorial on using the debugger see:
http://www.mjtnet.com/blog/2006/05/17/use-the-debugger/

There’s also the variable explorer (Tools/Variable Explorer). Quite handy for finding variable related problems without having to run the script.

___
More resources:
http://www.mjtnet.com/blog/2006/01/17/how-to-start-writing-an-automation-script/
http://www.mjtnet.com/blog/2008/03/21/round-up-of-learning-resources/