September 16, 2010

Making a Dialog or Window Stay On Top

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

Edit: 23 March 2011. In Version 12 it is possible to make a dialog stay on top simply by setting the dialog’s FormStyle property to fsStayOnTop. The method outlined in this post is not necessary for v12 dialogs but will remain as it can be used for older versions and the same approach can be used for windows belonging to other processes.

A question that comes up every now and then is how to make a custom dialog stay “on top” of other windows even when it loses the focus. There is a way to do this by calling the Windows API function SetWindowPos. There are examples in the forums but for convenience here’s a version of one:

SRT>StayOnTop
  Let>HWND_TOPMOST=-1
  Let>HWND_NOTOPMOST=-2
  Let>SWP_NOSIZE=1
  Let>SWP_NOMOVE=2
  Let>SWP_NOACTIVATE=16
  Let>SWP_SHOWWINDOW=64
  Let>WindowHandle=%StayOnTop_var_1%
  Let>Flags={%SWP_NOACTIVATE% Or %SWP_SHOWWINDOW% Or %SWP_NOMOVE% Or %SWP_NOSIZE%}
  LibFunc>User32,SetWindowPos,swpr,%WindowHandle%,HWND_TOPMOST,0,0,0,0,Flags
END>StayOnTop

This subroutine takes a window handle and modifies the properties of that window to make it stay above other windows.

For example, if we had just opened Notepad and wanted to force it to stay on top we could do:

GetWindowHandle>Untitled - Notepad,hwndNotepad
GoSub>StayOnTop,hwndNotepad

Or to get the handle of the active window use GetActiveWindow with WIN_USEHANDLE set to 1.

To set one of your own custom dialogs to stay on top use:

GoSub>StayOnTop,DialogName.Handle

Don’t forget that we are at the mercy of Windows here (and possibly the developers of the app we’re trying to force to the top). It’s a bit rude to have a window floating around on top of other windows. And consider what would happen if another window is opened which is also set to stay on top in the same way? You can’t have two windows on top. So multiple “stay on top” windows will still overlap each other depending on which one has the focus. In short you can’t really guarantee that a window will always be on top.

September 15, 2010

Accessing 64 bit System Folders – Turn off File System Redirection

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

If you need to run a 64 bit system command, or a system command that requires access to the 64 bit system folders you may need to turn off File System Redirection.

To turn off File System Redirection call the snappily named Wow64DisableWow64FsRedirection function and remember to turn it back on with the equally memorable function Wow64RevertWow64FsRedirection.

For example:

//turn off File System Redirection
LibFunc>kernel32,Wow64DisableWow64FsRedirection,result,0

Let>RP_ADMIN=1
let>RP_WAIT=1
DeleteFile>%TEMP_DIR%\vss.txt
Run>"cmd.exe" /c vssadmin list shadowstorage  >> "%TEMP_DIR%\vss.txt"
ReadFile>%TEMP_DIR%\vss.txt,vss
MessageModal>vss

//revert File System Redirection
LibFunc>kernel32,Wow64RevertWow64FsRedirection,result,0

Recently someone running the 64 bit version of XP was having a problem running this innocuous looking code:

Run>c:\windows\system32\mstsc.exe

mstsc.exe is the remote desktop client. Macro Scheduler returned an error saying that c:\windows\system32\mstsc.exe could not be found, yet using Windows Explorer we could see mstsc.exe clearly in the system32 folder. The same code worked perfectly fine on a Win7 x64 system.

Eventually I realised what was happening. Because the OS was 64 bit and Macro Scheduler is 32 bit, Windows was redirecting “c:\windows\system32\” to “c:\windows\syswow64” – the 32 bit system folder. There should be a 32 bit version of mstsc.exe in there, but on this particular system it was missing (it may be that earlier versions of Remote Desktop on x64 did not install a 32 bit version). Hence the error that Macro Scheduler could not find the file. We quickly resolved this by turning off File System Redirection using the code above.

