Isolating Microsoft Office Extensions with the COM Shim Wizard Version 2.3

Summary: Learn about an updated set of project wizards for Microsoft Visual Studio 2005 and Microsoft Visual Studio 2008 that automate the generation of COM shims for managed shared add-ins. (26 printed pages)

Andrew Whitechapel, Microsoft Corporation

Misha Shneerson, Microsoft Corporation

Published: May 2007

Updated: August 2008

Applies to: 2007 Microsoft Office System, Microsoft Office 2003, Microsoft Office XP, Microsoft Office 2000

Download the COMShimWizardSetup.exe sample file.

NoteNote

This tool is produced by members of the Visual Studio Tools for Office (VSTO) team, because we recognize that there are some customers who cannot use VSTO. However, unlike VSTO, this is not a supported tool.

Contents

  • Extensions for the 2007 Microsoft Office System

  • New Extensibility Interfaces

  • Office Fluent UI Extensibility

  • Building a Shim: Containment and Aggregation

  • Custom Task Panes

  • Custom Form Regions

  • Excel Automation Add-ins

  • Add-in Configuration Files

  • InfoPath 2007 and SharePoint Designer 2007

  • How the COM Shim Works

  • Managed Aggregator

  • How the COM Shim Wizard Works

  • Installing the COM Shim Wizard

  • Using the Wizard for Managed Shared Add-ins: A Walkthrough Exercise

  • Original Samples Updated

  • New Samples

  • Windows Vista

  • Differences Between COM Shim Wizard version 2.3 and COM Shim Wizard version 2.3.1

  • Code Differences Between COM Shim Wizard 2.0 and COM Shim Wizard version 2.3

  • Conclusion

  • Additional Resources

Extensions for the 2007 Microsoft Office System

When you build a managed Office extension of any kind, you should ensure that your extension is isolated from other extensions that might be loaded into the application. The standard way to isolate your extension is to build a custom COM shim by using the COM Shim Wizard, a set of Visual Studio project wizards that helps you construct shims quickly and easily. It is recommended that you build a shim for the following types of Office add-in or smart tag:

  • Managed COM add-ins that do not use VSTO.

  • Managed smart tag recognizer and action components.

  • Managed smart document solutions.

  • Managed Excel real-time data components.

  • Managed Excel automation add-ins.

This article describes the latest version of the COM Shim Wizard, which supports the new extensibility interfaces in the 2007 Office system, and adds supports for Microsoft Visual Studio 2008

NoteNote

VSTO solutions do not require custom shims because the VSTO runtime includes default shim functionality.

For descriptions and details about earlier versions, as well as the reasons why you should use COM shims, read the following articles:

The COM Shim Wizard is actually a set of five wizards, one for each type of managed extension assembly for which you can use a shim. You can use the wizards to produce shims for the following types of managed extensions:

  • A managed COM shared add-in assembly that implements IDTExtensibility2

  • A managed real-time data component assembly that implements IRtdServer

  • A managed assembly that implements both ISmartTagRecognizer and ISmartTagAction

  • A managed assembly that implements only ISmartTagRecognizer

  • A managed assembly that implements only ISmartTagAction

NoteNote

The shims for smart tags and real-time data components are the same in this release; only the add-in shims are different. For this reason, this article focuses only on the add-in shims.

New Extensibility Interfaces

The new extensibility interfaces that the 2007 Office system supports are listed in Table 1.

Table 1. New extensibility interfaces

Interface

Applications that support the interface

ICustomTaskPaneConsumer

Access, Excel, InfoPath, Outlook, PowerPoint, Word

IRibbonExtensibility

Access, Excel, Outlook, PowerPoint, Word

FormRegionStartup

Outlook

IBlogExtensibility

Word

IBlogPictureExtensibility

Word

EncryptionProvider

Excel, PowerPoint, Word

SignatureProvider

Excel, PowerPoint, Word

IDocumentInspector

Excel, PowerPoint, Word

NoteNote

It is possible to implement IDocumentInspector in an add-in. Such an add-in is included with the samples that accompany this article, and the COM Shim works with it. However, you can also implement IDocumentInspector in a regular DLL that is not an add-in. Indeed, you gain very little benefit from implementing IDocumentInspector in an add-in as opposed to in a regular DLL. Also, if you do implement IDocumentInspector in an add-in, your DLL is loaded twice—once via the regular add-in load mechanism, and again when the system application instantiates your IDocumentInspector object via COM. Alternatively, one benefit that you do gain is the ability to provide a shim for the DLL.

Office Fluent UI Extensibility

The shims are updated to support the new extensibility interfaces. For most of the new interfaces, the updates are relatively minor. But the programming model for Office Fluent User Interface (UI) Ribbon extensibility is completely different from the other interfaces.

All the other interfaces follow the same model as any COM interface: that is, the interface defines a number of methods, which are the communication points between the Office system application and the add-in. IRibbonExtensibility defines such an interface (as it is a COM interface), but it also assumes additional methods that are not defined in the interface.

IRibbonExtensibility only defines one method, GetCustomUI, but the Office system expects to call back on your IRibbonExtensibility object for any additional methods, such as to respond to user interaction with the Ribbon or to handle any programmatic changes to the state of the controls within the Ribbon.

