Isolating Microsoft Office Extensions with the COM Shim Wizard Version 2.0

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

Andrew Whitechapel, Microsoft Corporation

Misha Shneerson, Microsoft Corporation

Siew Moi Khor, Microsoft Corporation

May 2006

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

Summary: Learn about a set of new Microsoft Visual Studio 2005 wizards that automate the generation of COM shims for managed Microsoft Office extensions that do not use Microsoft Visual Studio 2005 Tools for the Microsoft Office System. (14 printed pages)

Download COMShimWizardSetup.msi.

Note

The COM Shim Wizard is not a supported tool.

Contents

Overview

One of the best ways to build managed add-ins for Microsoft Office is to use Microsoft Visual Studio 2005 Tools for the Microsoft Office System (Visual Studio 2005 Tools for Office), for Microsoft Office Outlook 2003. For other Office 2003 applications, and for earlier versions of Office, you can build traditional managed IDTExtensibility2-based add-ins, but if you do so you should isolate them. It is important to isolate all managed Microsoft Office extensions from each other. You can do so by using COM shims. These Office extensions include traditional managed IDTExtensibility2-based add-ins, managed smart tag recognizer and action components, and managed real-time data components.

This article introduces the Visual Studio 2005 versions of the COM Shim Wizards, which are based on the Visual Studio .NET 2003 COM Shim Wizards. Full details of the earlier wizards, as well as the reasoning behind why you should use COM shims, are given in this article: Isolating Office Extensions with the COM Shim Wizard.

Note that if you use any version of Visual Studio Tools for Office to build your Office extensions, you do not need to use a custom shim. This is because Visual Studio Tools for Office provides a standard shim as part of its runtime. The Visual Studio Tools for Office runtime is used to load Visual Studio Tools for Office document-level customizations for Microsoft Office Word and Microsoft Office Excel, managed smart document solutions, managed smart tag components that are registered as "Managed", and Visual Studio Tools for Office add-ins. Therefore, none of these solution types need to use a custom COM shim.

The COM shim should be used for the following Office extension types:

  • Managed COM add-ins based on IDTExtensibility2, also known as "Shared Add-ins"

  • Managed Excel real-time data components

  • Managed smart tags that are registered in the default manner, that is, not using the "Managed" subkey

How the COM Shim Works

The 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 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 target Microsoft Office applications that 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. 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 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 passes those calls on through the cached pointer to the actual extension class in the managed extension assembly.

How the COM Shim Wizard Works

The operation of the COM Shim Wizard is streamlined, to encourage you to use COM shims for your managed extensions. The wizard user interface is simple. It asks several questions and then does the work in the background. The aim is to enable you 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 2005 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 2005. 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 want 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 strong name of the assembly (if present), and any relevant GUIDs and ProgIds. The COMShimHarvester 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. 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 v2.0.0.0\. Note that two of the wizard components, the AppDomainHarvester.dll file and the ComShimHarvester.dll file, are also installed in the global assembly cache (GAC), so you must have administrator rights on your computer 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 files. Visual Studio aggregates information from all the files in the Visual Studio installation folder and subfolders.

If you want to uninstall the COM Shim Wizard, you can do so using the Add or Remove Programs item in Control Panel.

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, you create an example shim for a managed IDTExtensibility2 COM add-in. This is the most commonly used type of managed Microsoft Office extension, and it requires the most input from the developer to establish the precise registry requirements.

  1. After you install the COM Shim Wizard, start Visual Studio 2005. 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 1.

    Figure 1. Selecting the Addin Shim project in Visual Studio 2005

  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 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.

  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 2.

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

    Note

    If for some reason 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. If you clear the check box, the COMShimHarvester generates new values. But, 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 usually not desired behavior.

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

  7. By default, the wizard sets the add-in to load when the Microsoft Office 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. Figure 3 shows the Add-in Details page with user-specified choices completed.

    Figure 3. Specifying registry information for the add-in

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

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

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

    Figure 5. 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.

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 that 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 as the shim or to be 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, you 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 requires less input from you 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 2005. On the File menu, point to New, and then click Project. Expand the Visual C++ Projects node in the New Project dialog box, as shown in Figure 6. Select ST Recognizer + Action Shim, type a suitable name and location, and click OK.

    Figure 6. Selecting the ST Recognizer + Action Shim project in Visual Studio 2005

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

  3. By default, the wizard reuses the GUID and ProgId from the original assembly for both the recognizer and action classes. If you want the wizard to generate new values, you can clear the check box. In most situations, you should accept the default.

    Figure 7. Specifying the smart tag recognizer/action assembly

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

    Figure 8. 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 listing in Figure 9.

    Figure 9. Source files for the smart tag recognizer/action assembly

