March 11, 2008

Embedding Files in Macro Scheduler Scripts

Filed under: Scripting — Dick Lockey @ 8:38 pm

The purpose of embedding a file into a Macro Scheduler script is to be able to have a script use that file on a computer that might not have the file or that the file is in an unknown location. For example a spreadsheet or database file could be embedded so that the script could work with data on a computer that does not have other access to the data. Or a custom dynamic link library (DLL) file or an executable file could be embedded to provide functionality that would not normally be found on all computers.

Though it’s not necessary to understand file construction to be able to embed files into Macro Scheduler scripts, the following discussion may help you understand what is going on in the process. This discussion may only apply to Microsoft Windows operating systems and the English keyboard.

All files are constructed from bytes. There are 256 unique 8 bit bytes sometimes referred to as octets. If you feel the need to investigate, Google the word “byte” and you will find enough information to keep you busy studying for years. ASCII (American Standard Code for Information Interchange) represents each of these 256 bytes with a unique character symbol and an integer from 0 to 255. Some of these characters are found on your keyboard. For example, if you open Notepad, then press the “A” key with your caps lock on then save the work to a file. That file will contain the byte represented in ASCII as an “A” character. The integer for ASCII character “A” is 65. The binary number, which is how the computer sees the character, is 01000001. Notice that there are 8 characters in the binary representation, that is why they are referred to as octets. To reiterate, ALL files are constructed from combinations of 256 8 bit bytes.

With a few inclusions the ASCII characters in the range from 32 through 127 are generally referred to as “text” characters. These are the characters that you can type from your keyboard. There are three notable inclusions. ASCII character number 9 is a Tab. ASCII character number 10 is a line feed and ASCII character number 13 is a carriage return. You will send ASCII characters 13 and 10, in that order, whenever you press the enter key while editing a text document.

Macro Scheduler scripts are “text” files. This means that scripts should only contain the ASCII characters in the range from 32 through 127 and the notable inclusions. Since most file types are not “text” files, they will include most or all of the 256 ASCII characters. The challenge is how to embed a non-text file within a text only Macro Scheduler script. I know of three easy answers.

In ASCII every character has a representative number. Numbers are text characters. VBScript, which is functional within Macro Scheduler scripts, can convert file bytes to ASCII numbers or ASCII numbers to file bytes.

A second way to represent bytes is by the hexidecimal equivalent of the ASCII number. Google “hexidecimal” for more information on hexidecimal (often shortened to hex). Like ASCII, hexidecimal is a text representation of each of the 256 bytes from which files are constructed. VBScript can be used to convert file bytes from hexidecimal and hexidecimal to file bytes.

A third way to represent file bytes as text is by using Base64. If you want a detailed explanation of base64 look here: http://en.wikipedia.org/wiki/Base64
Base64 represents ASCII bytes as text by grouping file bytes by threes and representing the group with a unique four character text name.

Base64 generally uses less space than ASCII or hex. ASCII uses from 1 to 3 characters to represent each character, plus you need a delimiter because you wouldn’t otherwise know where each character description began and ended. So you have a minimum of 2 characters per file byte and in most cases 4 characters per file byte. Hex is more efficient since it uses exactly 2 characters to represent each file byte and therefore no delimiter is needed. Base64 is better still because its technique only adds about 30-40% more characters than the original text and no delimiter is needed. The following example shows the efficiencies using the phrase “A quick brown fox”.

Text: A quick brown fox
17 characters

Base64: QSBxdWljayBicm93biBmb3g=
24 characters

Hex: 4120717569636B2062726F776E20666F78
34 characters

ASCII: 65 32 113 117 105 99 107 32 98 114 111 119 110 32 102 111 120
61 characters (Spaces count)

Base64 has one more huge advantage when it comes to embedding files in Macro Scheduler scripts, Base64 encoding and decoding is built into Macro Scheduler. One line of Macro Scheduler code will encode or decode a file rather than 20 lines or more to encode or decode to ASCII numbers or hex using VBScript.

