Using Word as a Spell Checker from Managed C

Using Word as a Spell Checker from Managed C++

Just as you can add the power of other applications to your unmanaged applications, so too you can add it to managed apps. But instead of #import , type libraries, and progids , you have to learn your way around references, interop assemblies, and the GAC.

Primary Interop Assemblies

The temptation is strong to think you know how to get to Word from managed code; just add a reference on the COM tab, it will make you a Runtime Callable Wrapper (RCW) and away you go. But the last thing you want is to create your own RCW. Word comes with a set of Primary Interop Assemblies, or PIAs. A PIA does what you would expect any RCW to do; it looks like a .NET assembly on the outside, but it doesn't have implementation code. Instead it knows where to find that implementation code in COM, and how to marshal and convert back and forth between the managed and unmanaged worlds . What sets a PIA apart from the kind of RCW you could make yourself by just adding a reference? A PIA

  • Is signed by the creator of the PIA

  • Is marked with an attribute that indicates it is a PIA

  • Usually has better performance and marshaling, because it was hand-written and tweaked by a vendor who knows the COM component very well

Make no mistake about it, if you're going to automate Word from managed code, you want to use the PIAs. When you install Office 2003 on a machine that already has the .NET Framework installed (and therefore already has a GAC), you can install the PIAs into the GAC. Look for the .NET Programmability Support options. If Office 2003 is already installed, you can change your installation as follows :

  1. Choose Add/Remove programs from Control Panel.

  2. Find Office 2003 and click Change.

  3. Choose Add/Remove features and click Next .

  4. Make sure the Choose Advanced Customization of Applications check box is selected.

  5. In the tree view that appears, expand Microsoft Word for Windows.

  6. Click the symbol next to .NET Programmability Support and choose Run from My Computer.

  7. It's a good idea to do all the PIAs at once, even if you're only planning a Word project at the moment. Repeat steps 5 and 6 for Microsoft Excel for Windows, and under Office Tools for both Microsoft Forms 2.0 and Microsoft Graph.

  8. Click Update. The PIAs will be installed into the GAC.

Open Windows Explorer and browse to your Windows directory ( C:\Windows or C:\Winnt ) and then to the assembly directory beneath that. This shows you all the assemblies in the GAC. Make sure you see plenty of names that start with Microsoft.Office.Interop .

Creating the Sample Application

Create a managed console application called ManagedWord . Bring up the Add Reference dialog box and switch to the COM tab. Scroll until you find Microsoft Office 11.0 Library. Click Select and then OK. The reference is added, along with several dependent references. However, an RCW is not generated for you. In Solution Explorer, expand the References node and select Microsoft.Office.Interop.Word . Switch to the Properties window and you will see the properties of the reference itself. The Full Path property shows where the assembly is located: You will see a path like this:

 
[View full width]
 
[View full width]
c:\Windows\assembly\Gac\Microsoft.Office.Interop.Word.0.0.0__71e9bce111e9429c\Microsoft graphics/ccc.gif .Office.Interop.Word.dll

This reminds you that the PIA from the GAC was used, rather than a new RCW generated for you by the tlbimp utility.

The code for the main() function performs exactly the same tasks as the unmanaged version you've just seen, but because it is accessing Word functionality through a managed interface, there are a few slight differences at these stages:

  • When creating the application object

  • When passing parameters to the methods

  • During string manipulation

  • When closing Word

Rather than the smart pointers generated by #import , in managed code you create an instance of the coclass, and keep it in a pointer to the interface, like this:

 
 Microsoft::Office::Interop::Word::Application* ap =          new Microsoft::Office::Interop::Word::ApplicationClass(); 

To discover the name of the interface and the coclass, double-click the Microsoft.Office.Interop.Word reference in Solution Explorer. This brings up the Object Browser, which serves much the same purpose as the OLE/COM object viewer. You can select an interface from the tree view on the right, and see the details of the functions it holds on the left. When you select an interface, the bottom pane holds details about the interface itself. For example, if you select Application on the left, under the node for the Microsoft.Office.Interop.Word namespace, you will see the details shown in Figure 9.1.

Figure 9.1. The Object Browser provides all the information you need about the managed interface.

graphics/09fig01.jpg

The CheckSpelling() method is just like the equivalent method in the unmanaged interface, with one important exception: optional parameters. Like many of the methods exposed in the managed interfaces, CheckSpelling() expects parameters to be passed by reference. From C++ that means that you must pass an address of (a pointer to) a managed instance. This syntax is hard to reconcile with optional parameters.

