February 19, 2009

View System Windows – Windows and Objects

Filed under: Automation,Scripting — Marcus Tettmar @ 10:12 am

syswindows1

Macro Scheduler has a “View System Windows” tool which you can get to from the Tools menu. This displays a tree of all windows, their child windows and objects open in the system. For each entity you see its handle, class name and title/caption text.

The help file talks about windows and child windows. For windows you can read “objects”. In Windows a “windows control” is not necessarily a window in the sense that you are familiar with but could be a button, a status bar or edit box, or pretty much anything else you see inside a window or on the screen.

So the View System Windows tool shows the hierarchy of “windows” that currently exist on your system. You can expand one to see it’s child windows/objects and so on.

In the picture above I have highlighted the entry for what is commonly referred to as the “System Tray”. Its official title is “Notification Area”. If we right click this entry and select “Identify” a border will be drawn around the system tray icon area. This “Identify” option is a useful way to help locate a window or object and be sure that the entry you see in the list is the object you’re looking for.

A word about handles. These are the numbers that appear in the list. In the above snapshot the notification area has a handle of 131104. It will be different on your system. It will be different after you reboot. Handles are simply identification numbers that are allocated at run time when a window is first created by the system. So don’t go thinking you can grab that number and put it in your script and use it to identify your window. Next time you reboot the script won’t work.

After the handle you will see the “class name”. For the system tray this is “ToolbarWindow32”. This is static but not unique. It is just the type of object – the object class – that is used. We can use this to find an object, but it is not unique.

Macro Scheduler has a function called GetControlText which takes a window title (or handle), a class name and an index. This will return the text associated with an object of the specified class and index on the specified window. If there were two objects of that class an index of 1 would return the first and an index of 2 the second, in the order of creation (some trial and error is usually required). GetControlText is great for objects on regular windows and makes life simple. But for objects deeper in the hierarchy we might have to be a bit more clever.

Some objects also have captions and these are displayed after the class name. Here we see it is “Notification Area”. E.g. windows have captions and we’re used to seeing these in the title bar. Button objects have captions that appear on the button. Some other objects also have captions although they may not be visible (equally what you see on an object is not necessarily the caption that appears in the tree). If we have a caption we can also use this to help us find an object. If we don’t we would have to rely on indexes as in GetControlText – iterate through each instance of an object.

As an example, the following code gets us the handle of the system tray:

LibFunc>User32,FindWindowA,h1,Shell_TrayWnd,
LibFunc>User32,FindWindowExA,h2,h1,0,TrayNotifyWnd,
LibFunc>User32,FindWindowExA,h3,h2,0,SysPager,
LibFunc>User32,FindWindowExA,h4,h3,0,ToolbarWindow32,Notification Area

This code mirrors the hierarchy we see in the View System Windows tree above.

FindWindow takes a class name and caption to return a top level window. So this gives us the handle of the parent Shell_TrayWnd object. FindWindowEx finds an object on the given parent window. So the second line above looks for an object with class “TrayNotifyWnd” on the Shell_TrayWnd window (h1 returned by FindWindow) with no caption. It looks for the first match since the “child after” handle is set to zero. The next two lines continue to walk the tree. What we end up with in h4 is the handle of the system tray.

I use this code here in Activating System Tray Icons.

If all this sounds rather complicated, don’t worry. Most of us will never use code like this. I just wanted to explain more about what View System Windows shows us. There are much easier ways of identifying objects and scraping text from the screen that work in a much more human like way. Objects can be identified visually with Image Recognition and the Text Capture functions let us screen scrape by just specifying a window title or screen area. And most of the time we can automate a window by sending keystrokes to it and never have to care what an object is called.

February 5, 2009

Generating Random Characters and Strings

Filed under: Scripting — Marcus Tettmar @ 1:10 pm

Macro Scheduler has a Random function which will give you a random number between a specified range. Every now and then I am asked how you can create a random character or random character string.

The solution is to use VBScript’s Chr function which returns the character for the specified Ascii code. So we can use Random to get a random number within the required Ascii range, and then use Chr to get the corresponding character. There are a few examples in the forums, such as this one.

