Isolating Office Extensions with the COM Shim Wizard

 

Andrew Whitechapel
Misha Shneerson
Siew Moi Khor
Microsoft Corporation

September 2004

Applies to:
    Microsoft Office 2000
    Microsoft Office XP
    Microsoft Office 2003

Summary: Explains how to isolate Microsoft Office extensions from each other and why you should do so. Presents a set of new Microsoft Visual Studio .NET wizards that automate the generation of COM shims for managed Office extensions. (35 printed pages).

Download the OfficeToolComShimWizards.exe file containing the COM Shim Wizard.

Download the OfficeToolComShimWizards.exe containing the COM Shim Wizard. (406 KB)

Contents

Introduction
Types of Managed Extensions
Security Features of Office and .NET Framework
Application Domain Isolation
Developing with COM Shims
How the COM Shim Works
How the COM Shim Wizard Works
Installing the COM Shim Wizard
Using the Wizard for Managed COM Add-ins: a Walkthrough Exercise
Using the Wizard for Managed Smart Tags: a Walkthrough Exercise
Registering the Extension Assembly and Shim
Additional Features and Known Issues
Using the Sample Managed Office Extensions
Manually Editing the Shim Files
Conclusion
Further Reading

Introduction

This document explains why it is important to isolate managed Microsoft Office extensions from each other and how you can do so by using COM shims. First, we discuss the reasons why isolation is important. Next, we discuss how and why you should use a shim and how shims work. We then talk about how the new COM Shim Wizard works and how to install and work with it in Microsoft Visual Studio .NET. We present two walkthrough exercises to show you how to use the wizard to create two types of shims: one for managed COM add-ins, and one for managed smart tag solutions. Finally, we discuss how to manually edit the wizard-generated shim code, should you ever need to do so.