For example, in your Ribbon XML you specify that the callback method for a button is MyButtonCallbackMethod; but this method is not defined in the IRibbonExtensibility interface. The Ribbon customization model relies on the fact that IRibbonExtensibility is an Automation interface. You are allowed to define any number of additional methods on the end of the interface, and the Office system calls these methods by using IDispatch::GetIDsOfNames and IDispatch::Invoke.

Building a Shim: Containment and Aggregation

You can build a shim in one of two ways: by containment or by aggregation.

With containment, the shim acts like an outer component, and contains pointers to interfaces on the inner component (the add-in or smart tag, for example). The shim reimplements the same interfaces as the add-in, and when the Office system application calls into the interface on the shim, the shim passes the call on to the add-in’s implementation of the same interface. Using containment, the shim can specialize the interface with prolog and epilog code wrapping the call to the add-in. This is how the shim is currently written: it uses containment for the interface on the add-in. This is done to provide additional behavior to the add-in’s implementation of the interface—notably to create and destroy application domains as part of the startup (IDTExtensibility2::OnConnection) and shutdown (IDTExtensibility2::OnDisconnection) sequences.

The containment model holds true for smart tags and RTD components (although with different interfaces, of course). It is possible to extend this model to address all of the new interfaces, except IRibbonExtensibility. The containment model is not practical for IRibbonExtensibility because the Office system relies on the additional methods in the Ribbon object that are not defined in the IRibbonExtensibility interface. It is possible to discover these additional methods via reflection, but such an approach is prone to error. The only safe way to shim an add-in that implements IRibbonExtensibility is to aggregate it. (For more information, see Managed Aggregator.)

Custom Task Panes

Add-ins that directly implement ICustomTaskPaneConsumer expose one or more ActiveX controls that you must register. To use the test add-in for ICustomTaskPaneConsumer, you can either rebuild the add-in, which registers it, or simply register the add-in assembly that contains the managed ActiveX control. Following is an example.

cd C:\Windows\Microsoft.NET\Framework\v2.0.50727
regasm /codebase "<install_locationC:\Data\COMShims\2.3\Sample Managed Assemblies\TestExcel2007Addin\TestExcel2007Addin\bin\Debug\TestExcel2007Addin.dll"

You must also be sure to rebuild or re-register the shim, so that the shim registration overrides the add-in registration. Registering the shim does not interfere with the registration of the ActiveX controls. The shim project includes registration for most of the entries required by the add-in; the one exception is that the shim does not register any managed ActiveX controls that the add-in might implement.

If the add-in implements custom task panes, the ActiveX controls that the task pane uses are also created in the add-in's separate application domain.

NoteNote

Although the shim proxies ICustomTaskPaneConsumer, it does not proxy the ActiveX control directly. It is possible to proxy the ActiveX control, but it is unnecessary, because the main reason for the shim is to ensure application domain isolation. This requirement is met by simply creating a proxy for the add-in's primary assembly. All dependent assemblies (such as the assembly that implements the ActiveX control) are loaded into the add-in's application domain without any further explicit action in the shim.

Custom Form Regions

Microsoft Office Outlook 2007 introduces a form technology called form regions. You can build custom form regions with an add-in. To do this, your add-in must implement the FormRegionStartup interface, and it must supply an Outlook Form Region Storage (OFS) file and an XML manifest file for the custom form region. Additional registration is required so that Outlook can find the manifest. The COM Shim Wizard 2.3.1 identifies whether the target add-in implements the FormRegionStartup interface, and if so, it prompts the user to supply the required registration information for the custom form regions.

Outlook supports two mechanisms for registering the custom form region manifest:

  • Under each Outlook message class that your form region targets, you can register the custom form region name against the full path to the manifest file. This only works if your manifest is in an external file. An example follows: [HKCU\Software\Microsoft\Office\Outlook\FormRegions\IPM.Task] "CustomFormRegion_1"="C:\\Temp\\MyAddin\\bin\\Debug\\MyFormRegion.xml"

  • Under each message class that your form region targets, you can register the ProgId of the add-in. If you choose this approach, then Outlook calls into the add-in's implementation of FormRegionStartup::GetFormRegionManifest, and the add-in can choose where to get the manifest XML file from to return it to Outlook. In the example that follows, notice the additional "=" sign before the ProgId. [HKCU\Software\Microsoft\Office\Outlook\FormRegions\IPM.Task]"CustomFormRegion_1"="=TestOutlook2007Addin.Connect"

  • The COM Shim Wizard generates additional registration entries to support the second approach above. If your add-in uses the first approach, you can use the wizard to generate the entries in the second format, and then modify the generated .rgs file to provide the specific manifest path you require.

Excel Automation Add-ins

In the context of shims, Excel Automation add-ins resemble COM add-ins that implement IRibbonExtensibility. Specifically, the Excel Automation add-in exposes a number of custom methods that are used within the host application. Therefore, because the add-in shim works with the Automation methods on COM add-ins that implement custom Ribbon UIs, it also works for Excel Automation add-ins, without any additional changes.

Notice that the shim and the COM Shim Wizard can only handle COM add-ins that implement IDTExtensibility2. An Excel Automation add-in is not required to implement IDTExtensibility2, but it is useful to do so, because it provides easy access to the Excel object model.

