Wednesday, May 30, 2007

Automating Applications with Ruby & The Windows Script Host

We've talked at length about automating Windows applications through COM/OLE, using the win32ole library. But not all applications expose themselves (so to speak) to such automation. The Windows Script Host can automate the activation of windows, and the sending of keystrokes. This may sometimes be all that you need to get the job done.

The Windows Script Host (WSH) has been part of the Windows operating system since Windows 98. You can use WSH's Shell object (via COM/OLE) to send keystokes to windows.

First, require the win32ole library:


require 'win32ole'

Now we'll create an instance of the Wscript Shell object:

wsh = WIN32OLE.new('Wscript.Shell')

To send keystrokes to a window, you must first activate the window, bringing it to the forefront. This can be done with the Wscript Shell's AppActivate method, which returns true if the window was successfully activated, and false otherwise. The AppActivate method takes the window title text as it's argument:

wsh.AppActivate('Title')

The string passed to the AppActivate method can be a partial, but must be the start or ending of the window title. The method is not case sensitive, and does not accept regular expressions. To quote Microsoft: "In determining which application to activate, the specified title is compared to the title string of each running application. If no exact match exists, any application whose title string begins with title is activated. If an application still cannot be found, any application whose title string ends with title is activated. If more than one instance of the application named by title exists, one instance is arbitrarily activated."

Once you have the window activated, you may use the Wscript Shell's SendKeys method to send keystrokes to the window. The SendKeys method takes a string in quotes. Special keys (ie, ENTER, TAB, PGDN, PGUP, Function Keys) may be embedded in the string, if surrounded by braces:

wsh.SendKeys('Ruby{TAB}on{TAB}Windows{ENTER}')

The SHIFT key is represented by '+', the ALT key is represented by '%', and the CTRL key is represented by '^', so to quit an application by sending ALT-F4:

wsh.SendKeys('%{F4}')

Further details on the syntax of the SendKeys method can be found
here.

Timing is important using these methods, so you may need to insert a sleep method here and there, to get the optimal performance. For example, a 1-second (or less) wait between activating a window and sending keystrokes, or vice-versa.

So, putting it all together, here's a brief example that activates Notepad (which must be running first), inserts text, saves the file to a specific name, and quits Notepad:

# Require the win32ole library:
require 'win32ole'
# Create an instance of the Wscript Shell:
wsh = WIN32OLE.new('Wscript.Shell')
# Try to activate the Notepad window:
if wsh.AppActivate('Notepad')
sleep(1)
# Enter text into Notepad:
wsh.SendKeys('Ruby{TAB}on{TAB}Windows{ENTER}')
# ALT-F to pull down File menu, then A to select Save As...:
wsh.SendKeys('%F')
wsh.SendKeys('A')
sleep(1)
if wsh.AppActivate('Save As')
wsh.SendKeys('c:\temp\filename.txt{ENTER}')
sleep(1)
# If prompted to overwrite existing file:
if wsh.AppActivate('Save As')
# Enter 'Y':
wsh.SendKeys('Y')
end
end
# Quit Notepad with ALT-F4:
wsh.SendKeys('%{F4}')
end

The above code snippet can be improved upon, and I encourage you to do so. But it, hopefully, demonstrates what can be done.

Mimicing keystrokes is certainly not the ultimate in program automation, but it may sometimes be all that you need to get the job done. For example, back in the days before pop-up blockers, I had written a script that would simply run in the background, look for pop-up ads (based on a list of title strings), and close them. Simple, yet effective.

I should probably also mention AutoIt, "a freeware Windows automation language. It can be used to script most simple Windows-based tasks." I've not used it myself, but I believe that the Watir library leverages it.

That's all for now. As always, let me know if you have questions, comments, or requests for future topics.

Thanks for stopping by!


Digg my article

16 comments:

Anonymous said...

Great site, I've found it really helpful.

One question though, do you know of a way to interact with a VBA msgbox? I'm trying automate Excel and need to accept default answers to popups as they are generated when the spreadsheet is 'running'. Maybe Wscript/AutoIt is the only route?

David Mullet said...

What does the "VBA msgbox" say? In addition to interacting with them, some alerts -- to confirm overwriting an existing file, for example -- can be disabled in Excel...

excel.DisplayAlerts = false

...which may or may not address your problem.

The AppActivate and SendKeys methods used in this article should also work.

David

Anonymous said...

Hi,

While trying to run the following code mentioned in the blog itself, getting an error:

require 'win32ole'
wsh = WIN32OLE.new('Wscript.Shell')
....

Am getting the error on the 2nd line as:

invokingnotepadwsh.rb:4:in `initialize': failed to create WIN32OLE object from `Wscript.Shell' (WIN32OLERuntimeError)
HRESULT error code:0x8007007e
The specified module could not be found. from invokingnotepadwsh.rb:4:in `new'
from invokingnotepadwsh.rb:4
>Exit code: 1

Can you please tell me how to solve this?

Thanks,
Anukul

David Mullet said...

Anukul:

This error may occur if your Windows Script installation is corrupted. You might try reinstalling Windows Script from the Microsoft website.

David

Kiran said...

Hi David,

I am trying to automate excel sheets using Watir(Ruby as a scripting
language).

The excel is used to feed data into database which inturn reflects in
the application.

The data will be inserted upon a click of the button which is present
in the excel sheet.

Is there anyway to click the button. If not is there any way to
identify the coordinates in the excel window and click the same?


Note: I have tried even using Autoit but couldn't go much further.


Thanks in advance for your time and help

Anonymous said...

nooby question: are you supposed to save this script into a text file with a particular extension? What is the extension? .bat? thanks...

Anonymous said...

This does not work on Windows XP SP3

Ephraim Hohn said...

tested with windows xp sp3 and works like a charm! thank you very much!

asu said...

I am a newbie in Ruby. I just want to download a file from a webpage by watir. Upon clicking the downloading link I am able to download that. But how can I get the control of the 'File Download' window.So that my script can click on the Save button of the said window. What i tried is

wsh.SendKeys('{TAB}{TAB}{TAB}{ENTER}')

all the TABs are working but ENTER is not working.I am using IE8. Can anyone help me please. Thanking you.

Saumya said...
This comment has been removed by the author.
Saumya said...

Hi asu,
See if {SPACE} works in place of {ENTER}.

PS: This blog rocks, Cheers.

Anonymous said...

Hi,

is there a way to get the titles of all running applications? And if not, it would be also helpful to get the title of the activated application. So I could search for "a", "b" and then get out the entire title...

Thanks for help
Jens

Tom said...

Great solution, but one question. When I run this code (applied to IE rather than notepad), when the SendKeys is called with the name of the file, the focus of the window jumps to the "Save In" part of the dialogue. This doesn't make sense since when a Save As dialogue appears, focus is set to type into the File Name box. I have a work around of sending 4 {TAB}'s to get the focus in the right spot, but I don't think I should have to. Any idea what is happening? Thanks.

Arijit said...

Hi, I am trying to automate a web application using "Cucumber" with "watir-webdriver". A scenario needs to trigger the fire_event but it just doesn't work. Please let me know if there are any workarounds.
Thanks in Advance.

Unknown said...

Hi ,I am trying to automate excel i.e excel interactive am able to launch excel by using win32ole gem but after that unable switch smartview ribbon which is present on my excel menu so does any one which gem is there to automate.Please let me know if there are any workarounds.

Saylor Twift said...

Thanks so much for this post. Finding information on how to output Ruby keys was extremely hard, everyone just wanted Ruby to read the input, and the Win32ole documentation is lacking. This post is old, actually 10 years I believe, but still works well on Windows 10.