There are many ways to extend Office, using either unmanaged code (written in a language such as Microsoft Visual Basic 6 or Microsoft C++) or managed code (written in a language such as Visual Basic .NET or Microsoft C#). The Office applications themselves are currently written in unmanaged code. They offer a security model originally designed for macros and unmanaged add-ins and templates. An advantage of using managed code to extend Office applications is that you can also use Microsoft .NET Framework security features. The issues described in this document arise because of the differences between managed and unmanaged code, and the differences between traditional macro security and .NET Framework security. The discussion in this document applies to managed extensions for Office and Visual Studio and not to unmanaged extensions.

If you want to write managed extensions for Office, you are strongly encouraged to use a COM shim. For example, any Microsoft Visual Studio Tools for Office solutions you build automatically use the built-in COM shim provided by the Visual Studio Tools for Office loader. With Microsoft Office 2003, you can load managed smart tag solutions with the same Visual Studio Tools for Office loader and thereby take advantage of the same built-in COM shim. Managed smart document solutions also use the Visual Studio Tools for Office loader.

There are two particular reasons why you should use a COM shim—either the built-in COM shim provided by the Visual Studio Tools for Office loader or a custom COM shim—for your managed Office extensions:

  • Security. If you set the Office macro security level to High (or Very High in Office 2003), as is recommended, then Office examines the DLLs it loads for digital signatures. When you deploy a managed extension, the DLL that Office examines for signatures is always Mscoree.dll. This is the .NET common language runtime (CLR) engine, which in turn loads your custom managed extension DLL. The problem is that you cannot digitally sign Mscoree.dll. Therefore, you need to interpose a COM shim DLL, which you can sign, as the first DLL that Office examines for signatures.
  • Isolation. If you do not use a standard COM shim (such as the Visual Studio Tools for Office loader) or provide your own custom COM shim, your extension DLL loads into the default application domain along with all other un-shimmed extensions. All DLLs running in the same application domain are vulnerable to potential damage caused by any other DLL in the same application domain. Also, any un-shimmed add-in that crashes in a host application disables all other un-shimmed add-ins for that application.

Types of Managed Extensions

As mentioned previously, there are two basic reasons why you should use a shim for managed Office extensions: security and isolation. To understand why these are important, we need to provide some background.

First, here is an explanation of the term, "managed Office extension". There are many ways to extend Office, and in this article, we focus on three specific technologies for creating managed Office extensions. These three technologies are designed for different purposes but share two common features: by default, they all use the default application domain in the Office host application, and they are all subject to Office macro security. These extension technologies are:

  • COM add-ins
  • Smart tags
  • Real-time data components

A number of techniques are available for creating managed Office extensions. For information on these techniques, see the listings in Further Reading. This article does not address those techniques. To provide a basis for the discussions in this article, however, the three different types of extensions are briefly described below.

COM Add-ins

Microsoft Office 2000 and later supports a design architecture for building application add-ins to enhance and control Office applications. These add-ins are called COM add-ins. A COM add-in is an in-process COM server that implements the IDTExensibility2 interface.

You can create COM add-ins in unmanaged languages such as Visual Basic 6 and C++. You can also create COM add-ins in managed languages such as Visual Basic .NET and C#. Any managed assembly can implement COM interfaces and can then be registered for use by COM-aware clients. Unmanaged COM DLLs are typically registered with the Regsvr32.exe tool. Managed COM DLLs are registered with the Regasm.exe tool.

Smart Tags

A solution built on Microsoft smart tag technology allows you to develop code that recognizes text within a document or workbook and to perform custom actions based on that recognition. You can extend the capabilities of Microsoft Office XP and later versions by developing your own smart tag recognizer/action DLL for use in Office documents.

Note   In Office XP, only Microsoft Excel 2002 and Microsoft Word 2002 support smart tags. In Office 2003, Access, PowerPoint, and Outlook also support smart tags.

A smart tag DLL implements two special interfaces:

  • ISmartTagRecognizer. Recognizes text that is typed into a document as a smart tag.
  • ISmartTagAction. Performs actions on a particular smart tag string at the user's request.

You can implement both interfaces in the same DLL, or you can have a recognizer DLL and one or more action DLLs that extend a single smart tag type for different actions. As with COM add-ins, you can develop smart tags in either unmanaged or managed code.

Note that Office 2003 extends smart tags in several ways. Of relevance here is the fact that Office 2003 smart tags can be loaded by the Visual Studio Tools for Office loader. To have your managed smart tag assembly loaded by the Visual Studio Tools for Office loader, you add an additional registry key to the registry entry for the smart tag assembly. For further information, see Office 2003: Smart Tag Software Development Kit (SDK).

The Visual Studio Tools for Office loader is an unmanaged DLL that acts as a shim to load the .NET CLR and then your custom managed assembly. A custom COM shim has exactly the same behavior. If you use the Visual Studio Tools for Office loader, you can set Office macro security to High (or Very High in Office 2003), because a managed smart tag loaded by the Visual Studio Tools for Office loader is not subject to Office macro security checking at all.

Real-Time Data Components

Excel 2002 introduced a new worksheet function called RTD that supports the retrieval of real-time data. To use the RTD function to bring data into a spreadsheet, you must have a real-time data server to supply the data. A real-time data server is not a server machine; rather, it is an automation server (DLL or executable) installed on the same machine that is running Excel. The real-time data server receives requests for data from Excel, passes the requests on to some real-time data source, retrieves data from the source, and passes the data back to Excel. The server must implement the IRtdServer interface.

As with COM add-ins and smart tags, you can develop real-time data components in either unmanaged or managed code.

Security Features of Office and .NET Framework

As mentioned previously, the issues described in this document arise because of the differences between managed and unmanaged code, and the differences between traditional macro security and .NET Framework security. The following sections discuss how to work with some of the security features of Office and .NET Framework.

Office Security

You are strongly encouraged to set your Office security level to High (or Very High in Office 2003) and to disable the default Trust all installed Add-ins and templates setting. This combination automatically disables all add-ins and templates that are not code-signed. Furthermore, if the add-ins and templates are code signed with a certificate not listed in the Trusted Sources list, at High security level, the user is prompted to either enable or disable the add-ins, and at Very High level, the add-ins are silently disabled.

If you do set your Office macro security to High or Very High, then you must digitally sign your extension DLLs. This approach helps to increase the security of your Office platform, and you are strongly encouraged to do this.

For additional information on Office macro security, see Office XP Document: Macro Security White Paper and Microsoft Office 2003 Editions Security Whitepaper. For general information on Office security, see Security in Microsoft Office 2003 Editions.

.NET Framework Security

Code access security is a feature of .NET Framework that allows you to establish and enforce security restrictions on assemblies and the code within them. As it loads an assembly, .NET Framework grants that assembly a set of permissions to access system resources such as the file system, the registry, printers, and the environment table. A group of permissions is called a permission set, and these permissions are based on security policy.

The .NET Framework security infrastructure includes a standard set of permission sets, and an administrator can create new permission sets. There is also a standard set of code groups, and an administrator can create new code groups. A code group maps assemblies to permission sets by gathering evidence about an assembly, including its strong-name identity. The result is that an assembly with a particular identity is mapped to a specific set of access permissions. The security policy system is entirely open-ended and configurable through administrative tools supplied with the .NET Framework Software Development Kit. Your code may also make programmatic checks and requests for permissions.

All managed code is subject to code access security policy. The default code access security policy includes a My_Computer_Zone code group. In essence, this policy means that any managed code installed on the local machine is given the FullTrust permission set; that is, it has permission to perform any operation and access any resource. Note, however, that the capacity of any code (managed or unmanaged) to perform operations and access resources is still subject to the operating system-level permissions accorded to the current user. So, code access security FullTrust permissions allow code to do anything the current user can do.

All three types of Office extensions discussed previously must be installed on the local machine in order to function. Therefore, all three types become members of the My_Computer_Zone code group and have FullTrust permissions by default.

Whoever is responsible for administering code access security policy on the machine can establish a custom policy that restricts permissions in arbitrary ways. It would be possible, for example, to create a custom policy that restricts permissions specifically for COM add-ins, smart tags, or real-time data components. It is also possible to restrict permissions for specific individual assemblies. The scope for establishing custom code access security policy is open-ended.

**Note   **The Visual Studio Tools for Office loader revokes FullTrust policy for My_Computer_Zone as a first step before loading any managed assemblies. Therefore, if you use the Visual Studio Tools for Office loader to load your managed smart tag assemblies, you must set up code access security policy explicitly for those assemblies.

For further information on .NET code access security, see An Overview of Security in the .NET Framework.

Application Domain Isolation

With managed code, a single process can contain one or more application domains. You can include several application domains in a single process and achieve a level of isolation similar to that of separate processes, but without the complexity of cross-process calls or process switching. You can create multiple application domains in a process and load one or more DLLs or executables into each application domain. The ability to run multiple applications within a single process significantly increases server scalability. If you run applications in separate application domains within a single process, the benefits include:

  • Faults in one application domain cannot affect code running in another application domain.
  • Individual applications or assemblies can be stopped without stopping the entire process; that is, you can unload an application domain programmatically.
  • Code running in one application domain cannot directly access code or resources from another application domain. The CLR enforces this isolation by preventing direct calls between objects in different application domains. Objects that pass between application domains are either copied or accessed by proxy.

For more information on application domains, see Application Domains Overview.

The .NET CLR gives every managed application a default application domain when it starts. You can write code to create additional application domains to suit your requirements. When you build a managed extension solution, there is one application domain, called the default application domain. Table 1 summarizes the situations in which, with managed solutions, you have additional application domains.

Table 1. Application Domain Status of Managed Solutions

Managed Solution Additional Application Domains
Visual Studio managed extensibility project (COM add-in) No
Real-time data component No
Standard smart tag recognizer/action DLL No
Visual Studio Tools for Office-loaded smart tag recognizer/action DLL Yes
Visual Studio Tools for Office code-behind solution Yes
Smart document solution Yes
COM add-in using COM shim Yes
Real-time data component using COM shim Yes
Smart tag recognizer/action DLL using COM shim Yes

If you build a managed extension and you do not use the Visual Studio Tools for Office loader, then by default your assembly is put into the default application domain along with all other un-shimmed add-ins, smart tags, and real-time data components. If any one of these extensions malfunctions, it can damage your solution. Your solution may operate properly for a time, until a user installs a new managed extension that causes problems. Conversely, if your code malfunctions, you risk damaging every other extension in the default application domain.

Each time before an add-in is called, its associated file name is added to a block list (on a per-application basis). If the add-in malfunctions before returning to the host Office application code, its file name is not removed from the block list. In the case of an un-shimmed managed add-in, the only file name that Office knows about is Mscoree.dll. If one managed add-in malfunctions and is added to the block list, on next startup, all managed add-ins are added to the block list, and none of them are loaded. Thus, to help enhance security and to achieve isolation between solutions, you are strongly encouraged to use a shim for your managed extension.

Developing with COM Shims

Using a COM shim between an Office host application and your managed extension helps support Office security and achieve isolation between multiple Office extensions. Figure 1 shows the general sequence for developing a shimmed extension assembly.

Figure 1. Developing a managed extension with a COM shim

When you create a managed extension project in Visual Studio .NET, the project typically includes a step that runs Regasm.exe on the target assembly. This action puts entries in the registry such that Office loads the .NET CLR engine, Mscoree.dll, and then your custom extension assembly.

The COM Shim Wizard generates a Microsoft Visual C++ project (either as a new solution or as an addition to your managed extension solution as a new project). The wizard also includes a step to run Regsvr32.exe on the shim. This action replaces any registry entries for your original un-shimmed assembly (or creates new entries if there were none before for this extension assembly). These entries ensure that Office loads the shim instead of your original extension. The shim then loads your original extension assembly into a new application domain. Therefore, you no longer need to register your original managed extension assembly for COM interop itself.

Although it is an optional step, you are encouraged to sign the shim digitally with a code-signing tool such as Microsoft Authenticode. If you do so, then you can set the Office macro security level to High or Very High. This approach gives you both the security and the isolation benefits of using a shim. Just using the shim gives you application domain isolation, but only the opportunity to sign your code. If you choose to sign the shim, you get the additional security benefits.

You can write the shim code yourself, if you wish. For more information on the Visual C++ code for a managed COM add-in shim, see Using the COM Add-in Shim Solution to Deploy Managed COM Add-ins in Office XP. For information on a smart tag version of this shim, see Using the Smart Tag Shim Solution to Deploy Managed Smart Tags in Office XP.

Note that the original shim code described in these articles is slightly updated in the COM Shim Wizard. The differences are as follows:

  • Changed specification for imported type libraries (TypeLibs). The revised code no longer specifies the version or LCID. Instead, the latest version registered on the development machine is used.
  • Added #include <assert.h> to all the variations of stdafx.h in the template code. This is to avoid a compiler warning that may be shown with some version permutations.
  • Changed all shim project files to link statically with the C run-time library. This is to avoid the situation where the CRT DLLs may be available on the development machine but not the deployment machine.
  • Changed #import for Mscorlib to rename ReportEvent to InteropServices_ReportEvent to avoid the compiler warning C4278 about duplicate identifiers.
  • Removed the call to ICorRuntimeHost::Stop from ClrLoader.cpp for all extension types. In version 1.1 of .NET Framework, this Stop method is essentially a no-operation instruction, but in later versions, it causes irreversible unloading of the CLR from the process.

For more information on the use of shims, see Deployment of Managed COM Add-Ins in Office XP and Using the COM Add-in Shim to Trust Outlook 2002 Add-ins Built with Visual Studio .NET.

How the COM Shim Works

The shim is a Visual C++ Active Template Library (ATL) COM DLL. It exposes a COM-creatable class that acts as a proxy to the real managed extension class. The COM Shim Wizard registers the CLSID and ProgId for this class in the registry. In the case of COM add-ins and smart tags, the wizard also registers this class against all the target Office applications you choose. When the host Office application starts, it checks the registry to see which add-ins and smart tags 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 or smart tags.

When the COM shim is loaded and the proxy class is created, the proxy class in turn creates an instance of a second class: the CLR loader class. This class is implemented as a single instance, so only the first attempt to create it actually creates it, and all subsequent attempts merely return the same instance. The CLR loader object loads the .NET CLR. It then creates a new application domain and loads the managed extension assembly into the new application domain. It creates an instance of the managed extension class that implements the target interface (that is, IDTExtensibility2, ISmartTagRecognizer and/or ISmartTagAction, or IRtdServer) and caches the interface pointer. Subsequently, any calls from Office into this interface are handled first by the proxy class. The proxy class merely passes those calls on through the cached pointer to the actual extension class in the managed extension assembly.

Figure 2 summarizes the run-time behavior.

Figure 2. How the COM Shim works (click picture to see larger image)

You can register a COM add-in so that it loads when the target Office application starts. A user can also load and unload a COM add-in by using the COM add-ins toolbar option common to Office applications. This toolbar option is not visible by default, but you can add it to any Office toolbar. To do this, in an Office application, in the View menu, select Toolbars and then Customize. Find the COM add-ins option in the list, and drag it onto any of your existing toolbars. You can then select it to display the COM add-ins dialog box. This dialog box shows all COM add-ins that are installed on your machine and registered for your use (as opposed to any other COM add-ins that might be registered for all users of the machine). It also shows the COM add-ins that are currently loaded in Office.

In Office XP, smart tag recognizers and actions are loaded only when an Office application is started. In Office 2003, smart tags can be loaded without having to close and reopen the Office application.

Excel only loads a given real-time data component when the user enters or modifies a cell formula that refers to that component. In the cell formula, the Excel RTD function is passed to the ProgId of the real-time data server component. It is this ProgId that the COM Shim Wizard registers for the COM shim. Thus, when you enter such a formula in a cell in Excel, the shim is loaded. From that point onward, the behavior is the same as for add-ins and smart tags. The only variation from the flow chart shown in Figure 2 is that the real-time data component is not loaded when Excel starts, but later, after it starts.

How the COM Shim Wizard Works

The COM Shim Wizard operation is streamlined, to encourage developers to use COM shims for their managed extensions. The wizard has a simple user interface that asks several questions and then does all the work in the background. The aim is to allow developers to produce Visual C++ ATL COM shims without having to work directly with C++ code.

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 add-in assembly
  • A managed real-time data component assembly
  • A managed assembly that implements both ISmartTagRecognizer and ISmartTagAction
  • A managed assembly that implements only ISmartTagRecognizer
  • A managed assembly that implements only ISmartTagAction

The COM Shim Wizard operates similarly to most of the standard Visual Studio .NET wizards. It uses a combination of text files, script, and template code files. One difference is that it uses a pair of managed assembly components to perform reflection operations. When you install the wizard, it deploys a number of text files to the installed location for Visual Studio .NET. These text files list the five additional wizards and their properties, including icons to be used in the Visual Studio project wizard pages. The installation also copies five sets of template files, which form the basis of the code that is generated when the wizard runs. Five Microsoft JScript files are deployed, in the standard manner, and these are the controlling scripts that run when you start the wizard.

Finally, the installation deploys a pair of managed assemblies. The installed scripts load the first component, the COMShimHarvester, to ask you a few simple questions. The COMShimHarvester then uses a second component, the AppDomainHarvester. The AppDomainHarvester loads the target managed extension assembly for which you wish to create a shim into a new application domain. It reflects over the assembly to harvest metadata such as the fully qualified name of the class that implements the target interface (such as IDTExtensibility2 or ISmartTagRecognizer), the public key token that forms part of the assembly's strong name (if present), any relevant GUIDs and ProgIds, and the like. The COMShimHarvester uses these details to complete the template source files for the shim, including the registry information.

The rationale for using a shim between Office applications and managed extensions is generally accepted best practice. Moreover, the concepts of providing for a code-signable shim, and of providing a separate application domain for each extension, are also accepted best practice. However, the internal workings of the current version of the shim are merely implementation details. The need to use shims will persist for some time, but the details of how the shim is written internally may change. The current shim hosts the CLR directly, but a valid alternative is for the shim to load a COM-registered managed component, which in turn creates the separate application domain for the shimmed extension. For these reasons, you should not write code that is in any way dependent on the internal implementation of the shim.

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. Be sure Visual Studio .NET is not running when you install the wizard. The default installation location for the wizard is C:\Program Files\Microsoft\COM Shim Wizards. Note that two of the wizard components, the AppDomainHarvester.dll and ComShimHarvester.dll, are also installed in the global assembly cache (GAC), so you must have administrator rights on your machine in order 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 COM Add-ins: a Walkthrough Exercise

The COM Shim Wizard behavior described in this walkthrough exercise is representative of all five of the extension types.

In this exercise, we create an example shim for a managed COM add-in. This is the most commonly used type of managed Office extension and requires the most input from the developer to establish the precise registry requirements.

  1. After you have installed the COM Shim Wizard, start Visual Studio .NET. From the File menu, select New and then Project. In the New Project dialog box, expand the Visual C++ Projects node, and select the child node, COM Shims, as shown in Figure 3.

    Figure 3. Selecting the Addin Shim project in Visual Studio .NET (click picture to see larger image)

  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, as shown in Figure 4.

  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 the browse button and navigate to the assembly. For this exercise, you can use the sample add-in (TestAddin.dll) provided with the download.

  4. Select the assembly, TestAddin.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, as shown in Figure 4.

  5. As Figure 4 shows, 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 generates a new GUID and ProgId. You may want the shim to have a different GUID and ProgId from the original managed component, but it is more likely that you will want to re-use the GUID and ProgId of the original. This is particularly true if you already have test harnesses that rely on the original values. You can use the original values by selecting the check box for this option, as shown in Figure 4.

    Figure 4. Harvested assembly information with the original GUID and ProgId

    Note   We recommend that you reuse the original GUID and ProgId instead of the wizard-generated ones. If you do not, then 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 not normally what you want.

  6. 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 5). This information includes a suitable description and friendly name, the load behavior of the add-in, and the list of target host applications.

    By default, the wizard uses the fully qualified type name of the class that implements the target interface for both the description and friendly name. You can change these as appropriate for your project. Figure 5 shows the Add-in Details page with user-specified choices completed.

    Figure 5. Specifying registry information for the add-in

  7. Click Next to display the Summary page of the wizard, which summarizes the choices you made (Figure 6).

    Figure 6. Wizard summary page for new add-in assembly

  8. Click Finish to generate the new shim project and code. In the Visual Studio Solution Explorer, the shim project should include 15 source files, as shown in Figure 7. The exact number of files varies according to the type of extension for which you are developing a shim.

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

