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.
Related posts:
Hi Marcus,
Really Great solution!
The wait command is so important in macros. Everyone hesitates to put a wait in, but the processors run so fast, and multi-threaded, that the Wait command is almost mandatory. I found that even Wait>0.1 (a tenth of a second folks!) can make a script way more reliable. Can we spare a tenth of a second to make it work, and not call it a programming patch or band-aid? Certianly!
I have so many problems using other languages to do simple things. Wait, TestForReady, IsAvailable, IsLocked etc…
For example, the dreaded Outlook.pst backup.
Marcus, can you show us the way to do this kind of looping to test for files to be ready?
The scripts run too fast these days. You write a good script that works on one PC, then move to another PC and it’s all errors because the execution speed is different for PC to PC.
Please elaborate more on these Wait, TestForReady, IsAvailable, IsLocked etc… they are so important. And these are also what make your product and support above the rest in the real world of scripts!!!
There are lots of ways to wait for events to complete. Usually we’d watch for a visual cue using for e.g. WaitWindowOpen, WaitWindowClosed, WaitScreenImage, WaitPixelColor, WaitRectChanged, WaitScreenText, WaitCursorChanged or somesuch.
Have a look at this post and the section entitled “Make the script wait for events to complete.”.
http://www.mjtnet.com/blog/2008/10/27/top-tips-for-reliable-macros/