Introduction

This article is the second in a series describing the VOLE C++/COM Automation driver library. (The first one is here.) It illustrates the use of VOLE to rewrite (and dramatically simplify) Microsoft's own code example for how to drive Word's automation object model from C++.

Background

A new user of VOLE recently emailed me to ask whether VOLE could handle driving Microsoft Word, to achieve the functionality involved in Microsoft's own code example. He wrote:

"I have just come across your library while looking for a reasonably sane way to add a bit of Excel automation in my C++ program. Not being familiar with COM I found this example http://support.microsoft.com/kb/238393 too scary. Then I found your wrapper that promises to make COM easier to use."

The full program is included in the download; here I'll just show the salient parts. For clarity here, we'll assume that using declarations have been employed for the VOLE classes.

Step 1. Look up the CLSID for "Word.Application"

This step is easy: we can omit it entirely.

Because the (static) vole::create() methods allow the user to specify the class to be create via CLSID ({000209FF-0000-0000-C000-000000000046}), Programmatic Id ("Word.Application"), or string form of the CLSID ("{000209FF-0000-0000-C000-000000000046}"), we can just pass the Programmatic Id.

Step 2. Start Word and Get an IDispatch Pointer for the Application Object

To start the Word automation server, we pass "Word.Application" to the vole::object::create() method. This method returns an instance of vole::object that owns the underlying COM interface associated with the server.

Note that, just as in the Microsoft example, we must specify CLSCTX_LOCAL_SERVER to invoke Word as a local server.

Step 3. Make Word Visible

To make the Word application object visible, we set its Visible property to true. This is done by using the vole::object::put_property() method, as in:

word.put_property(L"Visible", true);

Step 4. Get the Documents collection

For this we need to elicit the application object's Documents property, in an instance of vole::collection, via the vole::object::get_property() method, as follows:

collection documents = word.get_property<collection>(L"Documents");

Note the explicit specialisation of the member function template vole::object::get_property(). All modern compilers are able to handle this modest sophistication, but some (in particular VC++ 6) are not. I'll show the alternate code, where required, for each step at the end of this article.

Step 5. Call Documents.Open() to Open "C:\Doc1.doc"

To invoke an Automation server's methods, we use the vole::object::invoke_method():

But that's less clear. Obviously, VOLE, although very expressive compared to raw C++ manipulation of COM Automation servers, still has a degree of verbosity to it. I tend, therefore, to try and keep each COM property/method invocation to a single C++ statement.

Step 13. Close the Document Without Saving Changes

Since we've modified the document object, trying to quit Word will fail because it will ask us whether we want to save our changes. So we first call the document method Close, passing false to indicate that we want to discard the changes.

document.invoke_method<void>(L"Close", false);

Step 14. Quit Word

Finally, we instruct Word to quit, which causes the Word application to shut down.

word.invoke_method<void>(L"Quit");

And that's all there is to it. Compared to the ~220 lines of code in the Microsoft example it's a huge win in expressiveness. Add in the flexibility in handling different fundamental types and character encodings, the robustness in that all server resources are handled in an exception-safe manner, and the fact that it's 100% header-only and compiler-independent, it's pretty clear that VOLE represents an optimal solution for driving COM Automation servers from C++. But I would say that, wouldn't I? So I invite you to try it out for yourselves.

Compatibility with Older Compilers

As described in the first article in this series, some old compilers (e.g. VC++ 6) have problems with VOLE's template syntax, so an alternate form is also supported. The alternate form is pertinent to the following steps:

History

5th August 2007: First version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

All the articles I've seen that use vole have the whole usage within a single function, is this because vole::object has no default constructor so for example you can't have the object within a class which methods can access ?

I'm just curious as to why all examples seem to have all the code within a main() type block.

The reason I've only presented a main() function is just to keep the examples small.

But you're correct about the default constructor. The reason is that by not providing a default constructor, it means that you're always guaranteed to be working with a properly constructed object. It simplifies the implementation considerably.

Could you provide a use-case illustrating your need? I'm reluctant to change the no-def-ctor, but I'm also reluctant to make VOLE-users' lives difficult.

Hi Matt, a good case example for this would be interfacing to photoshop, take cs, cs2 and cs3 for example, all these versions can share identical code apart from the COM object it interfaces to, which means duplicating the code 3 times but with a different string pointing to the relevent registered COM object.

So, if I were to have a class with methods that performed certain tasks, each method would need to call object::create so I could get a valid object to work with. So I guess, how would you impliment your 'word' example in a class, that has two methods, OpenDocument and SetSubject.

Regards,

Nik.

PS:- using vole is much much better than using the from typelib in MFC.

I've had another request on this very subject, so I guess I'll have to change it.

What I'd like to do is use Contract Programming to enforce that no methods get called on an empty instance.

However, with VOLE being a dynamic wrapper over a dynamic "language", I think it's likely that I'll have to use exceptions instead. It was the avoidance of such that inclined me to use the strongly immutable RAII model in the first place.

Ah, well. There's no point having software that's perfectly theoretically correct and practically difficult to use.