Your next step is to build the shim project. The wizard-generated project includes a final build step that runs Regsvr32.exe on the target DLL to register the shim, so you do not need to register it manually. You can then test your add-in. For example, the sample add-in we used in this walkthrough creates a custom button at the end of the Tools menu in Excel and Word.

To validate that the solution is working as expected with the shim in place, you should exercise your full test schedule. Before you can do this, however, there is one manual step you must take.

The wizard cannot make any assumptions about where you deploy the add-in solution, but because the shim currently requires the extension assembly to be either in the same folder or deployed to the global assembly cache, you should copy or move the extension assembly to the same folder as the shim before you test the solution.**Note that if you instead copy or move the shim to the folder where the managed extension assembly is, you must re-register the shim. This is because when you build the shim, its registry entries include the current path.

Using the Wizard for Managed Smart Tags: a Walkthrough Exercise

In this exercise, we use the COM Shim Wizard to create a shim for a managed smart tag assembly that implements both ISmartTagRecognizer and ISmartTagAction. Although two interfaces are involved, the wizard actually requires less input from the developer than for an add-in assembly, mainly because smart tag solutions need fewer registry entries.

  1. After you install the COM Shim Wizard, start Visual Studio .NET. From the File menu, select New and then Project. Expand the Visual C++ Projects node in the New Project dialog, as shown in Figure 8. Select ST Recognizer + Action Shim, type a suitable name and location, and click OK.

    Figure 8. Selecting the ST Recognizer + Action Shim project in Visual Studio .NET (click picture to see larger image)

  2. In the Specify the Managed Smart Tag Assembly page of the COM Shim Wizard, type in the path to the managed smart tag assembly for which you want to create a shim, or click the browse button to navigate to it. After you select your smart tag assembly, the wizard populates the fields in the dialog with details harvested from the assembly (Figure 9). The harvested information is for two interfaces.

  3. At this point, you have the option to reuse the GUID and ProgId from the original assembly for both the recognizer and action classes. Select the check box, as shown in Figure 9, if you want to reuse the GUID and ProgID. In most situations, you should reuse these values.

    Figure 9. Specifying the smart tag recognizer/action assembly

  4. For smart tags (and for real-time data components), the information on this page is all you must specify. Click Next. The Summary page of the wizard shows the specifications for the new smart tag shim (Figure 10).

    Figure 10. Summary of specifications for smart tag recognizer/action shim

  5. Click Finish. The wizard generates the project and code for the shim. The Visual Studio Solution Explorer shows a project file listing similar to the one in Figure 11.

    Figure 11. Source files for the smart tag recognizer/action shim