I was recently asked how to generate a random character from A to Z. Here’s my response:

Look at the ASCII table: http://www.asciitable.com/

Let’s look at upper case only first:

A is ascii code decimal 65
Z is ascii code decimal 90

So if we want a random character from A-Z inclusive we want to get a random number in that range (65 to 90).

Clearly it’s a range of 26. Random gives a value from 0 to range-1. So:

Random>26,random_number

So to get that into our range we clearly need to add it to 65, because the ascii range begins at 65 for “A”:

Let>ascii_code=65+random_number

Now we have an ascii code in the required range. So now use VBScript’s Chr function which returns the corresponding character for the ascii code:

VBEval>chr(%ascii_code%),random_char

So, in long form:

Random>26,random_number
Let>ascii_code=65+random_number
VBEval>chr(%ascii_code%),random_char

Or, shorter version:

Random>26,random_number
VBEval>chr(%random_number%+65),random_char

That will give you a random character between A and Z inclusive.

If you want lower case then the range is 97 – 122. There are some characters in between. So if you want to mix lower case and upper case, you could either look at the entire range, discarding any results that appear between the two, or throw a coin (Random>2) to determine whether to do the upper case range or the lower case range, or, I think simpler, randomly convert to lower case, e.g.:

Random>26,random_number
VBEval>chr(%random_number%+65),random_char

Random>2,UpperOrLower
If>UpperOrLower=0
  VBEval>LCase("%random_char%"),random_char
Endif

MessageModal>random_char

Remember, to use VBScript you need a VBSTART/VBEND block in your script, even if it is empty. Stick it at the top. So the whole example script becomes:

VBSTART
VBEND

Random>26,random_number
VBEval>chr(%random_number%+65),random_char

Random>2,UpperOrLower
If>UpperOrLower=0
  VBEval>LCase("%random_char%"),random_char
Endif

MessageModal>random_char

January 27, 2009

Modal vs Non-Modal – Windows, Not Jazz

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

Every now and then, in the forums and in emails to us, there seems to be some confusion over what these terms mean.  As a developer I take these terms for granted, but anyone else, depending on their walk of life, might think they refer to some kind of Modal Jazz; something to do with a modal matrix used in linear algebra; or a type of auxiliary verb used to indicate modality.  

I sometimes need to remind myself that not everyone coming to Macro Scheduler and Windows Automation in general are familiar with geeky software terms.  I can’t tell you much about modal jazz, linear algebra or auxiliary verbs, but I can do my best to set to rest any confusion about Modal and Non-Modal windows.

Modal Windows

When a window is modal it remains active and focused until the user has finished with it and dismisses it.  While it is active no other windows of the same application can be activated.  A modal window is therefore normally a child window.  The user needs to interract with it before control can be returned to the parent application. In effect the parent application is locked and nothing proceeds until the modal window is closed.

You’ll find a good definition of Modal Windows on Wikipedia, here.

Non-Modal Windows

So a non-modal window is the opposite. While it is active you can still activate other windows. The user can switch between windows of the same application. The window being active does not prevent the rest of the application from continuing.

Modal Dialogs

In Macro Scheduler you can create custom dialogs. These can be modal or non-modal to the script. When we refer to a modal dialog what we mean is that once the dialog is displayed, the script halts until the user closes the dialog. Script execution is paused until the dialog is dismissed. The script cannot do any processing while the modal dialog is active. A modal dialog is displayed with the Show command, with a return variable specified, in which the “modal result” of the dialog is reported, corresponding to which button was pressed to close the dialog.

Non-Modal Dialogs

A dialog can be made to be non-modal. In which case the script carries on even after it has displayed the dialog. The user can interact with the dialog while the script continues to perform other tasks. The developer would need to write code to retrieve the state of the dialog and the data that has been entered into it. Usually this would need to take place in a loop. A non-modal dialog is displayed with the Show command, with the return variable omitted. Inside the loop we would retrieve the state and data of the dialog with the GetDialogAction command, and set it with ResetDialogAction.

