Macro Scheduler 8.0.1 Update
We released the first 8.x maintenance release today. This fixes a few small bugs. You will find full details here:
http://www.mjtnet.com/usergroup/viewtopic.php?t=2568
Macro Recording and Automating Windows with Macro Scheduler – Tips & News
We released the first 8.x maintenance release today. This fixes a few small bugs. You will find full details here:
http://www.mjtnet.com/usergroup/viewtopic.php?t=2568
Nothing much to do with Macro Scheduler this except that it finds me sitting in an office in Geneva today. 100 meters beneath me, under the ground, is a circular tunnel 35m wide, 55m tall and 27 km long. It goes right underneath houses and offices in the city of Geneva and surrounding areas. Sandwiched between Lake Geneva and the mountains is the largest particle accelerator in the world, run by CERN, where Tim Berners Lee invented the World Wide Web. Next year they will accelerate protons around this tunnel and smash them together to create conditions similar to those that existed just after the Big Bang. They’re looking to prove the existence of a particle called the Higgs Boson, which is believed to provide matter its mass.
If you think you’ve created long scripts in Macro Scheduler you should see the software they’re creating at CERN to configure and measure the results from the particle accelerator. A friend of mine is debugging this C++ code right now and he says it takes 30-40 minutes to compile! That’s a lot of code. Think about that next time you’re debugging a script! 🙂
I was interviewed yesterday for Channel9’s New MicroISV Show. It will be aired in 5-6 weeks.
MicroISV is a new term to describe small software companies like MJT Net Ltd. ISV stands for Independent Software Vendor. The show is aimed at developers and small software companies and will give an insight into the benefits and challenges faced by small companies developing for the Windows platform.
This is something that comes up often, so I thought I’d mention it here. Every so often we get asked how to put a date into a filename. Say you want to rename or save a file with the date in the filename formatted to reportsYYYYMMDD.txt e.g.:
reports20061801.txt
Well the simplest way to do this is to use the basic date functions Year, Month and Day. These each return the current year, month and day numbers. We also need to embed the variables into the filename. We can create the filename like this:
Year>the_year
Month>the_month
Day>the_day
Let>filename=reports%the_year%%the_month%%the_day%.txt
See how we embed the variables by putting % symbols around them. The % symbols tell Macro Scheduler to find the variable within them and to use the value assigned to that variable. So on 18th January 2006 we get:
the_year=2006
the_month=01
the_day=18
And filename becomes:
filename=reports20060118.txt
Say we wanted to rename the reports.txt file to a reports20060118.txt. Let’s also say that the file is in the c:\data folder. We would do this:
MoveFile>c:\data\reports.txt,c:\data\%filename%
Or we could have said:
Let>filename=c:\data\reports%the_year%%the_month%%the_day%.txt
MoveFile>c:\data\reports.txt,filename
You might not be renaming a file, but sending the filename to an application. Most applications use the standard ‘Save As’ dialog. Once this has opened you can send the full path and filename directly to that box and hit the Enter key to perform the save. So after sending the keystrokes required to open the Save As dialog (often File/Save As) we’d do:
WaitWindowOpen>Save As
Send>c:\data\reports%the_year%%the_month%%the_day%.txt
Press Enter
The Year command returns the year in full 4 digit form. Suppose you only wanted the last two digits (06 in this case). Well we can use the MidStr command to extract the last two digits:
Year>the_year
MidStr>the_year,3,2,the_year
This says: Starting from the third character in the_year take two characters and return the result in the_year. In this case because the_year has previously been declared as a variable (by the Year command) it is seen as a variable. The result of the MidStr command is also the_year so it over writes the_year with the new value. We could have made a new variables:
MidStr>the_year,3,2,YY
Here we end up with a new variable, YY, which contains 06.
To understand this better, paste the following code into the Script Editor. Then open the Watch List (Debug/Show Watch List). Now place the cursor on the first line and hit F8. Each time you hit F8 the script will advance to the next line. You will see the new variables being created in the watch list to the right. And you will see their values changing as you advance through the script.
Year>the_year
Month>the_month
Day>the_day
Let>filename=reports%the_year%%the_month%%the_day%.txtMidStr>the_year,3,2,YY
Let>newfile=outcome%YY%.dat
You might want to use a previous or future date. For more advanced date manipulation I recommend using the VBScript date functions.
Let’s say we want to use yesterday’s date in the filename, instead of today’s. To determine past or future dates based on today’s date use the VBScript function DateAdd. DateAdd takes an interval type, a number and a date. To add one day to the current date do:
DateAdd(“d”,1,Date)
To subtract one day from the current date we use:
DateAdd(“d”,-1,Date)
So to create a dated filename based on yesterday’s date we can do this:
VBSTART
VBEND
VBEval>DateAdd(“d”,-1,Date),yesterday
VBEval>Year(“%yesterday%”),yyyy
VBEval>Month(“%yesterday%”),mm
VBEval>Day(“%yesterday%”),dd
Let>filename=reports%yyyy%%mm%%dd%.txt
Here we use VBScript’s version of the Year, Month and Day functions which operate on a given date. First we subtract one from the current date to get yesterday’s date and we use that in the Year, Month and Day functions to extract the relevant parts and then we can construct our filename.
See the VBScript Documentation for more info on using VBScript functions.
I’ve added some scripts to the Scripts & Tips forum:
A script to Monitor Startup Programs. Useful for detecting rogue apps like spyware etc
Is Drive Ready. A function to determine whether the drive is ready or not.
Automatically Create a System Restore Point. Useful at the start of a script that installs software or makes critical changes to the system.
Backup the Registry. Well worth scheduling to run regularly, or run manually before installing software.
Create a New Outlook Contact. Demonstrates how you can automatically add new contacts to Outlook. Could be used for importing data from another source, or could be integrated with a script that automatically parses order notification emails etc.
Cosmo asked in his comment on How to Start Writing an Automation Script how he could set the position of a desktop shortcut. This is an interesting question that hasn’t come up before and it took me a while to realise how it could be done. You may wonder why you’d even want to do it. Well if you’re automating software installations for your end-user or customer machines you may want to make sure the desktop icons are always in the same place. You may want to put a link to your support site in the top right of the screen, for example, so that your customer can always find it.
The solution wasn’t immediately obvious. You can move icons around with the mouse, but trying to automate that would be a bit of a nightmare and you can’t drag icons with the keyboard. Even then how do you know what position it’s at? So how do you do it? Well I realised that the desktop icons are actually elements of a hidden ListView control belonging to the desktop window. So we need to manipulate this ListView control. Macro Scheduler 8.0 has a function called GetListItem which will return the index of a list item by its caption. So immediately we can find the index of a desktop icon with just this line:
GetListItem>Program Manager,SysListView32,0,RealPlayer,0,0,0,Result,Handle
This line returns the index of the “RealPlayer” shortcut. Note that the main window title is Program Manager and the object class name is SysListView32. You can see this in the View System Windows tool (under the Tools menu in Macro Scheduler). Conveniently, GetListItem also returns the Handle of the listview object. We need this for the next bit.
We need to go a level deeper to figure out how to position the item. We get down and dirty with the Win32 API. The Win32API is a heap of functions deep within Windows. There’s a message called LVI_SETITEMPOSITION which is sent to a ListView control when it needs to be positioned. Messages always have two parameters, known as wParam and lParam. In this case wParam is the index of the ListView item, which we got from the GetListItem command, and lParam is a POINT structure. A POINT structure specifies an X and Y coordinate. So we can send the X and Y coordinate of where we want the shortcut to end up, to the ListView control by way of the LVI_SETITEMPOSITION command.
To send the LVI_SETITEMPOSITION message we need to use the Win32 API function SendMessage. First we need to convert the X and Y coordinates into an integer value. This involves a bit of wizardry using a couple of VBScript functions which are in the full script. So this allows us to do this:
Let>xpos=100 Let>ypos=500 VBEval>MAKELPARAM(%xpos%,%ypos%),lparam //Reposition Let>LVI_SETITEMPOSITION=4111 LibFunc>user32,SendMessageA,r,Handle,LVI_SETITEMPOSITION,Result,lparam
So using the VBscript function MAKELPARAM (it’s in the full script) we convert the target x,y coordinates to an lParam value which we can send to the ListView with the message. We declare the LVI_SETITEMPOSITION message (message 4111 in Windows – LVI_SETITEMPOSITION is just constant name) and then use the LibFunc command to run the SendMessageA Win32 API function which sends the message. We send it to the ListView control using the handle returned by GetListItem and we send the index, also returned by GetListItem, and our lparam which contains the new x,y coordinates.
So here’s the full script:
//Functions needed for working with windows messages VBSTART Function LoWord(wInt) LoWord = wInt AND &HFFFF& End Function Function HiWord(wInt) HiWord = wInt &H10000 AND &HFFFF& End Function Function MAKELPARAM(wLow, wHigh) MAKELPARAM = LoWord(wLow) Or (&H10000 * LoWord(wHigh)) End Function VBEND //Refresh icon view SetFocus>Program Manager Press F5 //Get index of RealPlayer shortcut on desktop GetListItem>Program Manager,SysListView32,0,RealPlayer,0,0,0,Result,Handle //Set position of icon. //If Align to Grid is on this will slot icon in where it fits best. //If Align to Grid is off it will place icon at this absolute position. Let>xpos=100 Let>ypos=500 VBEval>MAKELPARAM(%xpos%,%ypos%),lparam //Reposition Let>LVI_SETITEMPOSITION=4111 LibFunc>user32,SendMessageA,r,Handle,LVI_SETITEMPOSITION,Result,lparam
Apologies for the way the script is formatted. HTML doesn’t lend itself well to showing script code the way it was intended. But you can download the full script file at the end of the article.
Note the VBScript at the top which creates an LParam value out of two integers, in this case the x,y values. Besides declaring the VBScript functions, the very first thing the script does is focus the desktop and hit F5 to force a refresh. This ensures that the icons are in their correct places so that the GetListItem command returns the correct value. Note that the desktop window title is “Program Manager”. So we can use that to focus the desktop window.
If “Align to Grid” is enabled the icon will snap to the position in the list nearest the x,y position we provide. If Align to Grid is off it will use that absolute x,y position. I should also say that the x,y coordinates are the top left position of the shortcut. I’ve included some code to toggle Align to Grid in the downloadable script file.
Windows message names are constants. Macro Scheduler doesn’t have these variables pre-defined so to use Windows messages you’ll need to find out their integer values. If you have a development environment such as C++ you’ll find them declared in winuser.h. If you don’t, I’ve uploaded a text file with a large number here.
So there you are. I hope this is useful.
Download the Script File
AllApi.Net API List Windows 32 API Functions – this site is aimed at users of Visual Basic but I find it a handy reference of API functions and the examples can easily be translated to LibFunc calls.
This is the 10th commandment of system administration according to Brian Warshawsky’s article “Ten Commandments of system administration” over at NewsForge. I couldn’t agree more. You’ll find the article with links to the other nine commandments here. These articles are for Linux administrators so much of the content and the example scripts won’t be much use to Windows admins. But the message is perfectly valid. Linux admins have always had the power of scripting at their disposal and the benefit of an operating system which is powered by command line interfaces. Windows, though, is primarily GUI based, designed more for desktop users. That’s why you need Macro Scheduler to automate Windows applications and obey the 10th commandment!
Tim Jones emailed me to point out an error in my keyboard shortcut article. Tim is a proper keyboard junky. Not even sure he has a mouse!
___________________
> SHIFT-RIGHT on its own just moves the cursor to the end of the
> word and then to the next word and so on. Reverse it with
> SHIFT-LEFT.
SHIFT, marks next/prev letter, CTRL moves a word. I know you
know that, but the blog entry seems to kinda have it the other
way around.
___________________
Thanks Tim!
These are my tips for getting started with writing an automation script. While I’m, writing this with our Windows Automation tool, Macro Scheduler, in mind, these tips will be appropriate whichever automation tool you are using.
The most important thing before attempting to write a routine to automate a software process is to be familiar with the process itself. Run through the full process several times and find the simplest path. If you’re using the mouse excessively try to find keyboard alternatives.
Write down every step you take. Make a note of the keys you press, note down the title of each new window and how long each step takes. Try to determine what indicates the completion of each step. Make a note of it. You’ll end up with a list of keystrokes and window titles. This is the basis of your script. You’re now most of the way there. This list will translate well into a Macro Scheduler script.
I find it is best to break the script down into manageable chunks. Don’t try to write the whole thing at once. Start by just scripting the first few steps. E.g. write the code that opens the app, waits for it to be active and sends the first keystroke. Run it and make sure the process ends up where you expected. Tweak it if necessary. Now add the next couple of steps. Run it again. And so on. Building the script up in this way will iron out issues as you go rather than leaving you desperately trying to hunt down the cause of an error amongst one long script. You can also use the debugger to step through the script line by line.
Use SetFocus! When sending keystrokes Macro Scheduler just simulates what you do when you press keys on the keyboard. When you do that the keystrokes land on the active window. So you need to make sure the window you want the keystrokes to land in is the active window. Use SetFocus to do this. Get into the habit of using SetFocus even after running a program with the Run Program command. When you start a program it nearly always becomes the active window, but other applications can steal the focus. So it’s a good habit to get into to use SetFocus before sending a set of keystrokes.
Try not to use absolute wait times to wait for events to complete. The process may take longer on another occasion and fail. Also remember that when you do something manually you will subconsciously wait for the outcome of each action. A script isn’t quite so clever and unless you tell it to wait for certain actions to complete it will blindly move on to the next step. Always use WaitWindowOpen after running a program or sending an event that causes a new window to appear. This will ensure the script waits for that window to appear before continuing. Use WaitWindowClosed after issuing a command that causes the window to close. There are ways to wait for all sorts of other events to take place too, some more advanced than others. You can wait for pixel colors to change, mouse cursors and windows to change, detect object captions and wait for files, and portions of the screen to change amongst other things. When starting an application with Run Program use RP_WAIT=2 to tell it to wait until the application is ready for input before continuing.
Sometimes you do need a small Wait between events. Sometimes you need to slow down the key send rate. Scripts run faster than a human can type and not all applications can cope with that many keystrokes in such a short space of time. So the odd Wait>0.5 here and there can help. You can also slow down the key send rate with SK_DELAY.
When issuing shortcut keys such as ALT-F for the File menu use lowercase for the underscored character. E.g.:
Press ALT
Send>f
Release ALT
I have seen some applications fail to recognise shortcuts when Macro Scheduler sends the character in upper case. This is probably because it treats an upper case character send as having the Shift key pressed at the same time. So I always recommend issuing characters used in shortcut keystrokes in lower case.
It is best to avoid mouse events as much as possible, but if you do find you need to use the mouse for a particular action – maybe there’s no keyboard alternative – try to work out the relative mouse coordinates. Use the cursor monitor, set it to relative, and find the position relative to the window. Then use MouseMoveRel. This ensures that the mouse will always click on a position relative to the window rather than on an absolute screen position so that if the window opens in a different position next time around the macro will still work.
Clearly this article is all about automating via user simulation – by sending keyboard events to applications. Often there are better ways to automate an application such as via VBScript/ActiveX or DDE, or by reading data from files and databases. Before sitting down to automate an application by simulating user input stop to consider whether there are alternatives. For example, I’ve seen people write scripts to automate getting data from Excel by sending keystrokes to Excel to copy and paste. There’s no need to go to this trouble when Excel provides a DDE interface and can also be scripted with VBScript. See the sample script that comes with Macro Scheduler for an example of retrieving data from Excel directly. I’ve even seen macros which get data from a text file by driving Notepad with keystrokes. This is unnecessary and cumbersome when all you need to do is read data directly from the text file with the ReadLn command. If you need to get data from a database, you don’t need to send keystrokes to the query tool – you can read data directly into the script using VBScript/ADO/ODBC. Equally you can automate Internet Explorer elegantly via VBScript/ActiveX or with WebRecorder. There are usually at least two ways to automate something. Before writing your script stop and question whether there’s a better way.
Before writing your first script read Scripting Windows for Beginners in the Macro Scheduler help file/manual.
And browse the examples at the Scripts & Tips forum.
And if you don’t already have Macro Scheduler, you can download a trial version here.
As mentioned in my last post, the easiest, most reliable way to automate an application is via keyboard shortcuts. But I’ve found that many people are so used to using the mouse that they don’t even realise you can use the keyboard to move around Windows applications.
Some things I take for granted are completely new to others. For example, moving from one field to the next is accomplished with the Tab key. After entering some information into an edit box just hit Tab to move to the next edit box. This is surely faster than moving your hand away from the keyboard, moving the mouse, clicking in the next edit box, moving back to the keyboard … and so on. Tab is all you need. Something a lot of people don’t realise is that this even works on web pages. Tab will move from form field to form field, but also from link to link. Try it now while you are reading this post. Press Tab a few times. You’ll notice a faint hashed box around the focused link. Press Tab again and the box moves to the next link. And so on. When you’re on a clickable object, or link, the Enter key will select or “click” it. Tab to a link and then press Enter and you will go to that page. In a Windows application you can tab to any standard object. You can tab through edit boxes as well as check boxes, menu items and buttons, and pretty much anything else. If you tab to a button pressing Enter will “click” it.
Checkboxes can be toggled from checked/unchecked and back again by pressing the space bar. Tab to a checkbox and hit the space bar. Its checked status will change. To select an item in a list box you can use the up and down arrows. Same goes for treeviews and combo boxes. But what you might not realise is that many list boxes and combo boxes have a kind of “drill down” feature. Type the first few characters of an entry and the selected item will change to the first item that starts with those characters. Type more characters in one go to narrow down the selection. This can be really useful when automating applications where you want to select an item in a list box or drop down – you can just send the text of the item you want to select. E.g. the following script automates the Regional Settings control panel applet to automatically change the default input language to Chinese (Hong Kong):
//Set to Chinese (Hong Kong …)
ExecuteFile>intl.cpl
WaitWindowOpen>Regional and Language Options
SetFocus>Regional and Language Options
Let>SK_DELAY=10
Send>Chinese (Hong Kong
Press Enter
This works by sending the first few characters unique to the entry we want to select. Unfortunately not all list boxes work like this. That’s why Macro Scheduler has advanced commands like GetListItem to determine the index of an item given it’s text caption.
Most of us know that we can select menu items using the Alt key. When you press the Alt key you should see certain characters in the menu items and on other objects become underscored. These are the shortcut keys. So to select the file menu in most applications you would press ALT-F. If an application has been designed properly other fields and buttons will have underlined characters also. Even labels associated with objects should have shortcut keys so that when you press ALT and that key you move focus immediately to that object. All this makes automation so much easier.
Other key combinations that are useful include CTRL-TAB to move from page to page or tab to tab in a tabbed window, such as Firefox. Try CTRL-TAB in firefox with several tabs open. You’ll move from one to the next.
Did you know you can select the next word in an editor by pressing CTRL-SHIFT-RIGHT? SHIFT-RIGHT on its own just moves the cursor to the end of the word and then to the next word and so on. Reverse it with SHIFT-LEFT. SHIFT-END will highlight to the end of the line. SHIFT-CTRL-END to go to the end of the document.
This is just the beginning. There are all sorts of keyboard shortcuts that make working in Windows so much faster and make automation so much easier and more reliable. I don’t know them all. But it’s worth getting to know them if you want to get the best out of Windows Automation.
Here are links to some pages that list useful keyboard shortcuts:
List of the keyboard shortcuts that are available in Windows XP
http://support.microsoft.com/default.aspx?scid=kb;en-us;301583
Shortcut keys in Windows 95,98,Me:
http://support.microsoft.com/default.aspx?scid=kb;en-us;q126449
Getting the most out of your Windows Keyboard
http://www.internet4classrooms.com/winkeyboard.htm