You can test this shim with the sample file, TestSmartTagData.xls. The sample smart tag we use in this exercise recognizes the strings "one", "two", "three", and "four", and the action is to display a message box with a numeric equivalent of the string.

Registering the Extension Assembly and Shim

The wizard-generated project includes a final build step that runs Regsvr32.exe on the target DLL to register the shim, so you do not need to register it manually. If you need to re-register the shim at any time without rebuilding the wizard solution, run Regsvr32.exe using the syntax: regsvr32 /s Shim Assembly Name. For example, open a command window, and type in this command:

regsvr32 /s AddinShim1.dll

If you rebuild the managed extension assembly, by default, this also re-registers the assembly. Re-registering overwrites the registration that was done for the shim. It may be that you want this to happen, perhaps because you want to test the un-shimmed extension. If you need to re-register the managed assembly without rebuilding it, run Regasm.exe against it. For example, open a Visual Studio .NET command window, and type in this command:

regasm /tlb /codebase TestAddin.dll

The /tlb switch causes Regasm.exe to generate a type library, and the /codebase switch registers the full path to the assembly in the registry.

If you re-register the assembly and later want to test again with the shim, you must re-register the shim as indicated previously.

When you reach the stage in development where you no longer need to register the un-shimmed assembly, you can turn off registration in the project. To do this, in the Visual Studio Solution Explorer, right-click on the project and select Properties. In the Properties dialog box, navigate to Configuration Properties and select the Build child node. Set the Register for COM Interop setting to False.