You can test this shim with the sample file, TestSmartTagData.xls. The sample smart tag 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 Prompt window and type 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 for the shim. You may want to overwrite the registration, 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 Command Prompt window and type 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 the project and then click Properties. On the Properties page, select the Build node. Clear the Register for COM Interop check box.

If you want to remove the registration for the shim, use Regsvr32.exe. For example, open a Visual Studio Command Prompt window and type 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 want 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 Prompt window and type 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

The COM Shim Wizard includes several additional features:

  • The COM Shim Wizard enables 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 enables you to create shims for simple-named or weak-named assemblies. As a matter of general .NET Framework 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, but it allows you to create a shim for the assembly if you choose to.

  • 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 Microsoft Office 2003 primary interop assemblies, the Microsoft Office XP primary interop assemblies, or custom-generated interop assemblies for Office. The name of the primary interop assembly or interop assembly is not significant. When you generate custom interop assemblies for Office — which you should usually do only for versions of Office in which primary interop assemblies are not included by Microsoft (that is, for Microsoft Office 97 and Microsoft Office 2000) — the interop assemblies 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 in addition to ISmartTagRecognizer. You can also implement ISmartTagAction2 in addition to 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; doing so 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, although the shim works only with 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 an item with the Help cursor, you can see context-sensitive help for that item.

  • If you build Excel user-defined functions using managed automation add-ins, these should also be shimmed. The COM Shim Wizard does not support managed automation add-ins. However, it is possible to extend the shim code that the wizard generates for a regular COM add-in so that it can be used for an automation add-in. For details on how you can extend the shim code for this purpose, refer to these blog entries:

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. The default installation location is: %ProgramFiles%\Microsoft\COM Shim Wizards v2.0.0.0\Sample Managed Assemblies. Table 1 lists and describes these samples.

Table 1. Sample managed extensions

Sample

Description

TestAddin

Puts 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.

For additional information about C++ shim code, see Isolating Office Extensions with the COM Shim Wizard and Using the COM Add-in Shim Solution to Deploy Managed COM Add-ins in Office XP.

Differences Between Version 1 and Version 2

Releasing a new version of the COM Shim Wizards gave us the opportunity to make a few enhancements. These are detailed below:

  • We fixed a bug in the wizard where the original GUID and ProgId of a SmartTag Action–only assembly was not used.

  • The check box "Use the GUID/ProgId of the original assembly for the shim also" is now selected by default. Therefore, the wizard uses the original values unless you clear this check box.

  • The wizard now sets the Description and Friendly Name values to strings based on the fully qualified name of the extension assembly entry class. For example, if the assembly entry class is called "TestAddin.Connect", then the Description is "Description for TestAddin.Connect", and the Friendly Name is "TestAddin.Connect Friendly Name".

  • For add-ins, the check box "Load the add-in when the Office application starts" is selected by default. This effectively sets the registry LoadBehavior for the add-in to 3.

  • The CCLRLoader code, in the shim code, originally called CorBindToRuntimeEx to load all assemblies as domain-neutral. Domain-neutral assemblies cannot be unloaded. The new version of the shim code calls CorBindToRuntimeEx to load no assemblies as domain-neutral.

  • For add-ins (and only for add-ins), the shim now unloads the add-in application domain when it is disconnected. In the OnDisconnection call, the proxy class now calls a new method in the CCLRLoader class, called Unload. This unloads the application domain, and therefore all assemblies in the application domain. Because this is called in OnDisconnection, the add-in is unloaded when it is disconnected, either through the COM Add-ins dialog box or when the host application shuts down.