It is common to build an Excel Automation add-in that implements IDTExtensibility2, but it is unlikely that your Automation add-in needs to implement any other 2007 Office system extensibility interfaces. For example, you can build an Automation add-in that also implements IRibbonExtensibility, but this is not likely to be very useful. The Office system queries for IRibbonExtensibility only when it loads an add-in as a regular COM add-in. It does not query for IRibbonExtensibility—or for any other extensibility interfaces—when it loads the add-in as an Automation add-in. So although you can build this kind of composite add-in, and the COM Shim Wizard allows it, you are not encouraged to do so. If you are building a shim for an Automation add-in, the COM Shim Wizard will generate the additional "Programmable" registry entry in the target .rgs file for you.

Add-in Configuration Files

Microsoft .NET Framework–based application configuration files work on a per-application domain basis. Therefore, each application domain can potentially have its own configuration file. The primary purpose of the shim is to load its target add-in into a separate application domain. To help customize your add-in, you can create and deploy an application configuration file. To do this, create an XML file using the add-in assembly name as the base name, and append ".config" to the base name. For example, the sample add-ins that implement custom task panes all use a config file. The following example shows the .config file for the TestExcel2007Addin.dll. The .config file is named TestExcel2007Addin.dll.config.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="taskPaneTitle" value="Contoso" />
</appSettings>
</configuration>

The add-in checks its configuration file to find the string to use for the custom task pane title.

The shim code now supports DLL .config files by setting the AppDomainSetup.ConfigurationFile property on the application domain it creates for the add-in.

InfoPath 2007 and SharePoint Designer 2007

The COM Shim Wizard 2.3.1 adds support for Microsoft Office InfoPath 2007 and Microsoft Office SharePoint Designer 2007 add-ins. Office InfoPath now has support for COM add-ins in the 2007 release and Office SharePoint Designer is the evolution of Microsoft Office FrontPage (FrontPage is still supported).This release also updates the TestAddin so that it works correctly with InfoPath. Notice, however, that unlike other Office system applications, when InfoPath calls into IDTExtensibility2::OnStartupComplete, its startup is not necessarily complete. Specifically, if InfoPath is set to show the Getting Started dialog box, then any attempt to modify the command bars at this point will fail. The TestAddin modifies the command bars, so to avoid the exception, do the following:

  1. Start InfoPath.

  2. On the Tools menu, click Options, click General, and then select Do not automatically show the Getting Started dialog box.

This release also includes a simpler TestInfoPathAddin that does not attempt to modify the command bars.

How the COM Shim Works

The primary shim is a Microsoft Visual C++ Active Template Library (ATL) COM DLL. It exposes a COM-creatable class that acts as a proxy to the actual managed extension class. The COM Shim Wizard registers the CLSID and ProgId for this class in the registry. The wizard also registers this class against all target Office system applications that you choose. When the host application starts, it checks the registry to see which add-ins to load, and then uses standard COM object creation to instantiate the registered proxy class. This action loads the COM shim DLL that is registered to proxy one of the add-ins.

When the COM shim is loaded and the proxy class is created, the proxy class in turn creates an instance of the CLR loader class. The CLR loader object loads the Microsoft .NET Framework common language runtime (CLR). It then creates an application domain and loads the managed extension assembly into the new application domain. It creates an instance of the managed aggregator component in this application domain. The managed aggregator component aggregates the unmanaged shim with the add-in, with the unmanaged shim as the outer object and the add-in as the inner object.

Managed Aggregator

When an outer component aggregates an interface of an inner component, it does not reimplement the interface—it merely passes the inner component's interface pointer directly to the caller. With this model, the outer component cannot specialize the behavior of the inner component. The most important aspect of aggregation is that it hands out an interface pointer to the inner object back out to the caller. You must carefully construct the aggregation so that the caller always sees a consistent set of interfaces in the aggregation, regardless of which interface pointer it is using at any given time.

Figure 1 shows a logical view of how the new shim works with both the managed and unmanaged pieces.

Figure 1. Logical view of the managed and unmanaged shim aggregation

Logical view of shim aggregation

In the case of a new shim, the Marshal.CreateAggregatedObject method in the .NET base class library aggregates an inner managed object with an outer unmanaged object. This method is a static method on a sealed abstract base class. For this reason, the new version of the (unmanaged) shim first creates an instance of a managed object: the ManagedAggregator. This managed object performs the aggregation, and sets the shim as the outer object and the add-in as the inner object in the aggregation,.

A detailed representation of the components is shown in Figure 2.

Figure 2. Loading the managed and unmanaged shim aggregation

Loading the shim aggregation

After the add-in loads, all method calls from the host application to the add-in go through the shim proxy. The calls to IDTExtensibility2 methods are wrapped by the proxy to allow the proxy to perform additional processing during some of these calls. The calls to all other extensibility interfaces implemented by the add-in go through only the proxy—these calls are not wrapped by the proxy. This behavior is shown in Figure 3.

Figure 3. Run-time behavior of the add-in shim

Run-time behavior of the add-in shim

How the COM Shim Wizard Works

The operation of the COM Shim Wizard encourages you to use COM shims for your managed extensions. The aim of the wizard is to enable you to produce Visual C++ ATL COM shims without having to work directly with C++ code.