If you wish to remove the registration for the shim, use Regsvr32.exe. For example, open a Visual Studio .NET command window, and type in this command:

regsvr32 /u AddinShim1.dll

If the shim was registered after the managed extension assembly, and you have removed the registration for the shim, then there is no need to remove the registration for the managed extension assembly. However, if you wish to explicitly remove the registration for the managed extension assembly at any time, you can do so with Regasm.exe. To do this, open a command window and type in this command:

regasm /unregister TestAddin.dll

Note that you can remove the registration for the shim or the managed extension assembly multiple times without causing any harm.

Additional Features and Known Issues

Additional features of the COM Shim Wizard are as follows:

  • The COM Shim Wizard allows you to create shims for strong-named and delay-signed assemblies. Re-signing a delay-signed assembly does not require any changes to the shim.

  • The wizard allows you to create shims for simple-named or weak-named assemblies. As a matter of general .NET security, you are encouraged to strong-name your assemblies. If you attempt to create a shim for an assembly that is not strong named, the wizard warns you of this, but it allows you to create a shim for the assembly if you choose to. Figure 12 shows the security warning that appears if you attempt to create a shim for an assembly that is not strong named.

    Figure 12. Security warning that appears if you attempt to shim an assembly that is not strong named

  • If the managed assembly is missing GUIDs or ProgIds, new GUIDs are generated, and the fully qualified class name of the class that implements the target interface is used for the ProgId.

  • The wizard supports managed assemblies that use the Office 2003 Primary Interop Assemblies (PIAs), the Office XP PIAs, or custom-generated Interop Assemblies (IAs) for Office. The name of the PIA or IA is not significant. When you generate custom IAs for Office—which you should normally do only for versions of Office in which PIAs are not shipped (that is, for Microsoft Office 97 and Office 2000)—the IAs may have arbitrary names and arbitrary namespaces. For this reason, the wizard allows for arbitrary names and namespaces.

  • In addition to using the wizard to create a new solution, you can use the wizard to add a new shim project to an existing solution. In this way, you can add your shim to your existing managed extension solution. If you do this, you might also want to make the managed project a dependency of the shim project. Then, whenever you build the shim, the managed assembly is built first if it has been changed.

  • With Office 2003, you can optionally implement ISmartTagRecognizer2 as well as ISmartTagRecognizer. You can also implement ISmartTagAction2 as well as ISmartTagAction. Because you can use the Visual Studio Tools for Office loader for Office 2003 smart tags, it is unlikely that you will want to create a shim for an assembly that implements the newer ISmartTagRecognizer2 and ISmartTagAction2 interfaces. Therefore, the COM Shim Wizard does not support creating shims for these interfaces. It is also unlikely that you would develop a smart tag assembly that targets both Office 2003 and Office XP; in fact, this strategy is discouraged.

    If your component implements both ISmartTagRecognizer and ISmartTagRecognizer2, however, and for some reason you need to use the assembly without using the Visual Studio Tools for Office loader, then you should use a custom COM shim. For this reason, the wizard allows you to create a shim for such a component for the older ISmartTagRecognizer interface. The same is true for an assembly that implements both ISmartTagAction and ISmartTagAction2 and for an assembly that implements all four interfaces.

  • The COM Shim Wizard offers simple user assistance in the form of a Help button on the title bar. If you click this button, you get a Help cursor. If you click on an item with the Help cursor, you can see context-sensitive help on that item.

