November 2, 2010

Waiting for the Clipboard

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

Today I was helping someone who was wanting to write a script to take screen-shots from one application and then paste those screen-shots into Microsoft Excel.

Initially things weren’t working reliably because the script didn’t factor in the time taken for the large bitmap of a screen-shot to exist in the clipboard after pressing the print screen button, before attempting to paste into Excel. E.g. consider:

Press Print Screen
SetFocus>Microsoft - Excel*
Press CTRL
Send>v
Release CTRL

The above is probably going to fail most of the time because a screen shot is a large bitmap and is going to take some time to arrive on the clipboard, but the script above performs a paste in Excel immediately after pressing print screen. The print screen key being depressed and the clipboard containing the bitmap are not the same thing.

While we could have just said “wait 5 seconds” and that would probably have been fine for evermore, it isn’t very sensitive and wouldn’t be ideal for a script that needs to run as fast as possible. Ideally we only want to wait until we know the bitmap is in the clipboard.

Text is usually smaller than a bitmap, but for large text items one way to make things bulletproof is to do something like this:

PutClipBoard>dummy

SetFocus>source_app_title
Press CTRL
Send>c
Release CTRL

Label>wait_for_data
Wait>0.2
GetClipBoard>clipdata
If>clipdata=dummy
  Goto>wait_for_data
Endif

By putting a known value onto the clipboard in the first place we can then have a little loop which keeps checking the clipboard until the returned value is not our known value. We then know our CTRL-V has worked and that we can safely paste to the target application.

But how can we do the same thing when the clipboard data is an image? The above won’t work because GetClipBoard won’t return anything for non-textual data.

Well, Windows has a function called IsClipboardFormatAvailable which will allow us to determine what kind of data is on the clipboard. So we could use this in a similar way to above to see if the clipboard contains a bitmap or not. Like this:

Let>CF_BITMAP=2

Let>haveBMP=0
PutClipBoard>dummy

Press Print Screen

While>haveBMP=0
  LibFunc>user32,IsClipboardFormatAvailable,haveBMP,CF_BITMAP
  Wait>0.2
EndWhile

We could then paste it somewhere:

SetFocus>Document - WordPad
Press CTRL
Send>v
Release CTRL

It’s always nice to wait only as long as we have to and it makes the script more reliable and portable.