The COM Shim Wizard resembles most of the standard Microsoft Visual Studio 2005 or Microsoft Visual Studio 2008 wizards. It uses a combination of text files, script, and template code files. The installation also deploys a pair of managed assemblies: the COMShimHarvester and the AppDomainHarvester. When you create a shim project, the wizard uses these two assemblies to ask you a few simple questions and then to analyze the assembly via reflection to harvest metadata. This metadata includes the fully qualified name of the class that implements the IDTExtensibility2 interface, the public key token that forms part of the strong name of the assembly (if present), and any relevant GUIDs and ProgIds. In this release, the COMShimHarvester also determines whether the add-in implements any of the new extensibility interfaces. The wizard uses these details to complete the template source files for the shim, including the registry information.

Installing the COM Shim Wizard

To install the COM Shim Wizard, use the download link at the beginning of this article, and follow the instructions on the download page. For the wizard to install correctly, you must ensure the following:

  • Uninstall any previous versions of the COM Shim Wizard before installing the new version.
NoteNote

To uninstall the COM Shim Wizard, use Add or Remove Programs in Control Panel.

  • Be sure that Visual Studio is not running when you install the wizard. The default installation location for the wizard is %ProgramFiles%\Microsoft\COM Shim Wizards COM Shim Wizard version 2.3.1\.
NoteNote

Two of the wizard components, the AppDomainHarvester.dll file and the ComShimHarvester.dll file, are also installed in the global assembly cache, so you must have administrator rights on your computer to install the wizard. Installing the COM Shim Wizard does not change or remove any existing Visual Studio files, but it adds new ones. Visual Studio aggregates information from all the files in the Visual Studio installation folder and subfolders.

Using the Wizard for Managed Shared Add-ins: A Walkthrough Exercise

In this exercise, you create an example shim for a managed shared add-in. This is the most commonly used type of managed Office system extension, and it requires the most input from the developer to establish the precise registry requirements. Before you start the exercise, remember to install the COM Shim Wizard.

To create a shim for a managed shared add-in

  1. Start Visual Studio 2005 or Visual Studio 2008. On the File menu, point to New, and then click Project. In the New Project dialog box, expand the Visual C++ Projects node, and select the child node, COM Shims, as shown in Figure 4.

    Figure 4. Selecting the Addin Shim project in Visual Studio

    The AddinShim1 project in Visual Studio

  2. Select Addin Shim, and specify a name and location. For this exercise, you can accept the default name and location. Click OK. The wizard runs the COMShimHarvester component, which displays the Specify the Managed Add-in Assembly page of the COM Shim Wizard.

  3. If you know the full path to the managed extension assembly for which you want to create a shim, you can type it into the first text box. Alternatively, click Browse and navigate to the assembly. For this exercise, you can use one of the sample add-ins provided with the download. The two most complex of these add-ins are the TestWord2007Addin and the TestOutlook2007Addin. TestWord2007Addin is an add-in that targets Word 2007; it implements the maximum number of new extensibility interfaces. In fact, this add-in implements all of the new extensibility interfaces except FormRegionStartup (which can only be used in Outlook). Remember that this release of the COM Shim Wizard includes support for the new extensibility interfaces. The TestOutlook2007Addin add-in targets Outlook 2007, and implements FormRegionStartup. If you choose the TestOutlook2007Addin add-in, you see how the wizard offers the ability to generate registration code for your custom form regions.

  4. Select the assembly, TestWord2007Addin.dll or TestOutlook2007Addin.dll, and click Open. The COMShimHarvester opens the assembly (by way of the AppDomainHarvester helper assembly), harvests information, and populates the text boxes in this page of the wizard.

  5. The COMShimHarvester component of the wizard harvests the name and public key token of the assembly and the name of the class that implements IDTExtensibility2. By default, it also uses the original GUID and ProgId values, as shown in Figure 5.

    Figure 5. Harvested assembly information

    Harvested assembly information

    NoteNote

    If you do not want to use the same GUID and ProgId values for your shim as for your add-in, clear the check box for this option. The COMShimHarvester then generates new values. If you do not use the same IDs, both the managed extension assembly and the shim DLL are registered. In this situation, your tests pick up one or the other, depending on the GUID and ProgId you use in your test harnesses. This is usually not desired behavior.

  6. Click Next to open the Secondary Extensibility Interfaces page of the wizard, as shown in Figure 6. On this page, the wizard reports any of the new extensibility interfaces that the add-in implements. If this add-in implements IDocumentInspector, the wizard also offers you a text box in which you can type the string you want to register for this add-in. You can also specify whether or not this add-in uses a DLL .config file. The generated shim code always supports configuration files, so you do not need to select this option to get the support. This option simply allows the wizard to include the configuration file in the project post-build tasks.

    NoteNote

    If you select this option, your config file must be in the same folder as the target add-in when you run the wizard, because the wizard will set up the project to copy the file from that folder as a post-build task.

    Figure 6. Secondary extensibility interfaces

    Secondary extensibility interfaces

  7. The Secondary Extensibility Interfaces page of the wizard is configured dynamically, according to which interfaces the add-in implements. If you run the wizard on the TestOutlook2007Addin, you see how the wizard handles implementations of the FormRegionStartup interface. You can specify the registry names of your custom form regions and the message class that they map to. This variation of the Secondary Extensibility Interfaces page is shown in Figure 7.

    Figure 7. Secondary extensibility interfaces

    Secondary extensibility interfaces

  8. To specify the custom form region registry values, double-click in the Custom Form Regions panel—this adds a new message class node. Then, double-click the message class node to add a sub-node for each custom form region. Double-clicking outside a node creates a new message class node. To edit the text of a node, select that node, and then click it. An example, which matches the custom form regions implemented in the sample TestOutlook2007Addin, is shown in Figure 8.

    Figure 8. Specifying the Custom Form Regions registry information

    Custom Form Regions registry information

  9. If the wizard determines that the add-in is usable in Excel, then the Secondary Extensibility Interfaces page offers you an option to specify whether this is an Excel Automation add-in, as shown in Figure 9.

    Figure 9. Specifying that this add-in is an Excel Automation add-in

    An Excel Automation add-in

  10. Click Next to open the Add-in Details page of the wizard, in which you can specify additional information to enter into the registry (Figure 10). This information includes a suitable description and friendly name, the load behavior of the add-in, and the list of target host applications. If the wizard can determine which Office system application the add-in can reside in, it selects the corresponding check box. For example, if the add-in implements FormRegionStartup, the wizard selects the Outlook check box; if it implements IBlogExtensibility, the wizard selects the Microsoft Word checkbox; if it is an Automation add-in, the wizard selects the Microsoft Excel check box, and so on.

    Figure 10. Specifying standard registry information for the add-in

    Standard registry information for the add-in

  11. By default, the wizard sets the add-in to load when the Office system application starts. This effectively sets the registry LoadBehavior to 3. You can change this if you want a different setting. Also by default, the wizard uses the fully qualified type name of the class that implements the target interface as the basis for both the description and friendly name. You can change these, as appropriate, for your project.

  12. Click Next to display the Summary page of the wizard (Figure 11).

    Figure 11. The wizard summary page for a new add-in shim

    The wizard summary page

  13. Click Finish to generate the new shim project and code. In the Visual Studio Solution Explorer, there are two projects in the solution: an unmanaged C++ ATL shim project and a managed shim project called ManagedAggregator, as shown in Figure 12.

    Figure 12. Source files for the add-in shim project

    Source files for the add-in shim project

Your next step is to build the shim project. The generated project includes a final build task that runs Regsvr32.exe on the target DLL to register the shim, so you do not need to register it manually. The final build task copies the ManagedAggregator.dll into the target folder for the shim. It also copies the managed add-in assembly that you specified into the target folder for the shim, along with the configuration file for the add-in. The project dependencies are set so that the ManagedAggregator is built first, before the shim. If you add a shim project to an existing add-in solution, you should adjust the build dependencies, as appropriate.

NoteNote

If you build a setup project for your add-in, you should add the primary output of both the shim and the ManagedAggregator projects to your setup. You should also change the value of the Register property for all three project outputs. Set this to vsdrpCOM for the shim DLL, and vsdrpDoNotRegister for both the original add-in DLL and the ManagedAggregator DLL.

To validate that the solution is working as expected with the shim in place, run your full test schedule.

Original Samples Updated

The download provided with this article installs both the COM Shim Wizard and a set of sample managed extension assemblies for testing purposes. The default installation path is %ProgramFiles%\Microsoft\COM Shim Wizards COM Shim Wizard version 2.3.1\Sample Managed Assemblies. Table 2 describes these updated samples.

Table 2. Updated samples

Sample

Description

TestAddin

Add-in that places a custom button at the end of the Tools menu. When the user clicks the button, the add-in displays a message.

TestRealTimeData

A simple real-time data feed that produces sample stock data. An example of how this is used is given in the TestRealTimeData.xls workbook.

TestSmartTagAction

A smart tag assembly that contains only action functionality. An example of how this is used is given in the TestSmartTagData.xls workbook.

TestSmartTagRecognizer

A smart tag assembly that contains only recognizer functionality. An example of how this is used is given in the TestSmartTagData.xls workbook.

TestSmartTagRecognizerAction

A smart tag assembly that contains both recognizer and action functionality. An example of how this is used is given in the TestSmartTagData.xls workbook.

The simplest sample add-in (TestAddin) adds a custom button to the Tools command bar. In Office 2007 applications that support the Office Fluent UI in the main window, this is translated into a button on the Add-ins tab in the Ribbon UI. All other behavior is the same. As before, the shim assumes the managed extension is in the same folder, so you need to copy it across before testing.

Most of the sample managed extensions depend on the Office system primary interop assembly (PIA), and because they were originally built for Office 2003, they have references to the Office 2003 core PIAs. To test the sample add-ins on an upgraded computer that is running the 2007 Office system, and that has both the Office 2003 PIAs and the 2007 system PIAs, everything should just work. To test the sample add-ins on a computer that has only the 2007 Office system PIAs, you have two choices:

  • Deploy the Office 2003 PIAs also, which might require you to repair the 2007 system PIAs (because the PIA redistributable not only deploys the PIAs but also registers them, and you should ensure that the last version to be registered is the latest version).

  • Rebuild your add-in to target the 2007 Office system PIAs instead.

New Samples

The COMShimWizardSetup installer also adds new sample add-ins in the Sample Managed Assemblies folder of the install location. Table 3 lists the sample add-ins.

Table 3. New sample add-ins

Sample

Description

TestInfoPathAddin