Although the current 32 bit version of Macro Scheduler runs on and is compatible with all 64 bit versions of Windows, a 64 bit version of Macro Scheduler is in the pipeline. Until then calling some system level functions may need the above treatment if those commands need to access the 64 bit subsystem. Macro Scheduler can happily execute and interact with 64 bit applications, but Windows will redirect references to some locations such as system32 unless File System Redirection is disabled.

September 8, 2010

Trigger Scripts

Filed under: Automation,Scripting — Marcus Tettmar @ 3:03 pm

Macro Scheduler has a number of scheduling features to allow you to specify when a macro should fire. One of these mechanisms is called a Trigger. There are several trigger types:

  • Window Event
  • File Event
  • Folder Event
  • Custom Event

A Window Event can be set to fire when a specified window appears or disappears. Similarly file events can be set to fire the macro when the specified file exists or ceases to exist. Folder events offer much the same (a folder exists or ceases to exist) but also a little more. A Folder event can also be configured which will fire the macro when a new file appears in the folder, or a file is removed from the folder.

New File Triggers

With a “New File in Folder” trigger the macro will fire whenever a new file appears in the folder, regardless of what that file is called. This can be very useful and is often used to detect incoming files for further processing. In a situation like this we would need to detect that a new file has appeared and then do something with it. Using the New File in Folder trigger will cause the macro to run, but then we need the macro to determine what that file is called. We can do that with a VBScript function which returns the newest file in a folder:

VBSTART
Function NewestFile(Folder)
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oFolder = oFSO.GetFolder(Folder)
dPrevDate = "0"
For Each oFile In oFolder.Files
  If DateDiff("s", dPrevDate, oFile.DateLastModified) > 0 Then
    sNewestFile = oFile.Path
    dPrevDate = oFile.DateLastModified
  End If
Next
NewestFile = sNewestFile
End Function
VBEND

We can then call it like this:

VBEval>NewestFile("c:\downloads"),filename

This will return in filename the path of the newest file in the given folder. So if our macro is fired by a new file appearing in a folder it can then run this code to get that file’s path and do something with it.

Custom Triggers

Custom triggers allow you to create any kind of trigger you can think of by executing Macro Scheduler code. Maybe you want to monitor the contents of a text file or an INI file entry, a registry entry or the size of a specific file. If you can code the check in Macro Scheduler you can create a trigger out of it.

Custom triggers work by linking to a script (.scp) file which contain two subroutines. One subroutine is called Trigger and the other Reset. Initially the Trigger subroutine is called repeatedly until it sets MACRO_RESULT to TRUE. Thereupon Macro Scheduler calls the Reset routine instead until it too sets MACRO_RESULT to TRUE and the cycle then continues.

By way of an example lets say we want to trigger a macro based on the value of an INI file entry. Let’s say our INI file looks like this:

[MyStuff]
Color=red

Let’s say we want to trigger a macro to run when the Color entry in the INI file gets set to “blue”. Here’s our trigger script:

SRT>Trigger
  ReadIniFile>d:\files\myini.ini,MyStuff,Color,gColor
  If>gColor=blue
    Let>MACRO_RESULT=TRUE
  Endif
END>Trigger

SRT>Reset
  ReadIniFile>d:\files\myini.ini,MyStuff,Color,gColor
  If>gColor<>blue
    Let>MACRO_RESULT=TRUE
  Endif
END>Reset

So here the Trigger routine which is executed continually by the scheduler will return TRUE only if Color in the INI file becomes equal to “blue”. Thereupon the Reset routine will be executed which will reset the trigger when Color is no longer “blue”. After that the Trigger routine is executed again.

Since Trigger routines are run frequently on a very tight interval they should be kept as small as possible and should not be long running. Don’t make Trigger scripts that perform too much complicated processing or include delays as this could cause resource issues. Sometimes, for more complicated “triggers” or monitoring scripts you may be better off creating a looping macro or repeating macro. E.g. if you wanted to poll a POP3 server for an email message containing specific content this is a more time consuming process subject to network delays so would probably not be a good candidate for a Custom Trigger. Instead create a macro which checks the email every few minutes, or one that loops continuously.

August 24, 2010

Don’t Overwhelm your Target!

Filed under: Automation,Scripting — Marcus Tettmar @ 4:24 pm

When sending keystrokes to other applications remember that Macro Scheduler works much faster than a human being can type. Many applications do form field verification or background processing on the fly as the text is received. And most applications were designed on the assumption that a human being would be operating them. It may not have occurred to the developers that a robot might try to send a stream of text to the UI at the rate of 5000 characters a second!

With some applications if you try to send too many characters at once you may find that some of those characters fail to show up, or the string is truncated.

The solution should be obvious by now. That is to slow down the key send rate. You can do this easily by setting the SK_DELAY parameter which introduces a specified millisecond delay between each character.

Let>SK_DELAY=20
Send>long_string

Of course you could break the long string down into smaller chunks and insert a delay between them:

Send>part_1
Wait>0.3
Send>part_2
Wait>0.2
Send>part_3

But SK_DELAY is simpler and easier if you can’t control the length of the data easily.

A related issue I see every now and then is when “tabbing” from one field to another on a form. Pressing the tab key moves the focus to the next field, so we use this while sending data into a form. Sometimes we’ll encounter an application which needs a bit of time after a field has been focused before it can accept data, or the other way around. So sending Tab immediately after/before sending the data with no delay fails to work. Adding in a small delay between sending the data and the Tab solves it:

Send>field_data
Wait>0.3
Press Tab

Wait>0.3
Send>next_field
Wait>0.3
Press Tab

etc

Similarly when we need to tab through lots of fields at once without sending data we may try to save ourselves coding time by writing:

Press Tab * 10

But if the app needs time to react between each tab we may have to split that out into separate tabs with delays between them or replace with a repeat/until loop:

Let>tabs=0
Repeat>tabs
  Press Tab
  Wait>0.2
  Let>tabs=tabs+1
Until>tabs=10

As I’ve said many times before the most common causes of things not working as expected are either timing or focus. Macros run way faster than a human can type which is often beneficial but if things aren’t quite right start off by slowing the macro down a bit. You can always speed it up later!

July 14, 2010

Launching URLs in Default Browser (and a small bug!)

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

A quick and easy way to launch a URL in the default web browser is just to use the ExecuteFile command:

ExecuteFile>http://www.mjtnet.com/

However, I have recently discovered that ExecuteFile is currently limited to a command line length of MAX_PATH (260 chars) and that if more than 260 chars are passed it will cause Macro Scheduler to crash. This is a bug that we’ll get fixed in the next update (increase the limit if possible AND prevent a crash if more chars than can be handled are passed).

Another way to launch a URL in the default browser is to use the Run command. First you need to determine the path to the default browser which you can do by querying the registry:

Let>url=http://www.mjtnet.com/

//get path of default browser ...
RegistryReadKey>HKEY_CLASSES_ROOT,htmlfile\shell\open\command,,browserEXE

Run>%browserEXE% %url%

May 25, 2010

Tweetlib: A DLL Plugin for Tweeting Status Updates via oAuth

Filed under: Announcements,Automation,Scripting,Web/Tech — Marcus Tettmar @ 4:23 pm

As noted yesterday I have been waiting on Twitter to provide xAuth access. They declined, saying it was not appropriate. I’m not really sure why.

No matter, I decided to make a small DLL to simplify Tweeting from Macro Scheduler. It uses the full oAuth interface.

Implementing oAuth in Macro Scheduler code would require lots of VBScript code and would be very complicated (although doable in theory). So instead I decided to create a DLL which you can use in Macro Scheduler to tweet in one line of code.

You can download it here.

And then to post a status update all you need to do is something like this:

Let>message=Hello from Macro Scheduler
LibFunc>%SCRIPT_DIR%\tweetlib.dll,UpdateStatus,r,message,buff,1024

Note that the first time you call UpdateStatus you will be asked to log into Twitter and click “Allow” to authorise Macro Scheduler to access your account. You will then be given a PIN to enter. You only need to do this once. If you ever need to revoke access and start over call the RemoveCredentials function. Your Twitter username and password are NOT stored anywhere. This uses the oAuth authorisation scheme which provides an access token. It is the access token which is stored and this only allows Macro Scheduler to access the API for your account.

The return buffer will contain the XML of the status update operation if successful or an error message if not.

See readme.txt and sample .scp in the zip file. Enjoy.

May 24, 2010

Tweeting from Macro Scheduler Without the API

Filed under: Automation,Scripting,Web/Tech — Marcus Tettmar @ 1:51 pm

A while back I posted an article showing how to Tweet via Twitter‘s API. It uses basic authentication which Twitter plan to turn off in the near future. The alternative, oAuth is awkward for desktop based apps, but xAuth is now available and should be doable in Macro Scheduler. I have requested xAuth access from Twitter and, assuming it’s doable, will try and provide an example once I’ve received it and tried it out.

In the mean time it occurred to me that we don’t really need an API if all we want to do is send a status update. We can do that easily using Macro Scheduler and WebRecorder functions by controlling an instance of Internet Explorer, which can be done in the background.

Below is a script which demonstrates this. It offers a function called LoginToTwitter which need only be called once per session, and an UpdateStatus function to update your status. Just set your Twitter username and password in the first two lines and you should be all set.

Let>TW_USERNAME=XXXXX
Let>TW_PASSWORD=XXXXX

//only need do this once per session
GoSub>LoginToTwitter

GoSub>UpdateStatus,This is a test

GoSub>UpdateStatus,This is a test 2

GoSub>LogOut

// END

//*** SUBROUTINES ***//
SRT>LoadWR
  //load the WebRecorder runtime
  LibLoad>IEAuto.dll,hIE
  If>hIE=0
    MessageModal>Could not load IEAuto.dll, make sure it is in the path or edit the LibLoad line.
    Exit>0
  EndIf
END>LoadWR

SRT>LoginToTwitter
  GoSub>LoadWR
  //open IE
  LibFunc>hIE,CreateIE,ieTwitter,0
  LibFunc>hIE,ShowIE,res,ieTwitter,1

  //log in to Twitter
  LibFunc>hIE,Navigate,r,ieTwitter,http://twitter.com/login
  LibFunc>hIE,WaitIE,r,ieTwitter
  LibFunc>hIE,FormFill,r,ieTwitter,,,session[username_or_email],TW_USERNAME,0
  LibFunc>hIE,FormFill,r,ieTwitter,,,session[password],TW_PASSWORD,submit

  LibFunc>hIE,WaitIE,r,ieTwitter
  Wait>1
  LibFunc>hIE,WaitIE,r,ieTwitter
  Wait>1
END>LoginToTwitter

SRT>UpdateStatus
  LibFunc>hIE,FormFill,r,ieTwitter,,,status,UpdateStatus_VAR_1,submit
  LibFunc>hIE,WaitIE,r,ieTwitter
  Wait>1
END>UpdateStatus

SRT>LogOut
  LibFunc>hIE,KillIE,r,ieTwitter
END>LogOut

You need the IEAuto.DLL library which is installed with WebRecorder.

For a bit of fun the following code copies the currently highlighted text to the clipboard and tweets it. So assigned to a hot key it can be used to tweet any text from any application.

Press CTRL
Send>c
Release CTRL
WaitClipBoard
GetClipBoard>theText
GoSub>UpdateStatus,theText

The real challenge is finding something useful to do with it! 🙂

May 21, 2010

Weekly Forum Round-up

Filed under: Automation,General,Scripting,Testing — Marcus Tettmar @ 10:34 am

I thought I might start a weekly round up of some of the Macro Scheduler forum posts that caught my eye during the week. Not everyone gets a chance to browse the forums all the time, so it might help to link to some here. Then those that subscribe to the blog via RSS/Email will see them and they’ll also show up in Macro Scheduler’s News Feed window.

