Microsoft .NET Development for Microsoft Office

 

This article is an excerpt from Microsoft .NET Development for Microsoft Office, from Microsoft Press (ISBN 0-7356-2132-2, copyright Microsoft Press 2005, all rights reserved).

The author, Andrew Whitechapel, spent several years on the .NET Enterprise Applications Development team at Microsoft Consulting Services in the United Kingdom. He has extensive experience developing Microsoft Officebased applications with Microsoft .NET managed code. Andrew is now a technical program manager on the Microsoft Visual Studio Tools for Office team.

No part of this chapter may be reproduced, stored in a retrieval system, or transmitted in any form or by any meanselectronic, electrostatic, mechanical, photocopying, recording, or otherwisewithout the prior written permission of the publisher, except in the case of brief quotations embodied in critical articles or reviews.

Download sample code.

Chapter 2: Basics of Office Interoperability (Part 1 of 3)

Contents

2.1 Managed Interop Assemblies
    Office XP PIAs
    Office 2003 PIAs
2.2 Basic Interop Projects
    Basic Interop with Excel
    Interop with Office 97 and Office 2000
    Basic Interop with Word
    Basic Interop with Outlook
    Basic Interop with PowerPoint

In this chapter, we'll introduce all the basic concepts of interoperation between Microsoft .NET managed code and Microsoft Office applications. We'll also explore some practical examples that demonstrate how to develop managed applications using Office. The basic mechanics demonstrated in this chapter will form the basis of all later techniques presented in this book.

Office applications are COM servers, so the basis of the interoperation between Office applications and .NET code is COM Interop. The .NET Framework has good support for interaction with COM components, COM+ services, and native operating system services. Code executing under the control of the common language runtime (the CLR, or just "the runtime") is called managed code, while code that runs outside the CLR is called unmanaged code. All COM components and Win32 API functions are unmanaged code. As we'll see later, this doesn't prevent you from exposing managed .NET code in a way that is visible to, and consumable by, COM clients.

During development, you write your managed code against interop assembly (IA). This is a managed .NET equivalent of the COM type library. There are several ways of getting an interop assembly, and there are some deployment issues that we need to consider. After you build your code, at run time the CLR places a proxy between your managed code and the Office component you're talking to. We'll see how this proxy behaves and what it does for us. In this chapter, we'll also consider some issues that arise with memory and object lifetime management when managed code interoperates with Office. Finally, we'll make a complete side-by-side comparison of Microsoft Visual Basic .NET and Microsoft C#, to examine their respective advantages and disadvantages for Office development.

At run time, when you make a call from managed code to an (unmanaged) Office function, the CLR performs a number of operations for you. One of the most important is marshaling. Marshaling takes the parameter values you've passed in your managed method call and hands them off to the underlying unmanaged method. Some data types have a common representation in both managed and unmanaged memory and therefore don't need any special handling by the interop marshaler: these types are called "blittable" types, and they include the integral types. All other types are nonblittable, including char, string, array, struct, class, and so forth. Therefore, as part of its marshaling functionality, the CLR packages up the parameter values and translates them in a way that makes them usable at the other end. The marshaler has a degree of built-in intelligence so it can make sensible decisions about how to translate from a managed data type to an unmanaged data type, and vice versa.

To support the framework's marshaling, you can use various tools that ship with Microsoft Visual Studio .NET to generate wrapper classes to make both managed and unmanaged clients think they are calling objects within their respective environment. The situation is summarized in Figure 2-1. During development, you get an IA that is a managed equivalent to a COM type library. Indeed, the IA is generated based on the type library of the COM server (in this context, an Office application). Then you write your custom .NET code against this IA. In this way, you can develop against the type information in the IA—that is, the enumerations, classes, and interfaces, along with their methods, properties, and events.

Using IAs during development

Figure 2-1. Using IAs during development.

Visual Studio .NET ships with a number of tools—which you can use from the command line or from within the IDE—for interoperating CLR and COM objects. They are described in Table 2-1.

Table 2-1. Visual Studio .NET Interop Tools

Tool Description
AxImp Creates a .NET proxy for an ActiveX control so the control can be used in a .NET Windows Forms application
TlbImp Reads a COM type library to generate an IA that .NET clients can use with early binding
TlbExp Exports a .NET component's type information into a type library file that COM components can use with early binding
RegAsm Enters a .NET component into the system registry so it can be accessed by COM Services, which invokes it indirectly using the CLR execution engine