A simple test add-in for InfoPath that works regardless of how you set the Do not automatically show the Getting Started dialog box option in InfoPath.

TestExcel2007Addin

Implements the new extensibility interfaces, IRibbonExtensibility and ICustomTaskPaneConsumer. This add-in also has a configuration file. The add-in offers a custom Ribbon button, which toggles the visibility of a custom task pane. The custom task pane has one control, a label that reports the name of the application domain that the add-in is running in. This is an easy way to confirm that the add-in is running via the shim, because the application domain name is the path to the shim folder. If the add-in is running without the shim, then the application domain name is DefaultDomain.

TestOutlook2007Addin

Implements the new extensibility interfaces IRibbonExtensibility, ICustomTaskPaneConsumer, and FormRegionStartup. When you shim an add-in that implements custom form regions, you must also register the custom form region itself. This add-in has the same custom Ribbon and custom task pane behavior as the TestExcel2007Addin. In addition, it includes three custom form regions, mapped against two different Outlook message classes. The wizard prompts the user for the names of the message classes and the corresponding custom form regions, and then generates code in the shim .rgs file that registers these details.

TestEncryptionAddin

Tests the EncryptionProvider interface. For add-ins that implement EncryptionProvider, the add-in also requires custom registration in addition to the standard add-in registration keys. Specifically, the OpenXMLEncryption string value needs to be set to the CLSID (or ProgId) of the component that implements EncryptionProvider.

[HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Common\Security]

"OpenXMLEncryption"="1F6E6CC6-FC04-4F60-A673-7DFD1715C1C4"

(where “1F6E6CC6-FC04-4F60-A673-7DFD1715C1C4” is the CLSID of the sample TestEncryptionAddin.) The shim wizard generates code in the .rgs file in the shim to perform this registration. This simple test add-in only has a very minimal implementation of EncryptionProvider, just enough to confirm that the host application can call through the add-in to the EncryptionProvider methods. To test this add-in, run Word 2007. On the File menu, click Prepare, and then click Encrypt Document. The add-in responds by displaying a simple message box.

TestSignatureAddin