The forums are quite active but I won’t link to every little discussion or request for support – just those that demonstrate a new feature, or work as a “how to” or anything else that I think could be useful. We’ll see how it goes.

So here’s my list for week ending 21st May 2010:

How to trim spaces from the ends of, or within, a string

A Progress Bar Demo using Macro Scheduler 12

How to make a custom dialog minimize to the task bar

Putting a status bar with multiple panels on a custom dialog

Finding a drive based on its label (volume name)

Getting the HTML source of a page using WebRecorder

Running Automated Testing scripts in the background via VMWare

May 19, 2010

Test Automation Class Update; European Dates

Filed under: Announcements,Automation,Testing — Marcus Tettmar @ 10:06 am

Randy has just reported on his blog that the first run of his new Test Automation class which features Macro Scheduler in the hands-on exercises was a great success.

The next class will be held next month in Rome, Italy on 16/17 June, where Randy will also be presenting a workshop entitled “Innovative Software Testing Approaches“.

So if you’re this side of the pond and looking to improve your skills, with the added advantage of a business trip in a beautiful, historical city, check it out.

May 14, 2010

Remove Unnecessary Obstacles

Filed under: Automation — Marcus Tettmar @ 10:48 am

When considering how to automate something, it’s often worth thinking about whether or not you can change the process a little first.

An example that often comes up is the need to automate the retrieval and processing of a particular email message. Many people will already have a manual process that involves, not surprisingly, their email client, e.g. Outlook. They’ll have a filter set up that moves the email they want to look at into a specific folder. Then they’ll take each of the emails in that folder in turn and do whatever it is they need to do to them, such as pull out some information and put it in an Excel file or database or something.

The user’s first thought when he decides to automate something like this is to have Macro Scheduler manipulate Microsoft Outlook to find the email and parse it.

That’s all very well, and is certainly achievable but it does add a rather awkward obstacle into the mix that needs to be navigated around: Outlook.

Look at what we actually want to do. We have some form of email that gets sent, and we want to parse it and perform some action based on it. Outlook just happens to be what we are currently using to read that email. We’re using it because we’re human and Outlook was designed for humans. But it isn’t the only way to read email. The email exists quite happily without it. We could use a different email client. Or remove the need for an email client altogether.

Macro Scheduler can retrieve email directly from a POP3 server, download the email messages to files and then process them. So we can remove Outlook – or any other email client – from the process entirely.

“Wait”, I hear you say, “What about my other email? I don’t want Macro Scheduler messing up my Facebook notifications, birthday reminders, blog updates and work emails” (notice the order of priority there). Well, don’t worry about that, there are several options. Macro Scheduler’s POP3 commands can be set to leave emails on the server and we could set the script up to search the downloaded email and look for only the pertinent messages.

But Ideally we have control at the sending end. We could set up a dedicated email account which these emails get sent to. That simplifies selecting which email needs to be processed. If that is not possible what about creating a new email account (it could be a free webmail account if you don’t have control over your own mail server) and then set up your filtering rule in Outlook, or whatever client you normally use, to forward the desired emails to that email account? This means the only emails in that account are the ones Macro Scheduler should process, thus simplifying the script which now needs only to collect all email in the mail box and can delete it afterwards.

Of course you can take this concept of removing obstacles as far as you like and in the most ideal world whatever it is that sends emails and whatever it is you want the information extracted from those emails inserted into would have a more direct form of communication between each other. But the world is not always ideal and we don’t always have control over the entire sequence of events. At least consider removing unnecessary obstacles from the part of the process you do have control over.

Retrieving and parsing email is just one example. The concept of simplifying a process applies elsewhere too. E.g. moving and copying files using Windows Explorer: instead of automating Windows Explorer by sending keystrokes and mouse events, use the built in file handling commands; pulling data out of a database: don’t automate your database query tool’s front end, access the data directly using SQL; Getting data from an Excel file: no need to try and manipulate Excel via user simulation, you can query the data directly; and so on.

When you sit down and automate a manual process some of the steps in that process might be things you can replace or skip altogether.

« Newer PostsOlder Posts »