Many of the techniques described in this document involve the use of IAs for the Office applications. For Office 97 and Office 2000, you can autogenerate these IAs using TlbImp. Office XP includes pregenerated (and optimized) IAs that you should use instead of autogenerating IAs. An IA distributed by the owner of the original COM server is called a primary interop assembly (PIA). You should always use the specific version of the IA that matches the version of Office you're targeting.

In either case, the general pattern is to right-click the project in Solution Explorer and select Add Reference. Click the COM tab. Then select the appropriate type library—for example, for Excel you would select the following:

Office 97: Microsoft Excel 8.0 Object Library

Office 2000: Microsoft Excel 9.0 Object Library

Office XP: Microsoft Excel 10.0 Object Library

Office 2003: Microsoft Excel 11.0 Object Library

For Office 97 and Office 2000, this triggers TlbImp to generate a new IA based on the selected type library. For later versions (including Office XP and Office 2003), this simply adds a reference to the existing Microsoft-supplied PIA. You should also find that references have been added for the ancillary Office core and Visual Basic extensibility PIAs. Note that the minimum requirement is Office 97 with Service Release 1 (SR-1). The general behavior of the tools listed (as they are used from within Visual Studio .NET) is described in Table 2-2.

Table 2-2. Using Interop Tools from Visual Studio .NET

Operation Behavior
Add Reference to COM component If the PIA is registered, adds a reference to the PIA. Otherwise, runs TlbImp to generate a new IA.
Add Reference to ActiveX control Runs AxImp.
Drag and drop an ActiveX control from the Tools palette Runs AxImp.
Project Properties | Configuration
Properties | Register for COM Interop
Runs Regasm /tlb /codebase.

At run time, whenever a managed client calls a method on a COM object, it uses a run-time callable wrapper (RCW). In the reverse situation, when a COM client calls into a .NET component, it uses a COM callable wrapper (CCW). This is summarized in Figure 2-2. The functionality provided by the CLR interop layer and the RCW includes translating between managed types (e.g., string) and their unmanaged equivalents (e.g., char[]). Similarly, return values are translated from their unmanaged type to managed equivalents.

Runtime callable wrappers

Figure 2-2. Runtime callable wrappers.

Section 2.1 looks at how to use Office IAs, how to launch Office applications, how to attach to running instances of Office applications, and basic use of the native object model of an Office application from managed code. In this way, we'll cover all the basic mechanics of interoperating between managed code and Office applications. Later chapters will build on these basic mechanics.

2.1 Managed Interop Assemblies

**Note   **This section applies to Office 97, Office 2000, Office XP and Office 2003.

While anyone can create an IA for a COM server, only one IA is designated as the PIA for that server. The PIA contains the official description of the unmanaged types as defined by the publisher of those unmanaged types. The PIA usually also contains certain customizations that make the types easier to use from managed code. The PIA is always digitally signed by the publisher of the original unmanaged component.

Office XP PIAs

For Office XP, Microsoft has created several PIAs that contain the official description of commonly used Office XP type libraries for products such as Microsoft Access, Excel, Outlook, Word, and PowerPoint. These PIAs are known as the Office XP PIAs. Microsoft has customized these PIAs to make it easier for managed code to interoperate with the Office XP COM type libraries. Any Office XP COM IA that is not provided as part of the Office XP PIAs, or any Office XP COM IA that is generated by Visual Studio .NET at design time, should be considered unofficial, and you should avoid using it. The Office XP PIAs cover not only the primary type library for each application but also the dependent DLLs such as ADO, DAO, and MScomctl.

Note that for earlier versions of Office, you can use Visual Studio .NET (which uses TlbImp) to generate IAs from the type library. From that point on, using the Office application's object model will be very similar regardless of whether you're using the Office XP PIA or a TlbImp-generated IA. TlbImp is a tool that ships with Visual Studio .NET that imports a COM type library into a proxy .NET assembly that .NET clients can use with early binding.

You can download the Office XP PIAs from the Microsoft Downloads Web site, put them into the global assembly cache (GAC), and register them in the registry. This last step is to ensure that when you develop a .NET application to use Office and add a reference to the Office product of interest, the updated registry entry ensures that the PIA is picked up instead of generating a new IA.

When it comes to deploying a Visual Studio .NET solution that relies on the Office XP PIAs, you can deploy the relevant PIAs in the same folder as your application. But a better approach is to install the PIAs into the GAC on the target machine, using a Visual Studio .NET Setup project.

Office 2003 PIAs

