Calling Into A VSTO Add-in From a COM Smart Tag (McLean Schofield)

My last post explained some of the differences between VSTO smart tags (that is, smart tags that you implement in a document-level solution for Word or Excel by using VSTO) and COM smart tags (that is, smart tags that you create by implementing COM interfaces provided by the smart tag SDK). If you are using VSTO to create application-level add-ins for Word or Excel, or add-ins for other applications that support smart tags, such as PowerPoint and Outlook, then you must use the smart tag SDK if you want to also create smart tags for these applications. The general recommendation is to create your smart tags in a separate assembly (or unmanaged DLL, if you wish).

At the end of the post, I mentioned that if you go this route, you can still call into the VSTO add-in from the COM smart tag. Any technology that enables you to communicate between application domains, such as .NET remoting or Windows Communication Foundation, should work, with varying degrees of complexity. However, the easiest way to do use built-in APIs provided by VSTO and Office to expose an object in your add-in to the smart tag, and then to call into this object from the smart tag.

Exposing an Object in Your Add-in to the Smart Tag

Starting in VSTO 2005 SE, add-ins have been able to expose functionality to other Office solutions by overriding the RequestComAddInAutomationService method. When your add-in is loaded, the VSTO runtime calls this method to give you an opportunity to return an object that want other Office solutions to use. For example, if your add-in can display a custom task pane that enables end users to navigate data, you can expose this feature to other solutions by defining a class with a method that displays the task pane, and then returning an instance of this class in your override of RequestComAddInAutomationService.

Side-bar: COM add-ins that implement the IDTExtensibility2 interface directly can do the same thing. In the implementation of the OnConnection method, the add-in receives an object that represents the application’s view of the add-in as the AddInInst parameter. Although this parameter is typed as an object, for Office add-ins, this object is really a COMAddIn. Inside the OnConnection method, the add-in can set the COMAddIn.Object property to an object it wants to expose. When you override RequestComAddInAutomationService in a VSTO add-in, fundamentally the same thing is going on under the covers, but this is all abstracted away from view.

The following code example demonstrates a simple implementation of RequestComAddInAutomationService. This assumes that my VSTO add-in defines a class called AddInUtilities, which I want other solutions to be able to use.

    Private utilities As AddInUtilities

    Protected Overrides Function RequestComAddInAutomationService() As Object
        If utilities Is Nothing Then
            utilities = New AddInUtilities()
        End If
        Return utilities
    End Function

It is important to understand that you can’t return just any object. Your object must be in an instance of a class that is visible to COM, and that exposes the IDispatch interface. One way to meet these requirements is to first define a COM-visible interface that exposes IDispatch. You should define this interface in its own assembly (for example, a class library project), so that the VSTO add-in and the smart tag assembly can both reference the same interface declaration. The following example demonstrates a simple IAddInUtilities interface that defines a method called DisplayData.

    <System.Runtime.InteropServices.ComVisibleAttribute(True)> _
    <System.Runtime.InteropServices.InterfaceType( _
        ComInterfaceType.InterfaceIsIDispatch)> _
    Public Interface IAddInUtilities
        Sub DisplayData()
    End Interface

Then, in your VSTO add-in that references the assembly that declares IAddInUtilities, you can define a COM-visible class that implements the IAddInUtilities interface. The actual code in the DisplayData implementation isn’t important for this discussion, so I’ll leave it out for clarity.

    <System.Runtime.InteropServices.ComVisibleAttribute(True)> _
    <System.Runtime.InteropServices.ClassInterface( _
        System.Runtime.InteropServices.ClassInterfaceType.None)> _
    Public Class AddInUtilities
        Implements IAddInUtilities

        Public Sub DisplayData() Implements IAddInUtilities.DisplayData

            ‘ Do stuff here.

        End Sub
    End Class

For more specific details about the requirements for the object you return in RequestComAddInAutomationService, see Calling Code in Application-Level Add-ins from Other Office Solutions. For a walkthrough that demonstrates how to expose an object in a VSTO add-in and then call into the object from VBA code in an Excel workbook, see Walkthrough: Calling Code in an Application-Level Add-in from VBA.

Accessing the Object From a COM Smart Tag

When you create a COM smart tag, you must implement the ISmartTagRecognizer and ISmartTagAction interfaces. Of these, ISmartTagAction defines a specific action that the end user can select when they click your smart tag. This is a likely place to want to call into your VSTO add-in. The question is, how do you do this?

When the user clicks the icon to run an action, your implementation of the ISmartTagAction.InvokeVerb method contains the code that you want to run. The Target parameter of this method is an application-specific object that represents the context in which the smart tag appears. For example, in Excel, the Target parameter is a Range that identifies the cell that the smart tag was attached to.

In your implementation of ISmartTagAction.InvokeVerb, you can cast the Target parameter to the appropriate object in the object model of the application. From there, you can easily traverse the application’s object model to get the Application object, then the COMAddIn object for your VSTO add-in, and then finally the Object property that contains the object you exposed in the add-in.

The following example demonstrates a simple implementation of InvokeVerb. If the recognized term is sale, then this method calls a helper method named CallAddIn, and passes the Target parameter to this helper method.

    Public Sub InvokeVerb(ByVal VerbID As Integer, _
        ByVal AppplicationName As String, _
        ByVal Target As Object, ByVal Properties As ISmartTagProperties, _
        ByVal Text As String, ByVal Xml As String) _
        Implements ISmartTagAction.InvokeVerb

        If String.Compare(“sale”, Text, True) = 0 Then
            Select Case VerbID
                Case 1
            End Select
        End If
    End Sub

Here is the definition of the CallAddIn helper method.

    Private Sub CallAddIn(ByVal Target As Object)
        Dim ExcelApp As Microsoft.Office.Interop.Excel.Application = Nothing

        If Target IsNot Nothing Then
            Dim SmartTagRange As Microsoft.Office.Interop.Excel.Range = _
                TryCast(Target, Microsoft.Office.Interop.Excel.Range)

            If SmartTagRange IsNot Nothing Then
                ExcelApp = SmartTagRange.Application
            End If
        End If

        If ExcelApp IsNot Nothing Then
            Dim AddIn As Microsoft.Office.Core.COMAddIn = ExcelApp.COMAddIns( _
            Dim Utilities As AddInInterfaces.IAddInUtilities = _
                TryCast(AddIn.Object, AddInInterfaces.IAddInUtilities)

            If Utilities IsNot Nothing Then
            End If
        End If
    End Sub

This method tries to cast the Target parameter to an Excel Range. If successful, the method gets the Excel Application object, and then uses the COMAddIns property to get the COMAddIn that represents a loaded VSTO add-in with the name ExcelSmartTagInteropDemo. Finally, the method gets the Object property, tries to cast it to an IAddInUtilities, and then calls the DisplayData method implemented by the add-in. This of course assumes that the smart tag assembly references the assembly in which the IAddInUtilities interface is defined.

At run time, when the end user types “sale” in an Excel range and clicks the smart tag action, then the smart tag calls into the DisplayData method implemented in the VSTO add-in. This all happens by way of COM interop via several built-in APIs in VSTO and Office.


McLean Schofield, programming writer