In this post I have concentrated on definitions rather than going into detail on how you can create and control custom dialogs. That is a subject for another post and you will find more information in the help file and online help. Some dialog examples ship with Macro Scheduler and you’ll find more in the forums.

January 23, 2009

Screen Scraping with Macro Scheduler

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

What is Screen Scraping?

Screen Scraping is a term used to describe the process of a computer program or macro extracting data from the display output of another application.  Rather than parsing data from the database or data files belonging to an application, Screen Scraping pulls the data from the screen itself, extracting data that was intended to be displayed to the end-user as opposed to data designed for output to another application or database.  

Screen Scraping is necessary when there is a need to access the information displayed by the application but there is no method provided to access it behind the scenes.  The database or data files may not be accessible, or may be undocumented or proprietary and therefore cannot be parsed easily; the costs associated with interacting with the database may be too high; or the license agreement or warrenty prohibits it.  In the case of legacy systems that are no longer supported there may be no knowledge of the data structures, or the technology used is no longer compatible with current technology.  In these cases we are resorted to extracting the data from the screen – from the windows of the application.

The term Screen Scraping probably originates from the era of computer terminals when you could connect the terminal output of a computer to an input port on another and therefore record the screen data.  

Screen Scraping Methods

There are a number of ways we can retrieve information from the screen using Macro Scheduler, depending on the type of application the data is in.

Screen Scraping Web Applications

Applications like Macro Scheduler’s WebRecorder can access the data and objects inside an Interner Explorer window and can therefore be used to extract the data.  Technically speaking I would not call this screen scraping since WebRecorder is using an API interface provided by Internet Explorer, but the process of extracting information from web sites is commonly refered to as Screen Scraping.  With WebRecorder we can use the ExtractTag wizard to create code that extracts the text from a particular element in the page.   While WebRecorder is the easiest way to do it, it is also possible to automate IE and extract data from web pages by using VBScript. The following forum posts may help:

Automate Internet Explorer with OLE/ActiveX
Automate web forms with IE
HTTP GET and POST using VBscript

Screen Scraping Microsoft Office Applications

Microsoft Office Applications, like Internet Explorer, have a COM interface that allows scripts to manipulate them and access the data held within them.  Again, not really scraping data from the screen itself, as you are getting it directly from a programming interface.    There are a number of examples in the forums and blog archives and also some sample scripts that come with Macro Scheduler which demonstrate how to automate Office applications and retrieve data from them.  

Working with Excel

Screen Scraping Regular Windows Applications

Most other applications don’t offer a scripting interface like MS Office or Internet Explorer.  This is where we really need to work directly with the screen.   There are a number of ways we can do this kind of Screen Scraping with Macro Scheduler.

The Text Capture Functions

Macro Scheduler includes some Text Capture functions which can be used to extract text from a given window, rectangular screen area or screen point.  These functions use low level system hooks which monitor applications calling the various “TextOut” functions that Windows uses to output text to the screen.  By doing so they are able to capture this text.  The Text Capture functions return the text to  a variable which you can then use as needed.  

However, a few applications don’t use the Windows built-in functions to create and output text.  Don’t worry –Most do, but a few use their own techniques.  When you realise that text on the screen is just a sequence of small dots, if the application programmer decided to build his own routine to assemble text from dots rather than calling the Windows functions which already do that for you, you’re not going to be able to capture it.

The text capture functions and their limitations are explained here.  There is an example application, here, created with Macro Scheduler, which you can use to determine whether or not the text you want to capture can be captured using the text capture functions.

http://www.mjtnet.com/blog/2007/12/12/capturing-screen-text/
http://www.mjtnet.com/blog/2008/01/03/screen-scrape-text-capture-example/

Using the Clipboard for Screen Scraping

If the text you want to capture is selectable then you can use the clipboard to retrieve it.  A Macro Scheduler macro can send the keystrokes necessary to highlight and copy the text to the clipboard and then use the GetClipboard function to retrieve that text to a variable.  This is far less elegant than using the Text Capture functions but might be necessary if the application concerned is not utlising any of the Windows text out functions to create the text.

SetFocus>Notepad*
//Select ALL
Press CTRL
Send>a
Release CTRL
//Copy to clipboard
Press CTRL
Send>c
Release CTRL

//Get and display the data
WaitClipboard
GetClipboard>theData
MessageModal>theData

Screen Scraping via Optical Character Recognition

A final resort might be Optical Character Recognition (OCR). If the text is part of an image file neither the Text Capture or Clipboard methods are going to be able to extract it. But, depending on the quality of the image and the fonts used, OCR might. There are a number of OCR engines that can be used by Macro Scheduler, one of which is part of Microsoft Office. The following posts demonstrate how this can be done:

Screen OCR to Retrieve Otherwise Undetectable Text
Screen OCR – Recognising Graphical Text

More on Screen Scraping.

December 18, 2008

The Telnet Functions – Get POP3 Message Count

Filed under: Automation,Scripting — Marcus Tettmar @ 1:41 pm

Recently in a forum post, “Getting New Email Message counts” terencepjf asked:

Does anyone know a way to get a count of New Email messages:

1) Without downloading all the messages
2) Count of messages in Inbox Sub-Folders

RetrievePOP3 works but the files need to be downloaded.”

My answer is to use the Telnet functions. You might not realise that POP3 – the protocol used to download email – is a Telnet protocol. You can Telnet into your POP3 server and see the messages waiting. POP3 usually runs on port 110, so you would telnet into the mail server on port 110. Try this from a command prompt (you type the italics):

telnet your.mail.server 110
+OK Hello there.
user your_username
+OK Password required
pass your_password
+OK Logged in
STAT
+OK 3 325657

So you simply issue “user” followed by your username, then “pass” followed by your password to log in. There are then various commands you can issue. STAT returns the number of messages waiting on the server and their size. Note that STAT returns a string in the format:

+OK MESSAGE_COUNT MESSAGE_SIZE

There are other commands you could use: LIST gets a list of the messages on the server, with their sizes. RETR n would retrieve one of them.

So we could use the STAT command to get the number of messages on the server. This is a useful exercise in the Macro Scheduler Telnet functions:

TelnetConnect – connect to a telnet server
TelnetSend – send a string to a telnet server
TelnetWaitFor – wait for a specific response and returns the text received
TelnetClose – close the session

So here’s the code to connect to a pop3 server, enter username and password and issue the STAT command, then disconnect:

TelnetConnect>mail.server.com,110,pop3
TelnetSend>pop3,user YOUR_USER_NAME%CRLF%
TelnetWaitFor>pop3,Password required,5,resp
TelnetSend>pop3,pass YOUR_PASSWORD%CRLF%
TelnetWaitFor>pop3,logged in,5,resp
TelnetSend>pop3,STAT%CRLF%
TelnetWaitFor>pop3,OK,5,resp
TelnetClose>pop3

Line 1 connects to the mail server on port 110 and returns a session variable which we will use to access this session with the other commands.

Line 2 sends “user YOUR_USER_NAME”. Note that we also send a CRLF, otherwise it would be as if we didn’t press return. Nothing happens until the telnet server receives a CRLF.

Line 3 waits for “Password required” to be returned. We then know the server is ready to receive the password.

Line 4 sends “pass YOUR_PASSWORD” followed by the obligatory CRLF.

Line 5 waits for “logged in” to be returned.

Line 6 then sends the STAT command and Line 7 waits for it to complete (waits for “OK”).

The TelnetWaitFor command returns the response in a variable, which we have called “resp” above. We can now parse this to determine the number of messages waiting. As noted above STAT returns the message count in the second column. So we just need to do:

Separate>resp,SPACE,parts
Let>count=parts_2
MessageModal>There are %count% messages waiting

In other words, we split the returned lines into an array delimited by the SPACE character. So we end up with three entries, where the second is the one we want.

So the full script to connect to a mail server and retrieve the message count is:

TelnetConnect>mail.server.com,110,pop3
TelnetSend>pop3,user YOUR_USERNAME%CRLF%
TelnetWaitFor>pop3,Password required,5,resp
TelnetSend>pop3,pass YOUR_PASSWORD%CRLF%
TelnetWaitFor>pop3,logged in,5,resp
TelnetSend>pop3,STAT%CRLF%
TelnetWaitFor>pop3,OK,5,resp
TelnetClose>pop3
Separate>resp,SPACE,parts
Let>count=parts_2
MessageModal>There are %count% messages waiting

Only 11 lines of code. It will work quickly and transparently without having to download anything to disc. More to the point, it’s a useful example on how to use the Telnet functions. Hope you find it helpful.

November 25, 2008

Easy Text Extraction and Conversion

Filed under: Announcements,Scripting — Marcus Tettmar @ 11:58 am

If you’ve ever had to write code to read through large text files looking for patterns of text and extracting data you’ll know how fiddly it can be, even with Macro Scheduler‘s simple file reading and text parsing functions.

Well, there’s an amazing tool called TextPipe from DataMystic which makes extracting and transforming text data much easier. TextPipe can read huge files really quickly and allows you to create filters to process and extract the data without having to write any code. And the bonus is that TextPipe plays well with Macro Scheduler too, via its command line interface or ActiveX COM interface which you can call via VBScript.

I think TextPipe is a really nice add-on to Macro Scheduler so we’ve cut a deal with DataMystic to give Macro Scheduler customers a 25% discount off TextPipe Lite, TextPipe Std or TextPipe Pro. Registered users can log into the secure download area to grab a discount coupon which you can use at their site.

Once you’ve created a filter in TextPipe you can easily run it from Macro Scheduler via the command line, or from some VBScript code. And if you want to get really clever you can create filters on the fly with VBScript.

Here’s a simple macro which grabs some log files from a server, then runs a TextPipe filter and does something with the output:

VBSTART
Sub RunTextPipeFilter(filter_file)
  Dim TextPipeApp, Filter

  Set TextPipeApp = CreateObject("TextPipe.Application")
  Set Filter = TextPipeApp.newWindow
  Filter.openFilter( filter_file )
  Filter.execute()
  Set Filter = Nothing
  Set TextPipeApp = Nothing
End Sub
VBEND

//download the server logs
FTPGetFile>server,admin,7Gh63^,21,c:\filters,\logs\,*.log,A

//run the filter
VBRun>RunTextPipeFilter,c:\filters\extract_login_errors.fil

//transfer the output file
CopyFile>c:\filter\login_errors.txt,\\pluto\reports\login_errors.txt

For more on what you can do with TextPipe’s ActiveX interface see the VBScript Examples at DataMystic.com which can easily be used inside Macro Scheduler.

October 31, 2008

Asynchronous Processes

Filed under: Automation,Scripting — Marcus Tettmar @ 12:05 pm

The other day a customer had a problem. He wanted to use this VBScript code to trigger macros in Microsoft Excel workbooks. The trouble is that his company’s Excel security settings are set so that macros in non-signed Excel workbooks pop up a warning dialog.

http://www.mjtnet.com/images/xlwarn.jpg

The customer has to click a button to enable macros (or press the ‘E’ key). This dialog holds up the VBScript code until the button is clicked.

He didn’t want to have to digitally sign every single workbook and he wasn’t allowed to change the security settings.

Now, with regular Macro Scheduler code you could start Excel and send keystrokes to it to run the macro and therefore you could have the script send keystrokes to the warning dialog. But if you are using Excel’s ActiveX interface to run the macro via VBScript, the VBScript gets held up by that dialog.

The solution is to use an “asynchronous process” which does nothing but waits for the dialog to appear and then hits the button (sends the ‘E’ key). This process is launched just before the VBScript code that runs the macro is called. Then, although the main script is held up, the other script can cancel the warning, allowing the main script to continue.

We can do this by creating another macro and compiling it to an executable:

WaitWindowOpen>Security Warning
Wait>0.5
Press ALT
Send>e
Release ALT

We compile that code to an executable and then have our main script launch it – without waiting for it to complete – before calling the VBScript code:

VBSTART
Sub RunXLMacro(xlfile,macro)
  Dim xlApp
  Dim xlBook

  Set xlApp = CreateObject("Excel.Application")
  Set xlBook = xlApp.Workbooks.Open(xlfile)

  xlApp.visible = True
  xlApp.Run macro

  xlApp.Quit
  Set xlBook = Nothing