Known issues with the COM Shim Wizard are as follows:

  • The real-time data component shim works successfully for Microsoft Office Excel 2003. For Excel 2002, however, while you can successfully generate a real-time data shim, and while both the shim and the managed real-time data assembly are loaded, the RTD cell function fails. This is a known limitation of the COM Shim Wizard.
  • The shim wizard currently is not designed for use with managed Excel automation add-ins. These add-ins (designed for use exclusively with Excel 2002 and Excel 2003) may expose arbitrary custom automation interfaces, and the current shim does not support this model. It is likely that a subsequent release of the shim will support automation add-ins.

Using the Sample Managed Office Extensions

The download provided with this article installs both the COM Shim Wizard and a set of sample managed extension assemblies for testing purposes. Table 2 lists and describes these samples.

Table 2. Sample Managed Extensions

Sample Description
TestAddin Puts a custom button at the end of the Tools menu (in Excel or Word). When the user clicks the button, the add-in displays a message.
TestRealTimeData A simple RTD feed that produces dummy 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.

Manually Editing the Shim Files

The main purpose of the COM Shim Wizard is to allow developers to create shims without having to work directly with C++ code. Normally, you do not generate a shim until you finish developing a managed extension assembly. You may find, however, that you need to modify the assembly after you generate the shim. You might want to test the solution both with and without the shim, and testing with the shim might highlight issues that require you to change the assembly. Whatever you do, the generated shim code probably will continue to work regardless of any changes you make to the assembly.

Because the shim code has minimal dependencies on the managed extension code, you can generally continue to develop your managed extension project, even after you have generated the shim. If you change any of the dependencies, you can run the wizard again to regenerate the shim project. You must change or regenerate the shim only if you change any of the following:

  • The namespace or type name of the class that implements the target interface (IDTExtensibility2, ISmartTagRecognizer, ISmartTagAction or IRtdServer).
  • The CLSID or ProgId of the class that implements the target interface.
  • For COM add-ins only, the list of target host applications.
  • For COM add-ins only, the load behavior of the add-in.
  • For COM add-ins only, the per-user or per-machine registration.

If you make any of the listed changes, you must change the shim code. You can change the shim code in one of two ways:

  • Run the COM Shim Wizard again, and regenerate the shim project code.
  • Manually edit the generated shim code.

The first option is preferred, but if you choose the second option, there are only three source files in the shim project that you must change:

  • ShimConfig.cpp. This file contains the name and public key token of the class that implements the target interface.
  • xxxProxy.rgs. This file is the registry script that is built into the shim DLL. Its exact name can vary according to the type of extension assembly, but it always has an .rgs file extension. In the case of the combined smart tag recognizer/action shim, there are two .rgs files: one for the Action class, and one for the Recognizer class. The values in the registry scripts correspond to the choices made in the COMShimHarvester for such things as load behavior and target host applications.
  • xxxShim.idl. This file is the Microsoft Interface Definition Language (MIDL) source file that is built into the type library for the shim. You only need to change this file if you change the CLSID of the classes that implement the target interfaces.

The following sections provide details of the changes you might need to make to the shim code. The values that you might need to change are highlighted in bold text in the code examples.

Editing the ShimConfig.cpp File

The code example shown is a ShimConfig.cpp used for a managed COM add-in.

static LPCWSTR szAddInAssemblyName = 
    L"TestAddin, PublicKeyToken=ddf2e0af5633d44c";
static LPCWSTR szConnectClassName = 
    L"TestAddin.Connect";

The following example is another ShimConfig.cpp file, this time for a smart tag recognizer/action assembly.

static LPCWSTR szSmartTagsAssemblyName = 
    L"TestSmartTagRecognizerAction, PublicKeyToken=921d58dbc288bb6b";
static LPCWSTR szRecognizerClassName = 
    L"TestSmartTagRecognizerAction.Recognizer";
static LPCWSTR szActionClassName = 
    L"TestSmartTagRecognizerAction.Action";

Table 3 lists the variables that are dependent on the managed assembly.

Table 3. ShimConfig.cpp Variables

Variable Description
szXXXAssemblyName The name of the managed assembly, listing the name of the DLL file itself (without the .DLL extension) and the public key token.
szXXXClassName The fully qualified name of the class that implements the target interface, in the form, namespace.classname.

Editing the IDL File