Here is a process we can use to embed a base64 encoded file into a script.

The first step is to create a new script. For convenience you might call it “Base64 to clipboard”. This new script will contain the following lines.

Input>filename,Browse to Select a file for Base64 encoding
ReadFile>filename,filedata
Base64>filedata,Encode,b64data
PutClipBoard>b64data

Running this script will encode the selected file’s contents to base64 and place the base64 encoding onto the clipboard.

The next step is to get the base64 encoding into a script. Open a script in the advanced editor, place the cursor in an appropriate location and type:

Let>SomeVariable=

At the end of that line press Ctrl+C to paste the base64 encoding into the script.

We have now, encoded a file to base64 and placed the encoded file text into the script and assigned the text to a variable. The next step is to write the text back out to a file that will be used by the script. To do that we use the WriteLn> function. One very useful feature of the WriteLn> function is that by default, it adds the carriage return and line feed characters on the end of each line it writes. Unfortunately, those characters didn’t exist at the end of the original file and if we add those characters to the file we are creating, the file will be corrupt. Fortunately we can disable the default behavior by simply setting the Macro Scheduler system variable WLN_NOCRLF to 1. The next two lines will properly create our file.

Base64>SomeVariable,Decode,BinaryData
Let>WLN_NOCRLF=1
WriteLn>[Path]\[FileToCreate],wres,BinaryData

“[Path]\[FileToCreate]” is, of course, the path and file that you want the script to write for later use. “wres” is the result variable required by the WriteLn syntax. “BinaryData” is the variable that contains the Base64 decoded file information. You might want to use %TEMP_DIR% or %SCRIPT_DIR% in place of [Path] to have the file saved to the temp folder, or in the same location as the script.

The file is now available for use by the script.

Good practice dictates that we would place lines in the script to delete the file as the script is closing. And to be safe, I like to check to see if the file exists as the script is opening and if it exists, delete it there as well. So I would place the following lines at the start and at the end of a script.

IfFileExists>[Path]\\\[FileToCreate]
  DeleteFile>[Path]\[FileToCreate]
Endif

___________
Dick Lockey is M.I.S. Manager at Iowa Laser Technology, Inc., and has been using Macro Scheduler in his work since 2001. He is a regular contributor to the Macro Scheduler forums.

March 4, 2008

OnEvent – Dealing with Indeterminate Dialogs

Filed under: Automation,Scripting — Marcus Tettmar @ 2:34 pm

Most of the time when we are automating a process we are able to predict the sequence of events. We are working with a deterministic process and a linear flow of actions. But there are occasions when things can happen during a process that we cannot predict precisely. E.g.:

  • We might know that a dialog or window may appear sometime during the process, but we cannot predict exactly when that will happen.
  • We may have a situation where after entering some data into a text field a dialog may, or may not appear.
  • There might be some other software running on the PC which randomly pops up an error box. And we need a way to clear that when it happens.

There are a number of ways we can deal with such situations.

Window Event Schedules

If you have a situation where a known window can randomly appear – say a known error box – which always has the same window title, the simplest approach is to use the Window Event schedule in the Advanced Scheduling properties. Simply create a macro which closes the box – perhaps all it has to do is press enter – and specify the window title under Advanced Options in the macro properties. Then whenever Macro Scheduler sees this window it will run the macro and clear it.

Synchronous Coding

In the case where a window may, or may not appear after entering some data into a field, say a data validation dialog, we could just deal with this after sending the text, in regular fashion – something like:

Send>the_data
Wait>0.5
IfWindowOpen>Verification Alert
  Press Enter
Endif

So we simply send the data then IF the verification window appears, close it. But what if you have hundreds of data fields to enter? Dealing with each one would involve a lot of extra code.

OnEvent Window Event Handlers

Another way is to use the OnEvent function to create an event handler in your main script. There are three types of window events that can be monitored with OnEvent:

  • WINDOW_OPEN – monitors a specific known window title, or window title substring
  • WINDOW_NOTOPEN – fires the event handler when specified window closes
  • WINDOW_NEWACTIVE – fires the event handler when there’s a new foreground window

OnEvent is used to create an “event handler” which is just a subroutine which will be executed whenever the event occurs. So, for example, using OnEvent you can tell the script to run a subroutine whenever a specified window appears, whenever that may be, while the rest of the script is executing.

So let’s say we are working with an application which could, at any time, pop up a warning box titled “Connection Error”, and this can be cleared just by pressing enter to hit the default OK button:

OnEvent>WINDOW_OPEN,Connection Error,2,CloseWarning

..
.. rest of script here
..

SRT>CloseWarning
  Press Enter
End>CloseWarning

Of course there are a whole load of other things you can do. We may have a window whose title is always the same but the content differs and we need to react according to the content. In this case our event handler subroutine would have extra code in it to determine which type of dialog it is. We might do this using the text capture functions to read the text from the dialog, or using Screen Image Recognition to check for the presence of an object.

Maintaining Focus

Here’s an idea for an event handler which ensures the target application is always focused. If another application should steal focus at any point during the running of the script, it just grabs focus back again. It’s always good advice to use SetFocus before sending text. But if you have thousands of Send commands and want to slim down your script and make it more readable you could use this approach. Anyway, it’s just an example:

.. your code here to start and focus the app you want to automate, e.g.:
Run>Notepad.exe
WaitWindowOpen>Untitled - Notepad

//assuming the target window is now focused, get it's handle and process name
Let>WIN_USEHANDLE=1
GetActiveWindow>MyWindowHandle,x,y
GetWindowProcess>MyWindowHandle,pid,MyProcessName
Let>WIN_USEHANDLE=0

//now set up the event that is fired when a new window appears
OnEvent>WINDOW_NEWACTIVE,0,0,HandleNewWindow

..
..
.. rest of script here
..
..

//When a new window that does not belong to our process appears,
// set focus back to our window
SRT>HandleNewWindow
  Let>WIN_USEHANDLE=1
  GetActiveWindow>hwnd,x,y
  GetWindowProcess>hwnd,pid,winProcName
  If>winProcName<>MyProcessName
     SetFocus>MyWindowHandle
  Endif
  Let>WIN_USEHANDLE=0
End>HandleNewWindow

Note how this code gets the window handle and process name of your target window. Then whenever a new window appears the HandleNewWindow subroutine is fired which gets the process name of the active window. If the process name of the new active window is not the process name of your target window (i.e. the new window belongs to some other application) it sets focus back to your original window.

I hope this gives you a useful introduction to OnEvent event handlers and how they can be used to run code at any point during the script in response to events. OnEvent can also be used to detect files, dialog events, dialog changes and keyboard and mouse actions. For further information please see OnEvent in the help file.

January 3, 2008

Screen Scrape Text Capture Example

Filed under: Automation,Scripting — Marcus Tettmar @ 6:59 pm

In this post I discussed the Text Capture commands and explained what kind of text can be captured.

To try out the text capture functionality launch the latest version of Macro Scheduler. Click New to create a new script and then in the Code Builder locate the “Text Capture Wizard”. Point the mouse at some text while holding the Shift key down.

You can see a video of this in action here.

This Wizard will let you determine whether or not the text you want to capture can be captured with the Text Capture commands. If it is not revealed when you move the mouse over it while holding Shift down then it must not be generated by Windows text out functions. See my previous post for an explanation.

December 12, 2007

Capturing Screen Text

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

As I have mentioned previously, Macro Scheduler 10 introduces some powerful new commands for capturing screen text. In this post I aim to explain what kinds of text can be captured with these new commands and why there will always be some text that cannot be retrieved.

First let’s look at how the existing functions, GetWindowText and GetObjectText work in Macro Scheduler 9.x and below.

Open up Macro Scheduler and click on the Tools menu and then the “View System Windows” option. You’ll end up with a window that looks something like this:

visw.gif

What we are looking at is a tree representation of windows open on the system. In the above screen shot the highlighted line is showing us an object of class “Button” with caption “Test Center”. Each line gives us the current handle of the object, followed by its class name and then its caption text, if any.

This caption text belonging to an object is made available to other processes – it is published if you will. An app can simply ask the control for its text by sending a simple message to it. That is what Macro Scheduler is doing when it builds this list of windows and objects. Macro Scheduler enumerates all top level windows and then for each one enumerates each of its child “windows”. Note that I use the term window interchangeably with object or control here – the controls that appear in the list are “windowed controls” – they have window handles. A handle allows us to interact with the control. If we know its handle we can send a message to it saying “please give me your text”. And so we get the text of the control back. This is what GetObjectText and GetWindowText do.

There are a number of shortfalls to this approach. One is that the “caption text” that the object publishes is not always the text that you see on the screen. In the case of standard Buttons, Edits, Windows and Checkboxes the published text is usually what you see. But other objects don’t necessarily work the same way. We usually know where we are with common controls – ones that belong to Windows, but custom controls in third party software may not follow the same rules. And a treeview’s caption, for example, is not the text belonging to all its nodes which is written to the screen. Furthermore not all text belongs to windowed controls. In Delphi applications, a control class called TLabel is commonly used as a way to write text on a window. These are often used to label other controls like edit boxes. But TLabels are not windowed controls – they don’t have handles. So this technique will not be able to retrieve their text.

We also can’t use this approach to get text from the likes of Word documents or Internet Explorer pages. This text is not just some simple caption property belonging to an ordinary control – it is created in a more direct way.

When Windows writes text to the screen it uses one of a number of functions deep within the Windows API. Most Windows applications will trigger these functions whether or not the programmer realises it. One such function is TextOut:

Windows GDI – TextOut
The TextOut function writes a character string at the specified location, using the currently selected font, background color, and text color.

Note that this function is part of gdi32 which is responsible for graphics – GDI = Graphics Device Interface. So TextOut is being called to “paint” a character to the screen.

With Macro Scheduler 10, when you call one of the new text capture commands Macro Scheduler uses a “hook” to listen in to calls to TextOut and other similar functions. It is therefore able to intercept what is written to the screen and retrieve the text output by a window.

This works with all kinds of applications including Microsoft Office, Internet Explorer, Firefox and the vast majority of everything else. There are still some exceptions though. Remember that this works by hooking these low level functions within Windows that are used to create text. The vast majority of Windows applications will use these system calls (often indirectly). However, some software may not. There’s no reason why a programmer can’t write text in an even lower level way – he might decide to paint a word pixel by pixel.

As an example – Java applications written with the AWT or SWT frameworks write text using Windows API functions. So we can detect text from those. But if you have a Java app produced with the Swing libraries, which handle text output their own way, you’re not going to be able to capture the text from it.

Finally, what about text on images? Well, text on an image was already there. It was painted by the artist. It is set in stone. So text that appears in a jpg, bmp or any other image file, cannot be detected with the new text capture commands, because it isn’t produced on the fly by one of Windows own text output functions.

The best way to determine whether or not the text you are seeing can be captured with the new text capture commands is to fire up the “Text Capture” sample macro. This will show you the text beneath your mouse cursor. So move the mouse over the text you are interested in and see if it gets displayed in the dialog. If it is, you know you can use the text capture commands to retrieve this text in your macro.

The only way to detect text that cannot be detected with the text capture commands is via OCR. Two methods to do this in Macro Scheduler are discussed here and here.

November 20, 2007

EXEs and Hotkeys

Filed under: Automation,Scripting — Marcus Tettmar @ 11:09 pm

Something that comes up now and then is whether or not compiled Macro Scheduler macros can respond to hot-keys. Macro Scheduler itself lets you assign system-wide hot-keys to macros so that when you hit that key combination, no matter where you are in Windows, the macro will be fired.

Some people want to be able to distribute compiled macros (EXEs) to their friends and colleagues and have those EXEs respond to hot-keys in the same way. Something has to be running in order to listen for the hot-key and act on it. In the case of regular old Macro Scheduler that is Macro Scheduler itself. So how do we achieve it with a compiled macro?

Method 1 – make the EXE itself listen for the hot-key:

Use OnEvent to create a KEY_DOWN event handler to watch for the hot-key sequence:

OnEvent>KEY_DOWN,G,5,DoMacro

Label>Idle_Loop
Wait>0.02
Goto>Idle_Loop

SRT>DoMacro
..
.. Your Code Here
..
End>DoMacro

The above will run indefinitely and whenever CTRL+ALT+G is pressed the macro code in the DoMacro subroutine will be fired. Clearly the macro has to be running all the time to listen for the hotkey – after all, something has to listen for it. So you could have the EXE run on startup by adding a shortcut to the Startup folder for example. Perhaps also compile the EXE with the /NOSYSTRAY and /HIDE parms so that the EXE is not visible when it runs. Or, better, compile with a custom icon so that your EXE has it’s own fancy icon in the task bar.

See the documentation for the OnEvent function for more info on how to trap different keystrokes.

Method 2 – Let Windows do the work:

Don’t tell me – all these years using Microsoft Windows and you didn’t realise that keyboard shortcuts can be assigned to shortcuts?

So just create a desktop shortcut to your compiled EXE. Right click on the desktop shortcut and select properties. Notice the “Shortcut key” field. Press the keys you want to use to trigger the EXE. Press Ok. Sorted.

November 18, 2007

Application and System Monitoring

Filed under: Automation,Scripting — Marcus Tettmar @ 9:30 pm

A short roundup of resources and ideas for using Macro Scheduler for system and software monitoring.

Event Log Monitoring

Here’s a tip showing how to create a script to monitor event log messages:
http://www.mjtnet.com/usergroup/viewtopic.php?t=4256

Process/Service Monitoring

This script demonstrates a function called IsProcessRunning to check if a specific process is running. Use it to create a monitor to watch a specific application process and make sure it is running. It could send an email if the process is no longer running. The same script shows some code to get the status of a service which could be used to create a service monitor.

Monitoring Files

Macro Scheduler has built in functions to see if a file exists, get file lists, get file dates, see if a file has changed, read from text files, read from INI files and more. See the IfFileExists, ReadFile, ReadLn, GetFileList, FileDate and ReadIniFile commands in the help file.

Monitoring Web/Intranet Resources

Use HTTPRequest to retrieve data from web resources, or FTPGetFile to download a file from an FTP server. Monitor email with ReceivePOP3.

Monitoring Windows and User Interfaces

If you just want to check that a window exists use IfWindowOpen. GetActiveWindow will return the active window title. Maybe you need an alert when a window contains a specific message, or piece of text. GetWindowText will retrieve all detectable text in a Window. Use GetControlText to retrieve text from a given control class and instance.

But what if you need to monitor a graphical element, or look for some non-detectable text? Use FindImagePos. The Image Recognition functions mean any kind of user interface can be monitored and/or controlled. You can also monitor applications running remotely (e.g. in Windows Terminal Server or Citrix sessions, or even non-Windows VNC apps) or in virtual environments.

One of our customers provides hosted software solutions to clients who require a high degree of up-time. Their clients’ businesses depend on their software remaining online. So they have used AppNavigator to create UI monitors that watch their applications and alert them if an interface goes down.

Alerts

The Event Log monitor example above uses email to alert the system administrator when an event is detected. Email is probably the most common method. Email can also often be used to send an SMS message (check with your mobile carrier). But an alert could just be a message box popup, or the script could run another application, write to a file or database or call a web resource. Pretty much any kind of alert could be created.

Have you used Macro Scheduler for system monitoring? If you have an example or story to share, please post a comment.

October 8, 2007

Screen OCR to Retrieve Otherwise Undetectable Text

Filed under: Automation,Scripting — Marcus Tettmar @ 7:56 am

Some while ago I wrote this post about how to script a tool called Textract to perform OCR against the screen. Well gpulawski has just posted this tip in the forums pointing out that Microsoft Office 2003/2007 comes with something called MODI (Microsoft Office Document Imaging) which can OCR a bitmap or TIFF file and is scriptable. Very cool.

We can use MODI transparently in a Macro Scheduler script to use OCR to extract text from a Window. I’ve posted an example here.

This is really useful if you have a window that doesn’t expose text as text objects and you can’t get at the text any other way (e.g. via the clipboard or controls). You may want to check for the existence of a certain string in a window, or wait for a particular word or phrase to appear in order to determine when a process has completed. If the control doesn’t expose the text and the text cannot be copied to the clipboard, OCR may be the solution.

September 30, 2007

Speech Recognition for Automation

Filed under: Scripting — Marcus Tettmar @ 4:52 pm

The last time I tried speech recognition was about 15 years ago and back then it wasn’t very good. I suppose it was early days for the technology but I pretty much gave up on it. This morning, however, I was reading Scott Hanselman’s post about speech recognition in Vista and wanted to give it a go myself. It just so happened that I was in PC world today so I picked up a decent microphone, which I have been meaning to get for a while anyway – for product demos and Skype etc.

So when I got back I hooked up the microphone to my vista box and started playing with speech recognition. My goodness it is pretty amazing! In fact I am writing this post using dictation.

One really cool thing is that I can start Macro Scheduler macros using voice recognition by speaking the keyboard shortcuts assigned to them. This could be really handy in situations where voice recognition is needed for controlling complicated processes. E.g. Hands-busy medical applications, or for helping people with disabilities. Macros can be written to control those processes and assigned to shortcuts which can be triggered by voice commands. And all this is capable using Windows Vista without extra software (except Macro Scheduler of course).

QuickButtons makes it easier because you just have to say the text of a button rather than the shortcut key sequence. With QuickButtons you can create an AppBar with buttons assigned to keystroke macros, applications or Macro Scheduler macros. The AppBar can remain visible above or below the application you are working in. Just speak the caption of a button and that button is “clicked” and the macro is executed.

So not only have I learnt how cool Vista’s speech recognition is today, I’ve also discovered how it can be used with our very own Macro Scheduler and QuickButtons to great effect. Dedicated voice recognition software can cost thousands. Yet with Vista and Macro Scheduler and/or QuickButtons you can make voice recognition do anything for far less. Awesome.

September 12, 2007

Debug, Debug, Debug

Filed under: Scripting — Marcus Tettmar @ 10:18 am

Many support requests and forum posts I see include a script snippet and a statement like “this value is wrong” or “the if statement says false but it should be true”. So we make an intelligent guess and say “so perhaps the value you are comparing isn’t what you think it is. Use the debugger”.

Stepping through the debugger reveals answers to problems like this quickly. Another way of checking values is to use a diagnostic message box. Whack a line like this into the script:

MessageModal>%MyVariable%

And see if MyVariable is what you are expecting.

Here’s an article I wrote some time ago on how to use the debugger.

http://www.mjtnet.com/blog/2006/05/17/use-the-debugger/

Use it. Debug. Debug. Debug. Please.

August 27, 2007

Date Picker Dialog

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

Here’s a little script I wrote to give you a Date Picker dialog.

Useful if you need to ask the user to select a date and want to make it easy for the user and avoid too much validation.

« Newer PostsOlder Posts »