Set xlApp = Nothing
End Sub
VBEND

//Launch the dialog cancel process
Run>%SCRIPT_DIR%\ClearWarning.exe -HIDE -NOSYSTRAY

VBRun>RunXLMacro,C:\Documents\MyWorkBook.xls,SampleMacro

Another way to do this would be to use Macro Scheduler’s Triggers. Instead of writing and launching a second macro, just create another macro and under Macro Properties specify a Window Open trigger. This macro would then detect the warning dialog and clear it. But some benefits of the second “asynchronous” macro process are that you only run it when you need it (you might WANT to have to manually clear the warning dialog when using Excel manually) and it is also portable – you can use this technique with compiled macros that you distribute, and Macro Scheduler does not need to be installed.

Now, as stated earlier, we could have coded the whole thing – open Excel and launch the macro – using regular Macro Scheduler code and sending keystrokes, instead of using VBScript, therefore avoiding the problem. But VBScript is a nice way to run Excel macros, and I’m sure there are other scenarios where the “asynchronous process” technique may be useful.

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/

October 24, 2008

SOAP and WSDL Interaction

Filed under: Scripting — Marcus Tettmar @ 9:27 am

A recent post in the forums asks how to consume a SOAP service with Macro Scheduler. View the post for some techniques and examples.

October 22, 2008

SSH with Macro Scheduler

Filed under: Automation,Scripting — Marcus Tettmar @ 1:20 pm

As you know Macro Scheduler has Telnet, FTP and HTTP functions built in (and with v11 FTP/HTTP will support SSL). But what if you want to run some other secure protocol such as SSH?

We’ve taken the view not to reinvent the wheel and add every possible secure communications protocol to Macro Scheduler, especially as there are some great components already available that you can use in your scripts. Rather than add bulk that won’t always get used, I’d prefer to leave the security stuff to the experts and use a plug-in component when needed, such as one of the Active-X components from WeOnlyDo Software.

WeOnlyDo make Active-X components for all kinds of things, such as SSH and SFTP as well as the more general protocols Telnet, POP3, SMTP, amongst others. I recently downloaded their SSH component to help out a customer who needed a macro to access an SSH server, and was impressed with how easy it was to implement.

You can download the SSH component here.

Here’s an example I put together which logs into an SSH server, changes directory to /usr/bin and performs a directory listing, returning and displaying the results:

VBSTART
Function SSHExample(server,username,password)
  Dim ssh, a, resp

  Set ssh = CreateObject("WeOnlyDo.wodSSHCom.1")

  ssh.HostName = server
  ssh.Login = username
  ssh.Password = password

  ' set Blocking to true or false - blocking means the script is synchronous and waits for each method to complete
  ssh.Blocking = True

  ' Protocol: 0: Raw, 1: Telnet, 2: SSH1, 3: SSH2, 4: SSHAuto (Auto Negotiate)
  ssh.Protocol = 4

  ' remove ANSI codes from received data
  ssh.StripANSI = true

  'now connect
  ssh.Connect

  'wait for initial prompt
  ssh.WaitFor("regex:[\]\$] $")

  'issue a few commands, returning the output from the final one as the function result
  ssh.Execute "cd /usr/bin" & vbCRLF, "regex:[\]\$] $"
  SSHExample = ssh.Execute("ls -al" & vbCRLF, "regex:[\]\$] $")

  'finally disconnect
  ssh.Disconnect
End Function
VBEND

Input>server,Server:
Input>username,Username:
Input>password,Password:

// here we call the above VBS function to connect and run the command "ls -al"
VBEval>SSHExample("%server%","%username%","%password%"),response

//output the response
MessageModal>response

Notice the Execute statements above which execute a command and wait for the specified prompt to appear. The regex is simply waiting for any “]” or “$” found at the end of a line, which in my case signifies a prompt. Instead of using regex you can provide the exact prompt.

The component is nicely documented in the help file, with useful examples, so you should find it pretty easy to get up and running.

If you use any other components that work nicely with Macro Scheduler, let me know.

« Newer PostsOlder Posts »