The following is an example of an IDL file. In this file, you would only ever need to change the CLSID of the co-class. In this example, the CLSID is 7f20788f-7f95-43b4-8224-b1db54cd7fb7.

import "oaidl.idl";
import "ocidl.idl";

[
    uuid(7B7FEE35-BE47-4ABC-87AA-347BAE716DB7),
    version(1.0),
    helpstring("AddinShim1 1.0 Type Library")
]
library AddinShim1Lib
{
    importlib("stdole2.tlb");

     [
        uuid(7f20788f-7f95-43b4-8224-b1db54cd7fb7),
        helpstring("ConnectProxy Class")
    ]
    coclass ConnectProxy
    {
        [default] interface IUnknown;
    };

};

Editing the Registry File

The third file that contains dependencies is the registry script. The following example (ConnectProxy.rgs) is for a managed COM add-in intended to be hosted in Excel and Word.

**Warning   **Changing this file and rebuilding the corresponding project is equivalent to editing the registry. Editing the registry incorrectly can cause serious problems that may require you to reinstall Windows. There is no guarantee that problems resulting from the incorrect use of .rgs files can be solved. You should back up your registry before building a project where you have manually edited the .rgs file.

HKCR
{
   TestAddin.SomeProgId = s 'Connect Class'
   {
      CLSID = s '{7f20788f-7f95-43b4-8224-b1db54cd7fb7}'
   }
   NoRemove CLSID
   {
      ForceRemove '{7f20788f-7f95-43b4-8224-b1db54cd7fb7}'
           = s 'Connect Class'
      {
         ProgID = s 'TestAddin.SomeProgId'
         InprocServer32 = s '%MODULE%'
         {
            val ThreadingModel = s 'Apartment'
         }
      }
   }
}

HKCU
{
   NoRemove Software
   {
      NoRemove Microsoft
      {
         NoRemove Office
         {
            NoRemove Excel
            {
               NoRemove Addins
               {
                  ForceRemove TestAddin.SomeProgId
                  {
                     val 'Description' = s 'Adds a custom button to                          the end of the Tools menu.'
                     val 'FriendlyName' = s 'TestAddin.Connect'
                     val 'LoadBehavior' = d 3
                  }
               }
            }
            NoRemove Word
            {
               NoRemove Addins
               {
                  ForceRemove TestAddin.SomeProgId
                  {
                     val 'Description' = s 'Adds a custom button to                          the end of the Tools menu.'
                     val 'FriendlyName' = s 'TestAddin.Connect'
                     val 'LoadBehavior' = d 3
                  }
               }
            }
         }
      }
   }
}

Table 4 lists the variables that are dependent on the managed assembly.

Table 4. Add-in Registry Script Variables

Variable Value Description
TestAddin.SomeProgId Occurs four times. This is the registered ProgId of the class that implements the target interface.
7f20788f-7f95-43b4-8224-b1db54cd7fb7 Occurs two times. This is the CLSID of the class that implements the target interface, and is the same as the CLSID of the co-class in the IDL file.
Adds a custom button to the end of the Tools menu. Any arbitrary description.
TestAddin.Connect Any meaningful name for the extension.
3 The value of the LoadBehavior key. A value of 3 indicates that this add-in is loaded automatically when the host application starts. The only useful alternative value to put in this script is 0 (zero), which indicates that the add-in is only loaded when the user asks for it through the COM add-ins dialog.
HKCU Standard abbreviation for the registry hive HKEY_CURRENT_USER. If you want to register this add-in for all users on the machine, use HKLM instead (short for HKEY_LOCAL_MACHINE).

Changing the Host Application

In addition to the variables listed in Table 3, the registry script for an add-in lists the host applications that load the add-in. In the previous example, the script has entries for Excel and Word. To remove one of these entries, you remove the entire node. For example, to remove the Excel entry, you remove the entire node, from NoRemove Excel inclusive down to the closing brace (}) for that node. If you need to add a node, you can model it on the examples given previously. You should not remove any other nodes. If you want to remove all entries in the registry for the add-in, you should remove the registration for it using Regsvr32.exe, as shown in the following example:

regsvr32 /u AddinShim1.dll

The following is an example of a registry script that registers the add-in for all possible host applications. Note that the nodes for the Office applications are slightly different from those for Visual Studio and other applications.

HKCR
{
   TestAddin.SomeProgId = s 'Connect Class'
   {
      CLSID = s '{7f20788f-7f95-43b4-8224-b1db54cd7fb7}'
   }
   NoRemove CLSID
   {
      ForceRemove '{7f20788f-7f95-43b4-8224-b1db54cd7fb7}'
           = s 'Connect Class'
      {
         ProgID = s 'TestAddin.SomeProgId'
         InprocServer32 = s '%MODULE%'
         {
            val ThreadingModel = s 'Apartment'
         }
      }
   }
}