The Office 2003 PIAs are deployed with Office 2003 and should be installed when Office is installed. (Note that the PIAs are not installed during a Typical install, although they are set to install on first use if the .NET Framework is already present.) The best approach is to use a Custom install of Office and select the .NET Programmability Support option for each Office application you want to develop against. Bear in mind that it is somewhat difficult to predict which applications you might want to develop against in the future. Also, there are some ancillary components, such as the Microsoft Forms 2.0 components, that you might not realize you need. For these reasons, it is simpler to select all the .NET Programmability Support options.

If you want to install the Office 2003 PIAs, .NET Framework 1.1 is required. If you don't have .NET Framework 1.1 installed when you install Office 2003, the PIAs will not be installed. Additionally, the option to install the PIAs doesn't show up in the Custom setup for Office. If you do have .NET Framework 1.1 installed, you'll see ".NET Programmability Support" in the list of options when you install Office.

If you install .NET Framework 1.1 only after you've installed Office 2003, to get the PIAs after that, you must rerun the Office setup. On the first page, be sure to select the Choose Advanced Customization Of Applications option.

On the second page, expand all the nodes in the tree to make sure you have identified all the .NET Programmability Support nodes. For instance, the Microsoft Graph PIA is a subnode of Microsoft Graph, which is itself a subnode of Office Tools. Figure 2-3 shows the very first PIA in the tree, the Access PIA.

Microsoft Office 2003 Setup dialog box showing Access PIA

Figure 2-3. Setup dialog box showing Access PIA

Note that if you are doing development work, you shouldn't do a Typical install of Office 2003 if you want the PIAs. Because of a known bug in Visual Studio .NET and Visual Studio 2003, if PIAs are set to install on demand, as they are in a Typical install, when you reference an Office COM type library you might end up getting a Visual Studio–generated IA. To avoid this issue, do a Complete or Custom install to make sure the Office 2003 PIAs that you need to reference are already installed in the GAC before you start any development work.

Also note that PIA registrations get clobbered a lot. Sometimes a simple action such as installing some arbitrary independent software can clobber a PIA registration. If you know you have the Office 2003 PIAs in the GAC but somehow you keep getting an IA and not the PIA, some type library registration might have broken that specific PIA registration. To fix this, you can do any of the following:

  • Do an Office repair: Control Panel || Add/Remove Programs || Office || Change | Repair.

  • Reinstall the affected PIA. To do this, rerun Office setup.

  • RegAsm that PIA from the GAC to reregister that PIA. For example, to RegAsm the Office PIA (substituting, say, C:\windows or C:\winnt for %systemroot%):

    %systemroot%\Microsoft.NET\Framework\v1.1.4322\RegAsm.exe 
    %systemroot%\assembly\GAC\Office\11.0.0.0__71e9bce111e9429c\Office.dll
    
  • Finally, note that when you deploy a solution that uses the Office 2003 PIAs, you should not deploy the PIAs themselves. Office setup should be the only mechanism for installing the Office 2003 PIAs.

2.2 Basic Interop Projects

Note   This section applies to Office 97, Office 2000, Office XP and Office 2003.

In this section, we'll explore the use of IAs in a series of practical exercises. In each exercise, we'll create a simple managed Windows Forms application that will interoperate with a specific Office application. First we'll target Excel and demonstrate the basic mechanics of interoperating between Excel and managed code. We'll also look at the details that vary from one version of Excel to another. Then we'll work through similar projects that target Word, Outlook, and PowerPoint.

Throughout these exercises (and in all later exercises), you can use whatever name you like for your project, but if you use the suggested names your projects will correspond with the sample solutions supplied. Of course, it would be a good idea to put your own solutions in a different subdirectory from the sample code, to avoid accidentally overwriting the samples.

Basic Interop with Excel

In this first exercise, we'll create a managed Windows Forms application with a button on the form. When the user clicks the button, we'll launch Excel, add a new workbook to the collection of workbooks, get the ActiveSheet, and then put some dummy text into cell A1. Then we'll see how to quit Excel and make sure that we clean up all the Excel objects. Finally, we'll see where we need to modify our code to accommodate all versions of Excel.