The call from unmanaged code was

 
 bool spellingOK = ap->CheckSpelling(word); 

From managed code, all the parameters must be supplied. You can pass along a special value that means you are not supplying the parameter, but you must pass it by reference, like this:

 
 Object* missing = System::Reflection::Missing::Value; bool spellingOK = ap->CheckSpelling(s,&missing,&missing,                                       &missing,&missing,                                       &missing,&missing,                                       &missing,&missing,                                       &missing,&missing,                                       &missing,&missing); 

This is fairly tedious , but it works. Because the Office libraries are so rich in parameters passed by reference, C++ and C# programmers have to do a little more work to use them than Visual Basic programmers. But don't let that stop you!

In the unmanaged version of the spell checker, the strings were just char* pointers and good old C runtime library functions like strtok were the order of the day. In the managed version, it's a little different, although the concepts are the same. The strings are System::String instances, and the Split() method breaks it up according to delimiters you specify. But instead of calling it repeatedly, as with strtok() , you call it once to create an array. The loop is governed not by the return from strtok() but by an enumerator that goes through the array. You call MoveNext() to point the enumerator at the next element of the array, and use the Current property to access the current element.

Finally, the managed version of Quit() is multiply defined, so it's simpler to just close the active window. Because Word exits when the last document is closed, this will ensure you are not left with extra copies of WINWORD.EXE running. To pass the save options constant, you need to create an instance of Object (so you can pass the address to the method, which expects its parameters by reference) and then box the constant into the object, like this:

 
 Object* donotsave = __box(        Microsoft::Office::Interop::Word::WdSaveOptions::wdDoNotSaveChanges); ap->get_ActiveWindow()->Close(&donotsave,&missing); 

The Entire Managed Application

The managed version of the spelling checker is in Listing 9.2. Compare it to the unmanaged version.

Listing 9.2 ManagedWord.cpp
 // This is the main project file for VC++ application project // generated using an Application Wizard. #include "stdafx.h"  #using <mscorlib.dll> using namespace System; int _tmain() {    Microsoft::Office::Interop::Word::Application* ap =            new Microsoft::Office::Interop::Word::ApplicationClass();    Object* missing = System::Reflection::Missing::Value;    //to get suggestions, there must be a document    if (ap->Documents->Count == 0)       ap->Documents->Add(&missing,&missing,&missing,&missing);    Console::WriteLine("Enter a sentence and press enter:");    String* testSentence;    testSentence = Console::ReadLine();    String* delims = S" \t,() ";    Char delimiter[] = delims->ToCharArray();    String* words[] = 0;    words = testSentence->Split(delimiter);    System::Collections::IEnumerator* nextword = words->GetEnumerator();    while (nextword->MoveNext())    {       String* s = __try_cast<String*>(nextword->Current);       bool spellingOK = ap->CheckSpelling(s,&missing,&missing,&missing,                                              &missing,&missing,&missing,                                              &missing,&missing,&missing,                                              &missing,&missing,&missing);       if (!spellingOK)       {          Console::WriteLine("{0} is not recognized by Word. Word suggests:",s);          Microsoft::Office::Interop::Word::SpellingSuggestions* sugg  =               ap->GetSpellingSuggestions(s,&missing,&missing,&missing,&missing,                                            &missing,&missing,&missing,&missing,                                            &missing,&missing,&missing,&missing,                                            &missing);          int suggcount = sugg->Count;          for (int i = 1; i <= suggcount; i++)          {             Microsoft::Office::Interop::Word::SpellingSuggestion* suggestedword                           = sugg->get_Item(i);             if (suggestedword)             {                Console::WriteLine(suggestedword->Name);             }          }          if (suggcount == 0)             Console::WriteLine("No suggestions.");       }    }    Object* donotsave = __box(        Microsoft::Office::Interop::Word::WdSaveOptions::wdDoNotSaveChanges);    ap->get_ActiveWindow()->Close(&donotsave,&missing);    return 0;} 

The concepts involved in accessing Word are clearly parallel in the managed and unmanaged code; the interface names, method names, and meanings of the parameters are the same. The effort you put into learning your way around a COM interface is immediately applicable to the RCW (or better still, PIA) version of that interface. The skill of learning your way around an unmanaged COM interface using the OLE/COM object viewer is parallel to the skill of learning your way around a managed .NET interface using the Object Browser.



Microsoft Visual C++. NET 2003 Kick Start
Microsoft Visual C++ .NET 2003 Kick Start
ISBN: 0672326000
EAN: 2147483647
Year: 2002
Pages: 141
Authors: Kate Gregory

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net