HKCU
{
   NoRemove Software
   {
      NoRemove Microsoft
      {
         NoRemove Office
         {
            NoRemove Access
            {
               NoRemove Addins
               {
                  ForceRemove TestAddin.SomeProgId
                  {
                     val 'Description' = s 'Some Description'
                     val 'FriendlyName' = s 'Some Friendly Name'
                     val 'LoadBehavior' = d 3
                  }
               }
            }
            NoRemove Excel
            {
               NoRemove Addins
               {
                  ForceRemove TestAddin.SomeProgId
                  {
                     val 'Description' = s 'Some Description'
                     val 'FriendlyName' = s 'Some Friendly Name'
                     val 'LoadBehavior' = d 3
                  }
               }
            }
            NoRemove FrontPage
            {
               NoRemove Addins
               {
                  ForceRemove TestAddin.SomeProgId
                  {
                     val 'Description' = s 'Some Description'
                     val 'FriendlyName' = s 'Some Friendly Name'
                     val 'LoadBehavior' = d 3
                  }
               }
            }
            NoRemove 'MS Project'
            {
               NoRemove Addins
               {
                  ForceRemove TestAddin.SomeProgId
                  {
                     val 'Description' = s 'Some Description'
                     val 'FriendlyName' = s 'Some Friendly Name'
                     val 'LoadBehavior' = d 3
                  }
               }
            }
            NoRemove Outlook
            {
               NoRemove Addins
               {
                  ForceRemove TestAddin.SomeProgId
                  {
                     val 'Description' = s 'Some Description'
                     val 'FriendlyName' = s 'Some Friendly Name'
                     val 'LoadBehavior' = d 3
                  }
               }
            }
            NoRemove PowerPoint
            {
               NoRemove Addins
               {
                  ForceRemove TestAddin.SomeProgId
                  {
                     val 'Description' = s 'Some Description'
                     val 'FriendlyName' = s 'Some Friendly Name'
                     val 'LoadBehavior' = d 3
                  }
               }
            }
            NoRemove Publisher
            {
               NoRemove Addins
               {
                  ForceRemove TestAddin.SomeProgId
                  {
                     val 'Description' = s 'Some Description'
                     val 'FriendlyName' = s 'Some Friendly Name'
                     val 'LoadBehavior' = d 3
                  }
               }
            }
            NoRemove Word
            {
               NoRemove Addins
               {
                  ForceRemove TestAddin.SomeProgId
                  {
                     val 'Description' = s 'Some Description'
                     val 'FriendlyName' = s 'Some Friendly Name'
                     val 'LoadBehavior' = d 3
                  }
               }
            }
         }
         NoRemove Visio
         {
               NoRemove Addins
               {
                  ForceRemove TestAddin.SomeProgId
                  {
                     val 'Description' = s 'Some Description'
                     val 'FriendlyName' = s 'Some Friendly Name'
                     val 'LoadBehavior' = d 3
                  }
               }
         }
         NoRemove VisualStudio
         {
            NoRemove 7.1
            {
               NoRemove Addins
               {
                  ForceRemove TestAddin.SomeProgId
                  {
                     val 'Description' = s 'Some Description'
                     val 'FriendlyName' = s 'Some Friendly Name'
                     val 'LoadBehavior' = d 3
                  }
               }
            }
         }
         NoRemove VSA
         {
            NoRemove 7.1
            {
               NoRemove Addins
               {
                  ForceRemove TestAddin.SomeProgId
                  {
                     val 'Description' = s 'Some Description'
                     val 'FriendlyName' = s 'Some Friendly Name'
                     val 'LoadBehavior' = d 3
                  }
               }
            }
         }
      }
   }
}

Editing the Registry File for Other Assemblies

The following is another example of a registry script; this time for a combined smart tag recognizer/action assembly. The script for the action registry entries is shown, and there is a second .rgs file for the recognizer entries that follows the same general pattern. Again, values that you might need to change are highlighted in bold text.

HKCR
{
   TestSmartTagRecognizerAction.SomeActionProgId = s 'STRecognizerProxy Class'
   {
      CLSID = s '{d47d6baa-3989-46e8-adca-250dde710df2}'
   }
   NoRemove CLSID
   {   
      ForceRemove {d47d6baa-3989-46e8-adca-250dde710df2} = s 'STActionProxy Class'
      {
         ProgID = s 'TestSmartTagRecognizerAction.SomeActionProgId'
         InprocServer32 = s '%MODULE%'
         {
            val ThreadingModel = s 'Apartment'
         }
      }
   }
}
HKCU
{
    NoRemove Software
    {
        NoRemove Microsoft
        {
            NoRemove Office
            {
                NoRemove Common
                {
                    NoRemove 'Smart Tag'
                    {
                        NoRemove Actions
                        {        
                            ForceRemove {d47d6baa-3989-46e8-adca-                                250dde710df2}
                        }
                    }
                }
            }
        }
    }
}

Table 5 lists the variables that are dependent on the managed assembly.

Table 5. Smart Tag Registry Script Variables

Variable Value Description
TestSmartTagRecognizerAction.SomeActionProgId Occurs two times. This is the registered ProgId for the class that implements ISmartTagAction.
d47d6baa-3989-46e8-adca-250dde710df2 Occurs three times. This is the CLSID for the class that implements ISmartTagAction.

Note that for smart tag shims and real-time data component shims, you should not remove any of the nodes. If you need to remove the registration for a smart tag or real-time data shim, use Regsvr32.exe to remove the registration for the shim, as shown in the following example:

regsvr32 /u STRAShim1.dll

For additional information on the C++ shim code, see Using the COM Add-in Shim Solution to Deploy Managed COM Add-ins in Office XP.

Conclusion

If you want to write managed add-ins, smart tags, or real-time data components for Office, 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.

There are two reasons why you should use a COM shim: security and isolation. You can digitally sign your COM shim and thereby take fullest advantage of Office security features for your managed extension. If you do not use a COM shim, Office loads your extension DLL into the default application domain along with all other un-shimmed extensions. All DLLs in the same application domain are vulnerable to potential damage caused by any other DLL in the same application domain**.**

Further Reading

Advanced COM Interop

An Overview of Managed/Unmanaged Code Interoperability

AppDomains and Dynamic Loading

COM Interop Part 1: C# Client Tutorial

Creating Office Managed COM Add-Ins with Visual Studio .NET

Digital Code Signing Step-by-Step Guide

Securing Office Documents and Visual Basic for Applications Code

Security in Office Solutions That Use Managed Code Extensions

Smart Tag Installation and Security for Microsoft Office XP

Support for Managed-Code Smart Tags

Visual Basic Language Concepts: COM Interop

© Microsoft Corporation. All rights reserved.