Saturday, April 14, 2007

Automating Word with Ruby: The Application Object

Automating Microsoft Word can involve hundreds of objects, each with its own properties and methods. You can use the ole_methods method, or Word's built-in Object Browser, to browse the objects, properties, and methods. For further details, see this article.

If you've read some of my articles about automating Excel with Ruby, you will see similarities between the two object models.

Let's start by looking at Microsoft Word's top-level Application object.

You'll use the win32ole library to create a new instance of the Word application object:


require 'win32ole'
word = WIN32OLE.new('Word.Application')

...or to connect to an existing instance of the Word application object:

word = WIN32OLE.connect('Word.Application')
document = word.ActiveDocument

I often use the connect method for ad hoc scripts to perform a series of actions on a Word document (or Excel workbook) that I already have open.

Note that a new instance of Word will be not visible by default. To show it, set the application object's Visible property to true:

word.Visible = true

You may want to first hide Word until your data input and formatting process is complete, then make it visible. This may speed things up, and prevents the user from interfering with your program (and vice versa).

You'll spend most of your time working with Document objects, but first let's look at some of the properties and methods for the Application object.

Call the Version method to determine the version of Word, which is returned as a decimal string (ie, "11.0"):

if word.Version.to_i => 11
# do something
else
# do something else
end

You can set the PrintPreview property to place Word in print preview mode:

word.PrintPreview = true
word.PrintPreview = false # return to previous view mode

The System object includes a number of (read-only) properties about the system that Word is running on. Most of these will be of no use whatsoever to you, but a few may prove helpful:

word.System.OperatingSystem # => "Windows NT"
word.System.HorizontalResolution # => 1440
word.System.VerticalResolution # => 900
word.System.LanguageDesignation # => "English (U.S.)"

The FontNames, PortraitFontNames, and LandscapeFontNames methods return collections of font names:

for font in FontNames
puts font
end

The Tasks method returns a collection of Task objects, representing tasks being run on the PC. Most of these tasks will be processes that you have no interest in. But you can, for example, use this to determine quickly if a particular application is currently running and, if so, address that application's window:

if word.Tasks.Exists('Firefox')
word.Tasks('Firefox').Activate
word.Tasks('Firefox').WindowState = 1 # maximize
word.Tasks('Firefox').WindowState = 2 # minimize
word.Tasks('Firefox').WindowState = 0 # restore to normal
end

Calling ole_methods on one of these Task objects reveals what we may do with it:

irb(main):004:0> word.Tasks('Firefox').ole_methods

=> [QueryInterface, AddRef, Release, GetTypeInfoCount,
GetTypeInfo, GetIDsOfNames, Invoke, Application, Creator,
Parent, Name, Left, Left, Top, Top, Width, Width, Height,
Height, WindowState, WindowState, Visible, Visible,
Activate, Close, Move, Resize, SendWindowMessage,
GetTypeInfoCount, GetTypeInfo, GetIDsOfNames, Invoke]

So we see that we can, for example, hide, unhide, move, resize, and close other application windows through the Task object:

word.Tasks('Firefox').Visible = false
word.Tasks('Firefox').Visible = true
word.Tasks('Firefox').Top = 100
word.Tasks('Firefox').Left = 300
word.Tasks('Firefox').Height = 500
word.Tasks('Firefox').Width = 500
word.Tasks('Firefox').Close

You should, naturally, use the Tasks collection with care, as you could wreak havoc otherwise.

To quit Word, call the Quit method:

word.Quit # Do not save changes
word.Quit(0) # Do not save changes
word.Quit(-1) # Save changes
word.Quit(-2) # Prompt user to save changes

That's all for now. Next up, we'll look at working with the Document object. As always, let me know if there are any specific topics you would like to see discussed here.

10 comments:

Walker said...

Just curious...why do you think Tasks is an option from the word application object? What does controlling tasks have to do with Word?

Matt said...

I recently tried to control Windows Media Player (v. 11) from a Ruby script and was successful when creating a new instance of the app, ie. WIN32OLE.new('WMPlayer.OCX') but had no luck trying to connect to a running instance using WIN32OLE.connect(). I assume WMPlayer (I only tried v.11) doesn't expose an OLE interface but haven't been able to confirm that. Does anyone have a clue?

David Mullet said...

walker: I'm not sure why MS decided to include the Tasks collection in the Word object model. While it may prove useful, it doesn't seem to be consistent with the model.

matt: Sorry, but I have yet to find any info on connecting to a running instance of WMP.

David

kim said...

Would love to see the series also covering MS project..

David Mullet said...

@kim-

I'm not too familiar with MS Project, but will see if I can offer anything of value here in the coming weeks.

David

KTamas said...

Interestingly if i do a document.Close (where document is a Document object), Quit doesn't work.

shiju said...

This is very helpful for me. Thanks a lot. But i am serching how to get the line count in a .doc file. Can anybody help me

Yudi said...

Hi all,

I need help with ruby and win32ole. I use ruby version 1.8.6.
I use irb and type require 'winole32' and the output came out "no such file to load".

I need urgent help what causing this? Thanks in advance for the help.

Lohith MV said...

I am using win32ole API to convert word to PDF, everything is running smoothly but if I get some invalid file it will prompt for some action(user needs to pick some option),so how do we suppress these prompts ? is it possible to run everything in headless mode ?
IF yes how do I set those flags ?

Anonymous said...

Hi..Is there any way to combine two word documents into a single document? Anybody please help..thanks in advance...