**Note   **The sample solution for this topic can be found in the sample code at <install location>\Code\Office<n>\TestExcelPIA.

  1. In Visual Studio .NET, create a new Windows application called TestExcelPIA. Put a Button control on the form, and set its Text property to Run Excel. Add a handler for the Click event on this button.

  2. In Solution Explorer, right-click and select Add Reference. Click the COM tab. For Office XP, select the Microsoft Excel 10.0 Object Library from the list. In the list this will appear as something like C:\Program Files\Microsoft Office\Office 10\Excel.exe, but once you've added the reference, if you look at its properties, you'll see that the path is set to something like this:

    C:\WINDOWS\assembly\GAC\Microsoft.Office.Interop.Excel\10.0.4504.0__
    31bf3856ad364e35\Microsoft.Office.Interop.Excel.dll

    This is the Excel PIA, not the Excel executable. Note that this will be a full path to the location of the PIA in the GAC—the exact path will depend on the version of Office you are developing against. You should also see that references have been added for the Office core and Visual Basic extensibility PIAs.

    The procedure for Office 2003 is the same, except that you select the Microsoft Excel 11.0 Object Library from the Add Reference COM list. (For earlier versions of Office, see the following note.)

    **Note   **When you select the referenced IA in Solution Explorer, if the Copy Local property in the Properties pane is set to True, you are referencing an autogenerated IA, not the PIA. You should remove this reference and add the correct one. If you correctly reference the Excel 2003 PIA, your Excel reference's Copy Local property will be False and the Path property, on a Windows XP machine, should look something like this:

    C:\WINDOWS\assembly\GAC\Microsoft.Office.Interop.Excel\11.0.0.0__
    71e9bce111e9429c\Microsoft.Office.Interop.Excel.dll

  3. Once you have successfully set a reference to a specific Office IA, you can use it just like any other COM IA. At this point, it's worth running up the Object Browser, where you can see the IA equivalents for the Excel type library (Figure 2-4).

    Visual Studio Object Browser showing Excel type library

    Figure 2-4. Visual Studio Object Browser showing Excel type library

  4. Add a using statement for the Microsoft.Office.Interop.Excel namespace. If you examine the Object Browser carefully, you'll realize that this will introduce an ambiguity: there's an Application interface in Excel and also an Application class in System.Windows.Forms. To fix this, you have a couple of choices. First, you can fully qualify the reference to Application in Main:

    System.Windows.Forms.Application.Run(new TestExcelPIAForm());
    

    Alternatively, you can reference the Microsoft.Office.Interop.Excel namespace through an alias, like so:

    using Excel = Microsoft.Office.Interop.Excel;
    

    Using this second approach, you would qualify each type in the Microsoft.Office.Interop.Excel namespace with this alias. For example:

    Excel.Application xl = new Excel.Application();
    

    Whichever approach you use for disambiguating the types, it's obviously a good idea to stick to that approach throughout.

  5. In the button Click handler, we'll instantiate the Excel Application (not the ApplicationClass—see the Interface/Class Ambiguity section later) to launch Excel and make it visible:

    Microsoft.Office.Interop.Excel.Application xl =  
            new Microsoft.Office.Interop.Excel.Application(); 
        xl.Visible = true;
    
  6. Add a workbook to the collection of workbooks, and then get the active worksheet:

    Workbook book = xl.Workbooks.Add(XlSheetType.xlWorksheet); 
    Worksheet sheet = (Worksheet)book.ActiveSheet;
    
  7. Specify the cell Range A1 (that is, one cell) and set its value to some arbitrary text:

    Range r = (Range)sheet.Cells[1,"A"]; 
    r.Value2 = "Hello World";
    
  8. Build and test. (Note: Because we set Excel to be visible, it is assumed to be under user control, so Excel doesn't shut down when our application does.)

  9. It is quite likely that we want to automate Excel, not make it visible, and terminate it. If this is the required behavior, we should take responsibility for terminating Excel in our code. So add a second button to the form, labeled Quit Excel. Set its Enabled property to false. (We don't want the user trying to quit Excel before starting it.)

  10. Move the declaration of the Excel Application object—make it a private field in the form class instead of a local variable in the button handler. We need to access this across two methods.

  11. In the COM world, you have to be careful to manage the lifetime of COM objects and release them as soon as you're done with them. In the managed world, the CLR garbage collector cleans up memory for us, and the COM interop layer makes sure that any COM objects we use are eventually released. Sometimes, the nondeterministic release behavior of the CLR garbage collector can be problematic in COM interop situations. This is discussed further in a later section.

    In the Quit Excel button handler, we'll explicitly quit Excel and set the reference to the Application object to null. Note that the memory associated with the reference (that is, the proxy to the Excel Application object, and therefore the interface pointer on the object itself) will be released according to the normal garbage collection mechanism. Therefore, the Excel process will remain in memory for some indeterminate time after we quit Excel. To clean up immediately, we can force a garbage collection. This is a somewhat heavyweight operation, and you don't want to do this often, but is useful if you want to make sure you're cleaning up completely. You should certainly consider doing this as a matter of course at key termination points, such as when a form or other significant managed object is being destroyed, and at the end of your application.

    if (xl != null) 
        { 
            xl.Quit(); 
            xl = null; 
            GC.Collect(); 
            GC.WaitForPendingFinalizers(); 
            GC.Collect(); 
            GC.WaitForPendingFinalizers(); 
    }
    

    Note that we're repeating the calls to Collect and WaitForPendingFinalizers. This is because the memory for the Excel reference might have survived the first pass, although it will then have been marked for collection on the next pass. So we will make a second pass to clean up anything that survived the first pass but was otherwise available for collection.

  12. Build and test.

When you quit Excel, you'll get the usual prompt about saving your changes, and this message box might well be hidden behind the main Excel window. If you want to avoid this message box, you have several choices:

  • Save the workbook before you quit.
  • Turn off Excel's standard alerts.
  • Set the workbook's Saved status to true.

We'll look at saving workbooks in later sections because it introduces other issues. So, if you want to turn off Excel's standard alerts, just add this line of code—the sensible place to do this is just after you've instantiated the Excel Application object:

    xl.DisplayAlerts = false;

Alternatively, to set the workbook's Saved status to true, add this line of code instead (after making the code that actually changes the workbook's data):

book.Saved = true;

When you've done that, build and test the solution. See Figure 2-5.

Test Excel PIA Hello World sample

Figure 2-5. Test Excel PIA Hello World sample

Interop with Office 97 and Office 2000

For Office 97 and Office 2000, don't install the Office XP/2003 PIAs. If you need to uninstall them, following the instructions in <Office XP PIA, install folder>\ReadMe.htm. To save time, you can use the sample unregisterPIAs.bat file, which removes all the Office XP PIAs from the GAC. You must then remove the registry entries corresponding to the reg files in <Office XP PIA install folder>. The registry entries in these files will redirect most COM uses of Office to the Office XP PIAs. If necessary, reinstall Office 97 to restore the correct registry entries. Pay close attention to the gacutil output—if a GAC assembly is currently in use, it won't be deleted from the GAC.

Reinstalling Office 97 or Office 2000 will restore the Office 97 or Office 2000 registry entries, respectively, but it won't remove the additional entries added by the Office XP or Office 2003 PIAs. You particularly need to make sure that the additional typelib entries are removed because marshaling the COM interfaces is done on the basis of the application's type library. Registering the Office XP PIAs adds a typelib entry to the registry, as shown here (for Excel):

[HKEY_CLASSES_ROOT\TypeLib\{00020813-0000-0000-C000-000000000046}\1.4]
"PrimaryInteropAssemblyName"="Microsoft.Office.Interop.Excel, 
Version=10.0.4504.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

So, using RegEdit, you can delete this 1.4 (Office XP) subkey or 1.5 (Office 2003) subkey, leaving the earlier 1.0 and 1.2 entries.

Warning   Editing the registry incorrectly can cause serious problems that might require you to reinstall Windows. There is no guarantee that problems resulting from the incorrect use of RegEdit (including the use of .reg files) can be solved. Use RegEdit at your own risk.

The Office XP and Office 2003 PIA registry entries include HKCR\CLSID InprocServer32 entries for the objects themselves. You can remove these or leave them in if you want, because the original LocalServer32 entries will be used instead.

In the project, don't reference the Office XP or Office 2003 PIAs. Instead, generate a new IA from the application's typelib—for example, from Excel8.olb in Office 97 or Excel9.olb in Office 2000. This will be listed on the COM tab of the Add References dialog box. When you add it, the ancillary interop assemblies (Office and VBIDE) will also be generated. The namespaces for these interop assemblies will be different from those in the PIAs. For example:

//using Microsoft.Office.Interop.Excel;    // Office XP version 
using Excel;                    // Office 97/2000 version

Note that because the interop assembly for Excel that is generated by Visual Studio has Excel as its root namespace, you can't use the name Excel as an alias. You could use anything else, of course. Alternatively, and to avoid the ambiguity between the Excel.Application and the System.Windows.Forms.Application mentioned above, you can fully qualify the reference to the Application in Main:

    System.Windows.Forms.Application.Run(new TestExcelPIAForm());

To launch the Excel application, you use the following changes:

    // Microsoft.Office.Interop.Excel.Application xl = 
    //     new Microsoft.Office.Interop.Excel.Application(); 
    Excel.Application xl  = new Excel.Application();

Subsequently, the bulk of the code will be the same for all versions of the application.

Note   Instead of generating the IAs each time, you can generate them once, put them somewhere suitable, and thereafter reference them from that location.

Now that we've seen how to perform basic operations with Excel, we'll demonstrate how to perform equivalent functionality with Word, Outlook, and PowerPoint. You'll see that the basic interop mechanics are the same for all Office applications, even though the fine details differ according to each application's functionality. By the same token, you'll see that the basic mechanics are nearly identical across all versions of any particular application, with only minor changes to any of the project settings or code.

Basic Interop with Word

In this exercise, we'll create a simple Windows Forms application to launch Word, add a new document, and insert some dummy text.

Note   The sample solution for this topic can be found in the sample code at <install location>\Code\Office<n>\TestWordPIA.

  1. In Visual Studio .NET, create a new Windows application called TestWordPIA. Put a Button control on the form, and set its Text property to Run Word. Add a handler for the Click event on this button. Add a second Button control, set its Text property to Quit Word, and add a Click handler.

  2. In Solution Explorer, right-click and select Add Reference. Click the COM tab. For Office 2003, select the Microsoft Word 11.0 Object Library from the list. This will appear in the list as something like C:\Program Files\Microsoft Office\Office11\MSWord.olb, but once you've added the reference, if you look at its properties, you'll see that the path is set to something like this:

    C:\WINDOWS\assembly\GAC\Microsoft.Office.Interop.Word
    \11.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.Word.dll
    

    This is the Word PIA, not the Word executable. Note that this will be a full path to the location of the PIA in the GAC—the exact path will depend on the version of Office you are developing against. You should also see that references have been added for the Office core and Visual Basic extensibility PIAs. All three of these should have their Copy Local property set to False by default.

  3. Add a using statement for the Microsoft.Office.Interop.Word namespace. This introduces an ambiguity between the Application interface in Word and the Application class in System.Windows.Forms. To fix this, you can reference the Microsoft.Office.Interop.Word namespace through an alias:

    using Word = Microsoft.Office.Interop.Word;
    
  4. Qualify each type in the Microsoft.Office.Interop.Word namespace with this alias. For example, our first declaration should be for a Word.Application interface reference. Declare this as a class field member:

    private Word.Application word;
    
  5. In the button Click handler for the Run Word button, set up a try block, instantiate the Word Application object to launch Word, and make it visible. Also, get hold of the Documents collection:

        private void btnRunWord_Click(object sender, System.EventArgs e) 
        { 
            try 
            { 
                word = new Word.Application(); 
                word.Visible = true; 
                Word.Documents docs = (Word.Documents)word.Documents;
    
  6. Add a Document to the collection of Documents. The Documents.Add method takes four parameters. All are optional; in this example, we only want to pass specific values for the last two. We can use the System.Type.Missing value for the first two. The third parameter specifies the type of document to add—in this case, a document (as opposed to a Web page or e-mail message). For the last parameter, we'll pass true to indicate that we want the document to be made visible:

                object templateName = Type.Missing; 
                object openAsNewTemplate = Type.Missing; 
                object openVisible = true; 
                object documentType = Word.WdDocumentType.wdTypeDocument; 
                Word.Document doc = docs.Add( 
                    ref templateName, ref openAsNewTemplate,  
                    ref documentType, ref openVisible);
    
  7. Next we need to get a Range object to insert some text. We'll use starting and ending positions of zero because we know there's no text in our new document yet. Call the Range.InsertAfter method, and then close off the try/catch block:

                object startPosition = 0; 
                object endPosition = 0; 
                Word.Range r = (Word.Range)doc.Range( 
                    ref startPosition, ref endPosition); 
                r.InsertAfter("Hello TestWordPIAForm"); 
            } 
            catch (Exception ex) 
            { 
                Debug.WriteLine(ex.Message); 
            } 
    }
    
  8. In the Click handler for the Quit Word button, first make a call to Word's Application.Quit method. Then be sure to clean up. In this simple example, the managed references we have used to access the Documents collection, and the Document and Range objects, will all go out of scope at the end of the Click handler and become available for collection, thereby releasing the underlying Word objects. However, the Word.Application reference was scoped to the form class and won't go out of scope until the form itself is destroyed. Because we are quitting Word in our Quit Word method, we can also clean up this reference at this point:

        private void btnQuitWord_Click(object sender, System.EventArgs e) 
        { 
            object missing = Type.Missing; 
            word.Quit(ref missing, ref missing, ref missing); 
            word = null; 
            GC.Collect(); 
            GC.WaitForPendingFinalizers(); 
            GC.Collect(); 
            GC.WaitForPendingFinalizers(); 
        }
    
  9. Build and test. The run-time behavior should look like Figure 2-6.

    Test Word PIA code sample result

    Figure 2-6. Test Word PIA code sample result

Version Notes   For each version of Word, the specific version of the PIA or IA should be referenced. The code as described above will work for both Word 2003 and Word XP. For Word 2000, the only change is to remove the aliased namespace using statement. For Word 97, remove the aliased namespace using statement and remove the last two parameters to the call to the Documents.Add method.

Basic Interop with Outlook

In this exercise, we'll create a simple Windows Forms application to launch Outlook and add a new AppointmentItem, including some dummy text and arbitrary properties. Consistency between the Office applications is good, but in terms of programmability they are by no means identical. Outlook probably differs most from the other Office applications. Outlook's object model is significantly different in many respects. This has a lot to do with the way Outlook integrates with Microsoft Exchange Server as well as with Outlook's wide-ranging functionality—support for e-mail, the address book, and calendaring are all significantly different in functionality. As a trivial example, we've seen that both Excel and Word offer an Application object, and that this object exposes a Visible property. Outlook does expose an Application object, but making Outlook visible is a somewhat more complicated proposition, as we will see.

**Note   **The sample solution for this topic can be found in the sample code at <install location>\Code\Office<n>\TestOutlookPIA.

In Visual Studio .NET, create a new Windows application called TestOutlookPIA. Put two Button controls on the form, labeled Run Outlook and Quit Outlook. Add handlers for the Click events on these buttons.

In Solution Explorer, right-click and select Add Reference. Click the COM tab. For Office 2003, select the Microsoft Outlook 11.0 Object Library from the list. In the list this will appear as something like C:\Program Files\Microsoft Office\Office11\
MSOutl.olb, but once you've added the reference, if you look at its properties, you'll see that the path is set to something like this:

C:\WINDOWS\assembly\GAC\Microsoft.Office.Interop.Outlook\
11.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.Outlook.dll

At the top of your code, add an aliased using statement for the Outlook namespace:

using Outlook = Microsoft.Office.Interop.Outlook;

Declare a reference to the Outlook.Application object as a field in your form class:

    private Outlook.Application outlook;

In the Run Outlook button Click handler, set up a try block and launch Outlook through the Application reference. Then get hold of the MAPI Namespace object—we need this to make the calendar visible:

    try 
    { 
        outlook = new Outlook.Application(); 
        Outlook.NameSpace mapiNamespace = outlook.GetNamespace("MAPI"); 
        Outlook.MAPIFolder mapiFolder = mapiNamespace.GetDefaultFolder( 
            Outlook.OlDefaultFolders.olFolderCalendar); 
        mapiFolder.Display();

Use Outlook's Application.CreateItem method to create a new appointment and set some arbitrary properties. Save the appointment and finish off the try/catch block:

        Outlook.AppointmentItem appointment =  
            (Outlook.AppointmentItem)outlook.CreateItem( 
            Outlook.OlItemType.olAppointmentItem); 
        appointment.Start = DateTime.Now.AddMinutes(1); 
        appointment.Duration = 60; 
        appointment.Subject = "Coffee"; 
        appointment.Body = "Go to cafe to get more coffee"; 
        appointment.Location = "London"; 
        appointment.Save(); 
    } 
    catch (Exception ex) 
    { 
        Debug.WriteLine(ex.Message); 
    }

Code the Quit Outlook handler to both quit Outlook and clean up the class-scoped Application reference:

        outlook.Quit(); 
        outlook = null; 
        GC.Collect(); 
        GC.WaitForPendingFinalizers(); 
        GC.Collect(); 
        GC.WaitForPendingFinalizers();

Build and test. The run-time behavior should look like Figure 2-7.

Test Outlook PIA code sample result

Figure 2-7. Test Outlook PIA code sample result

**Version Notes   **For each version of Outlook, the specific version of the PIA or IA should be referenced. The code as described above will work for both Outlook 2003 and Outlook XP. For Outlook 2000, the only change is to remove the aliased namespace using statement. For Outlook 97, remove the aliased namespace using statement and change the statement that calls CreateItem to use the older OlItems enumeration in place of the OlItemType enumeration:

        Outlook.AppointmentItem appointment =  
            (Outlook.AppointmentItem)outlook.CreateItem( 
        //    Outlook.OlItemType.olAppointmentItem); 
            Outlook.OlItems.olAppointmentItem);

Basic Interop with PowerPoint

In this exercise, we'll create a simple Windows Forms application to launch PowerPoint, add a new slide, and insert some dummy text. The PowerPoint object model is much closer in behavior to Excel and Word than Outlook's is. For example, PowerPoint exposes an Application object with a Visible property. PowerPoint also exposes a Presentations collection, and each Presentation has a Slides collection. This is similar to Excel, which offers a Workbooks collection where each Workbook has a Worksheets collection. In much the same way, Word has a Documents collection.

**Note   **The sample solution for this topic can be found in the sample code at <install location>\Code\Office<n>\TestPowerPointPIA.

  1. In Visual Studio .NET, create a new Windows application called TestPowerPointPIA. Put two Button controls on the form, labeled Run PowerPoint and Quit PowerPoint. Add handlers for the Click events on these buttons.

  2. In Solution Explorer, right-click and select Add Reference. Click the COM tab. For Office 2003, select the Microsoft PowerPoint 11.0 Object Library from the list. In the list this will appear as something like C:\Program Files\Microsoft Office\Office11\MSPpt.olb, but once you've added the reference, if you look at its properties, you'll see that the path is set to something like this:

    C:\WINDOWS\assembly\GAC\Microsoft.Office.Interop.PowerPoint\
    11.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.PowerPoint.dll
    
  3. At the top of your code, add an aliased using statement for the PowerPoint and core Office namespaces:

    using PowerPoint = Microsoft.Office.Interop.PowerPoint; 
    using Office = Microsoft.Office.Core;
    
  4. Declare a reference to the PowerPoint.Application object as a field in your form class:

        private PowerPoint.Application powerPoint;
    
  5. In the Run PowerPoint button Click handler, set up a try block, launch PowerPoint through the Application reference, and make it visible. Then get hold of the Presentations collection and add a new Presentation:

        try 
        { 
            powerPoint = new PowerPoint.Application(); 
            powerPoint.Visible = Office.MsoTriState.msoTrue; 
            PowerPoint.Presentations ppts = powerPoint.Presentations; 
            PowerPoint.Presentation ppt = ppts.Add( 
                Office.MsoTriState.msoTrue);
    
  6. Get hold of the Slides collection, and add a new Slide:

            PowerPoint.Slides slides = ppt.Slides; 
            PowerPoint.Slide slide = slides.Add( 
                1, PowerPoint.PpSlideLayout.ppLayoutBlank);
    
  7. Get hold of the Shapes collection, and add a text box shape. Put some dummy text into this text box. Finally, finish off the try/catch block.

            PowerPoint.Shapes shapes = slide.Shapes; 
            PowerPoint.Shape shape =  
                shapes.AddTextbox( 
                    Office.MsoTextOrientation.msoTextOrientationHorizontal, 
                    0, 0, 500, 50); 
            shape.TextFrame.TextRange.InsertAfter( 
                "Hello TestPowerPointPIAForm"); 
        } 
        catch (Exception ex) 
        { 
            Debug.WriteLine(ex.Message); 
        }        
    
  8. Code the Quit PowerPoint handler to both quit PowerPoint and clean up the class-scoped Application reference:

            powerPoint.Quit(); 
            powerPoint = null; 
            GC.Collect(); 
            GC.WaitForPendingFinalizers(); 
            GC.Collect(); 
            GC.WaitForPendingFinalizers();
    
  9. Build and test. See Figure 2-8.

    Test PowerPoint PIA code sample result

    Figure 2-8. Test PowerPoint PIA code sample result

Version Notes   For each version of PowerPoint, the specific version of the PIA or IA should be referenced. The code as described above will work for both PowerPoint 2003 and PowerPoint XP. For PowerPoint 2000 and PowerPoint 97, the only change is to remove the aliased namespace using statements.

From this section, it should now be clear that, while each Office application offers different functionality, there is some consistency across all the Office object models. More important, the basic mechanics for developing managed code against Office applications are essentially the same. There is also little variation across different versions of any one Office application. This means that throughout the rest of this book we can focus on the use of each technique without worrying unduly about the specific Office application or application version that we use to demonstrate the technique.

Read Chapter 2, Part 2

Read Chapter 2, Part 3