Tests the SignatureProvider interface, a minimal custom signature provider. To test this add-in, run Word 2007. On the Insert tab, select the custom entry TestSignatureAddin.Connect (the add-in's registered friendly name) from the Signature Line list. The add-in then places a custom signature block in the document.

TestBlogAddin

A minimal custom blog add-in. It has just enough functionality to confirm that the add-in gets loaded and called through the IBlogExtensibility and IBlogPictureExtensibility interfaces. To test this, run Word 2007. On the File menu, point to New, and then click Blog Post. Click Manage Accounts. In the Blog Accounts dialog box, you see a row for the Simple Blog FriendlyName, which is registered by this add-in.

TestDocumentInspector

A Word add-in that tests the IDocumentInspector interface. To test this add-in, run Word 2007, and open the TestDoc.docx file provided with the sample. This contains a single line of text where the words are spelled using U.K. English spelling.

TestWord2007Addin

A Word add-in that implements the maximum number of new extensibility interfaces in one add-in. This add-in implements seven of the eight new interfaces. It does not implement FormRegionStartup, because this interface is only supported in Outlook. This add-in combines the functionality of TestExcel2007Addin, TestBlogAddin, TestEncryptionAddin, TestSignatureAddin, and TestDocumentInspectorAddin.

TestExcelAutomationAddin

An Excel Automation add-in that exposes several user defined functions (UDFs). One of these UDFs reports the current application domain and can be used to provide easy confirmation that the add-in is running via the shim. The sample also includes a test workbook, Test.xlsx. To test this add-in, run Excel 2007. On the File menu, click Excel Options. In the Excel Options dialog box, select Add-ins. From the Manage list, select Excel Add-ins, and then click Go. This opens the Add-ins dialog box (not the COM Add-ins dialog box). In this dialog box, click Automation to open the Automation Servers dialog box. Select the entry for the TestExcelAutomationAddin.UdfClass from the displayed list.

Windows Vista

In Windows Vista, Visual Studio 2005 is required to run in elevated mode, that is "run as administrator." This is a standard requirement, not specific to COM add-ins nor to the shim, because building a shim project requires writing to the registry, including potentially to HKEY_LOCAL_MACHINE (HKLM), and non-administrative users do not have permissions to write to the HKLM subkey.

In addition, to successfully install the COM Shim Wizard on Windows Vista, you need to run the install process in elevated mode. This is because the COM Shim Wizard includes two components (COMShimHarvester and the AppDomainHarvester) that are installed to the global assembly cache, and a user who does not have administrator permissions cannot install to the global assembly cache.

The COM Shim Wizard also registers the COMShimHarvester.AssemblyDialog as a COM component. This component must be registered to the HKLM subkey because when Vista runs in elevated mode, it only looks in HKLM for COM registration keys. Therefore, to successfully install the COM Shim Wizard on Windows Vista, use the following steps.

To install the COM Shim Wizard on Windows Vista

  1. To run the entire install process as elevated, click Start, right-click the Command Prompt, and then select Run as administrator.

  2. Run the COMShimWizardSetup.msi from the elevated command prompt.

  3. Select the Install for all users option, which registers the wizards to the HKLM subkey, where Vista can find the entries.

Differences Between COM Shim Wizard version 2.3 and COM Shim Wizard version 2.3.1

The major difference between COM Shim Wizard version 2.3 and COM Shim Wizard version 2.3.1 is that COM Shim Wizard version 2.3 was built to install with Visual Studio 2005, whereas COM Shim Wizard version 2.3.1 installs with either Visual Studio 2005 or Visual Studio 2008, or both. The setup for the COM Shim Wizard now gives you the option to choose which version or versions of Visual Studio to install for. There are 3 additional enhancements to the setup for the COM Shim Wizard version 2.3.1:

  • The setup disables installation on a per-user basis. This is because installing per-user (and therefore registering to HKCU) causes problems on Vista with UAC turned on, or when running Visual Studio in an elevated process.

  • The custom actions are set to NoImpersonate, so that they work correctly on Vista with UAC on.

  • The setup is formatted so that any error messages are displayed (otherwise they are not visible in Vista, the user just sees Error 2869).

CLR Start Bug

This fix was documented as a community comment added to the MSDN documentation for the COM Shim Wizards v2.3.0.0. Part of what the shim does is to load the CLR: it does this by calling CorBindToRuntimeEx. We test the return from CorBindToRuntimeEx, and if it is S_FALSE, this indicates that the CLR has already been loaded. In the released version of the shim, if we get S_FALSE, we simply return.

However, there is a scenario where the CLR could already have been loaded but not started. It's an unusual scenario: it could happen if some other managed code was loaded into the process but never executed. The problem is that the shim code assumes that if the CLR is loaded it is not necessary for us to start it. Of course, if the only other things in the process that load the CLR are all shims created with the COM Shim Wizard, then this assumption would be true – because the first one to load would have loaded the CLR and started it. However, this assumption doesn't hold if there are other native components that load the CLR and don't start it.

The change to fix the bug is to simply remove the test for S_FALSE – this works because we do call Start later on. The fixed code is shown below, with the S_FALSE test shown as a comment (the test is actually removed altogether in the code generated by the COM Shim Wizard v2.3.1):

HRESULT CCLRLoader::LoadCLR()
{
    HRESULT hr = S_OK;
 
    // Ensure the CLR is only loaded once.
    if (m_pCorRuntimeHost != NULL)
    {
        return hr;
    }
 
    // Load the CLR into the process, using the default (latest) version, 
    // the default ("wks") flavor, and default (single) domain.
    hr = CorBindToRuntimeEx(
        0, 0, 0, 
        CLSID_CorRuntimeHost, IID_ICorRuntimeHost, 
        (LPVOID*)&m_pCorRuntimeHost);
 
    // If CorBindToRuntimeEx returned S_FALSE, the CLR has already 
    // been loaded.
    //if (hr == S_FALSE)
    //{
    //    return hr;
    //}
 
    // If CorBindToRuntimeEx returned a failure HRESULT, we failed to load 
    // the CLR.
    if (!SUCCEEDED(hr)) 
    {
        return hr;
    }
 
    // Start the CLR.
    return m_pCorRuntimeHost->Start();
}

Note that this test for S_FALSE occurs in all variants of the shim code – that is, the shim for add-ins, smart tags and real-time data components. The fix removes it from all these variants. In each case, this is in the file ClrLoader.cpp – these are installed to the %ProgramFiles% or %ProgramFiles(x86)% folder when you install the wizard, for example:

%ProgramFiles(x86)%\Microsoft Visual Studio 8\VC\VCWizards\COMShims\AddinShim\Templates\1033\clrloader.cpp

%ProgramFiles(x86)%\Microsoft Visual Studio 8\VC\VCWizards\COMShims\RealTimeDataShim\Templates\1033\clrloader.cpp

%ProgramFiles(x86)%\Microsoft Visual Studio 8\VC\VCWizards\COMShims\SmartTagActionShim\Templates\1033\clrloader.cpp

%ProgramFiles(x86)%\Microsoft Visual Studio 8\VC\VCWizards\COMShims\SmartTagRecognizerActionShim\Templates\1033\clrloader.cpp

%ProgramFiles(x86)%\Microsoft Visual Studio 8\VC\VCWizards\COMShims\SmartTagRecognizerShim\Templates\1033\clrloader.cpp

Post-build step to copy ManagedAggregator.dll

In COM Shim Wizard version 2.3, a post-build step in the generated shim project copies the ManagedAggregator.dll from the \bin\debug folder. This has been changed in COM Shim Wizard version 2.3.1 to copy the dll from the target folder for each project configuration. For example, for a debug configuration, it will copy from \bin\debug; but for a release configuration, it will copy from \bin\release.

Code Differences Between COM Shim Wizard 2.0 and COM Shim Wizard version 2.3

The COM Shim Wizard version 2.3.1 includes all the changes in version 2.3. Between version 2.0 and version 2.3, the code was refactored and cleaned up, with the focus on the add-in shim. The shim code for smart tags and real-time data components was not updated because the behavior of these components was not changed. Code clean-up included correctly splitting function declarations in header files from definitions in cpp files, using symbol names that conform to standards, minimizing the use of smart pointers, and using safe string methods. The following sections describe the more significant changes for specific classes in the add-in shim.

Interop.h

This defines two interfaces, IComAggregator and IManagedAggregator. The unmanaged shim implements IComAggregator in the ConnectProxy class. The managed aggregator implements IManagedAggregator. This allows the ManagedAggregator to aggregate the ConnectProxy (shim) class as the outer object, with the add-in as the inner object. The IComAggregator interface defines one function, SetInnerPointer, which is used to cache the inner object pointer within the outer object, and thereby allow the client application to call through the outer object to interfaces in the inner object.

ConnectProxy.h/cpp

In COM Shim Wizard version 2.3, the ShimConfig and ConnectProxy classes are merged into one class.

The ConnectProxy COM map specifies that the shim itself implements IDTExtensibility2 and IComAggregator, and that all other interfaces are implemented on the inner object (the add-in). This is done via "blind" aggregation. That is, the shim does not need to specify exactly which interfaces are supported; it simply passes all queries to the add-in. This allows the shim to support an open-ended number of interfaces, not just the eight new ones defined in the 2007 Office system, but also arbitrary interfaces implemented by Excel Automation add-ins, and any new interfaces defined in the future.

The OnDisconnection, OnBeginShutdown, and FinalRelease implementations are rationalized. OnDisconnection is called if the user disconnects the add-in via the COM add-in's dialog box or programmatically. The shim wraps this so that it can clean up the reference it holds to the inner object. The shim must also allow for the possibility that the user has disconnected the add-in via the COM add-in's dialog box or programmatically. In this scenario, OnDisconnection is called first, and this add-in never gets the OnBeginShutdown call (because it is already disconnected). In OnBeginShutdown, the shim must test that the add-in pointer is not null, to allow for the case where the add-in was previously disconnected before the application was closed. In FinalRelease, the shim ensures it unloads the application domain, and cleans up its references. FinalRelease is the last thing called in the shim or add-in, after OnBeginShutdown and OnDisconnection. The isShutdownInProgress guard in OnDisconnection is removed because it is redundant: the AppDomain.Unload call is now made in the FinalRelease on the shim, not in OnDisconnection.

The string variables for the target add-in's assembly and class name are now in ConnectProxy, and the redundant wrapper methods are removed. A third string variable is introduced for the add-in's configuration file.

The previous version of the shim loaded the CLR, created a new application domain, and then instantiated the add-in in that application domain. In COM Shim Wizard version 2.3, there is an additional step after loading the CLR, to instantiate ManagedAggregator. ManagedAggregator, in turn, actually loads and instantiates the add-in object, so that it can correctly aggregate this with the shim.

CLRLoader.h/cpp

In previous versions, CLRLoader is a singleton class. However, because only the ConnectProxy uses this class, and it only instantiates the CLRLoader object once, the singleton enforcement is unnecessary. In COM Shim Wizard version 2.3, CLRLoader is no longer a singleton class.

In previous versions, the CreateLocalAppDomain method does not have parameters. In COM Shim Wizard version 2.3, this method is now CreateAppDomain and it takes a string parameter. This parameter is the name of the (optional) DLL configuration file for the add-in.

The CLRLoader class has a new method, CreateAggregatedAddin, which loads the common language runtime, calls CreateAppDomain to create a new application domain, instantiates ManagedAggregator, and aggregates an instance of the target managed add-in in that application domain.

The CreateAppDomain method creates an application domain, as in previous versions, but it also sets the application domain to use a local DLL configuration file if there is one. The shim discovers the configuration file at run time, which means that you have the option to choose whether or not to deploy a configuration file either before or after building the add-in and the shim.

ConnectProxy.rgs

This file now accommodates additional registry entries, to support the new extensibility interfaces. These entries are all parameterized. For example, if the COM Shim Wizard discovers that the target add-in implements FormRegionStartup, it adds entries in the ConnectProxy.rgs for all specified custom form regions that need to be registered. Not all of the new extensibility interfaces require additional registry entries, but the wizard generates entries in the .rgs file for all that do:

  • IBlogExtensibility

  • IBlogPictureExtensibility

  • EncryptionProvider

  • FormRegionStartup

  • IDocumentInspector

Note that the entries for IDocumentInspector are always in the HKLM subkey, whereas all the other entries are in (HKEY_CURRENT_USER) HKCU by default.

Post-build Steps

The wizard now generates up to four post-build steps:

  • Register the shim, using regsvr32.exe.

  • Copy the target add-in assembly into the shim target folder.

  • Copy the target add-in assembly's configuration file, if found, into the shim target folder.

  • Copy the ManagedAggregator.dll into the shim target folder.

Conclusion

If you are not using Visual Studio Tools for Office to build managed 2007 Microsoft Office system extensions, you should use a custom COM shim. There are two reasons why you should use a COM shim: security and isolation. You can digitally sign your COM shim and, by doing so, take full advantage of Office system security features for your managed extension. If you do not use a COM shim, the Office system loads your extension DLL into the default application domain along with all other extensions. All DLLs in the same application domain are vulnerable to potential damage caused by any other DLL in the same application domain.

If you want to write managed add-ins, smart tags, or real-time data components for the 2007 Office system, you should use a COM shim. Visual Studio Tools for Office solutions and later smart tag solutions use the COM shim that is built into the Visual Studio Tools for Office loader. For all other solutions, you can use the COM Shim Wizard to generate a COM shim automatically for your managed extension.

Additional Resources

For more information, see the following resources.