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

  • Comments 8

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
                    CallAddIn(Target)
            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( _
                "ExcelSmartTagInteropDemo")
            Dim Utilities As AddInInterfaces.IAddInUtilities = _
                TryCast(AddIn.Object, AddInInterfaces.IAddInUtilities)

            If Utilities IsNot Nothing Then
                Utilities.DisplayData()
            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

Leave a Comment
  • Please add 4 and 1 and type the answer here:
  • Post
  • PingBack from http://microsoft.wawblog.info/?p=6663

  • This is an important article to read if you've every wondered about how to get add-ins you've written

  • Hi,

    You have said in above article :

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

    My question is that, can we access this Range (Target) in the Recognize method of the ISmartTagRecognizer. Actually, I want to add or remove the smart tag actions depending of the excel formula.

    Any help will be greatly appreciated.

    Thanks,

    Shridhar

  • As far as I know, none of the ISmartTagRecognizer methods (including Recognize) provide access to the location of the text. The location appears to only be provided by methods of the ISmartTagAction and ISmartTagAction2 interfaces.

    I believe you can implement the ISmartTagAction2 interface (see http://msdn.microsoft.com/en-us/library/aa206976(office.11).aspx) to create an action that dynamically hides or shows itself based on the context of the application, which sounds like the scenario you are trying to achieve. I haven't tried this myself, but based on the documentation for this interface, the following should be possible:

    1. Implement the ISmartTagAction2.IsCaptionDynamic method (see http://msdn.microsoft.com/en-us/library/aa207110(office.11).aspx) to return true to specify that the action is "dynamic". That is, the action can be shown or hidden based on the return value of your implementation of ISmartTagAction2.VerbCaptionFromID2.

    2. Implement ISmartTagAction2.VerbCaptionFromID2 (see http://msdn.microsoft.com/en-us/library/aa207110(office.11).aspx). Use the Target parameter (which is a Range, like the Target parameter of ISmartTagAction.InvokeVerb) to determine the context of the recognized text, and return an empty string if you do not want to display the action in this case.

  • I can't do this. Please help me... Could You write any code in c# ?

  • Really great information, has given me an idea for a blog of my friends.

  • This post is quite informative. I'm just wondering where the name "ExcelSmartTagInteropDemo" originates from. From my experience, this is not a ProgId of the MyAddin class. Is ist the name of the DLL, or the value of its [AssemblyProduct] attribute?

  • "ExcelSmartTagInteropDemo" is the name of the add-in DLL. You can index into the COMAddIns collection by using this add-in name. By default, this is the same name you give your project when you create it in Visual Studio. If you want to change this after creating your project, you can go to the Application tab of the project properties page in Visual Studio, and change the "Assembly name" field. This is also the name of the <add-in ID> registry entry for the add-in under HKEY_CURRENT_USER\Software\Microsoft\Office\<application name>\Addins.

Page 1 of 1 (8 items)

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