These last two changes are related. That is, specifying that no assemblies are loaded as domain-neutral allows us to unload assemblies.

The main reason for supplying the COM Shim Wizards is so that you do not need to worry about the details of the shim code. However, we provide details of the significant differences between versions, so that you can either retrofit this behavior to version 1 COM shims, or reinstate the version 1 behavior for version 2 COM shims if you choose. To see full details of the differences, we recommend using the program WinDiff (or other comparison software) on wizard-generated shim code.

The CConnectProxy class has two new fields: one is a pointer to the CCLRLoader, the other is a flag to determine whether the host has started to shut down.

CCCLRLoader *m_pCCLRLoader;
bool isShutdownInProgress;

These two fields are both initialized in the constructor:

CConnectProxy()
    : m_pConnect(NULL), m_pCCLRLoader(NULL), isShutdownInProgress(false)  
{
}

For all COM add-ins, OnDisconnection is called if the user disconnects the add-in by using the COM Add-ins dialog box. It is typical for developers to code the OnDisconnection method in the add-in to call OnBeginShutdown, but this is not guaranteed. The shim code implements OnDisconnection to unload the application domain, but only if the host application has not already started a shutdown.

STDMETHOD(OnDisconnection)(
    AddInDesignerObjects::ext_DisconnectMode RemoveMode, SAFEARRAY **custom )
{
    HRESULT hr = S_OK;
    if (m_pConnect)
    {
        hr =  m_pConnect->OnDisconnection(RemoveMode, custom);
        if (SUCCEEDED(hr))
        {
            m_pConnect->Release();
            m_pConnect = NULL;

            if (!isShutdownInProgress)
            {
                if (m_pCCLRLoader)
                {
                    hr = m_pCCLRLoader->Unload();
                    m_pCCLRLoader = NULL;
                }
            }
        }
    }
    return hr;
}

When the host application shuts down, it calls OnBeginShutdown, and then OnDisconnection, so the version 2 OnBeginShutdown does not try to call into the underlying add-in's OnBeginShutdown if the application domain is already unloaded.

STDMETHOD(OnBeginShutdown)(SAFEARRAY **custom )
{
    HRESULT hr = S_OK;
    if (m_pCCLRLoader && m_pConnect)
    {
               hr = m_pConnect->OnBeginShutdown(custom);
        isShutdownInProgress = true;
    }
    return hr;
}

The CCLRLoader class defines a new static Unload method:

static HRESULT Unload(mscorlib::_AppDomain *m_pLocalDomain);

The LoadCLR method starts up the common language runtime, as before. The difference in version 2 is in the call to CorBindToRuntimeEx. In version 1, the third parameter was STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN, which means all assemblies were loaded as domain-neutral. In version 2, the third parameter is 0. This means that CorBindToRuntimeEx uses the default, which is STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN. (No assemblies are loaded as domain-neutral, so all assemblies can be unloaded.) Note that CorBindToRuntimeEx returns an ICorRuntimeHost pointer as the final (out) parameter — this is used later to unload the application domain.

HRESULT CCCLRLoader::LoadCLR()
{
    HRESULT hr = S_OK;
    if (m_pHost != NULL)
        return hr;

    hr = CorBindToRuntimeEx(0, 0, 0, 
        CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (PVOID*) &m_pHost);

    if (hr == S_FALSE)
        return hr;
    if (!SUCCEEDED(hr)) 
        return hr;
    return m_pHost->Start();
}

The Unload method is simple: it calls the UnloadDomain method on the ICorRuntimeHost pointer that was returned from CorBindToRuntimeEx.

HRESULT CCCLRLoader::Unload(void)
{
    HRESULT hr = S_OK;
    IUnknown* pUnkDomain = NULL;
    IfFailGo(m_pLocalDomain->QueryInterface(__uuidof(IUnknown), (void**)&pUnkDomain));
    hr = m_pHost->UnloadDomain(pUnkDomain);
    Error:
    if (pUnkDomain != NULL)
        pUnkDomain->Release();
    return hr;
}

Conclusion

If you are not using Visual Studio Tools for Office to build managed Microsoft Office 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 fullest advantage of Microsoft 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.

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

Additional Resources

For more information, see these resources: