Calling Code in Application-Level Add-ins from Other Office Solutions

You can expose an object in your add-in to other solutions, including other Microsoft Office solutions. This is useful if your add-in provides a service that you want to enable other solutions to use. For example, if you have an add-in for Microsoft Office Excel that performs calculations on financial data from a Web service, other solutions can perform these calculations by calling into the Excel add-in at run time.

Applies to: The information in this topic applies to application-level projects for Microsoft Office 2010 and the 2007 Microsoft Office system. For more information, see Features Available by Office Application and Project Type.

There are two main steps in this process:

  • In your add-in, expose an object to other solutions.

  • In another solution, access the object exposed by your add-in, and call members of the object.

Types of Solutions That Can Call Code in an Add-In

You can expose an object in an add-in to the following types of solutions:

  • Visual Basic for Applications (VBA) code in a document that is loaded in the same application process as your add-in.

  • Document-level customizations that are loaded in the same application process as your add-in.

  • Other add-ins created by using the Office project templates in Visual Studio.

  • COM add-ins (that is, add-ins that implement the IDTExtensibility2 interface directly).

  • Any solution that is running in a different process than your add-in (these types of solutions are also named out-of-process clients). These include applications that automate an Office application, such as a Windows Forms or console application, and add-ins that are loaded in a different process.

Exposing Objects to Other Solutions

To expose an object in your add-in to other solutions, perform the following steps in your add-in:

  1. Define a class that you want to expose to other solutions.

  2. Override the RequestComAddInAutomationService method in the ThisAddIn class. Return an instance of the class that you want to expose to other solutions.

Defining the Class You Want to Expose to Other Solutions

At a minimum, the class you want to expose must be public, it must have the ComVisibleAttribute attribute set to true, and it must expose the IDispatch interface.

The recommended way to expose the IDispatch interface is to perform the following steps:

  1. Define an interface that declares the members that you want to expose to other solutions. You can define this interface in your add-in project. However, you might want to define this interface in a separate class library project if you want to expose the class to non-VBA solutions, so that the solutions that call your add-in can reference the interface without referencing your add-in project.

  2. Apply the ComVisibleAttribute attribute to this interface, and set this attribute to true.

  3. Modify your class to implement this interface.

  4. Apply the ClassInterfaceAttribute attribute to your class, and set this attribute to the None value of the ClassInterfaceType enumeration.

  5. If you want to expose the class to out-of-process clients, you might also need to do the following:

    • Derive the class from StandardOleMarshalObject. For more information, see Exposing Classes to Out-of-Process Clients.

    • Set the Register for COM interop property in the project where you define the interface. This is necessary only if you want to enable clients to use early binding to call into the add-in. For more information, see How to: Register a Component for COM Interop.

The following code example demonstrates an AddInUtilities class with an ImportData method that can be called by other solutions. To see this code in the context of a larger walkthrough, see Walkthrough: Calling Code in an Application-Level Add-in from VBA.

<ComVisible(True)> _
Public Interface IAddInUtilities
    Sub ImportData()
End Interface

<ComVisible(True)> _
<ClassInterface(ClassInterfaceType.None)> _
Public Class AddInUtilities
    Implements IAddInUtilities

    ' This method tries to write a string to cell A1 in the active worksheet.
    Public Sub ImportData() Implements IAddInUtilities.ImportData

        Dim activeWorksheet As Excel.Worksheet = Globals.ThisAddIn.Application.ActiveSheet

        If activeWorksheet IsNot Nothing Then
            Dim range1 As Excel.Range = activeWorksheet.Range("A1")
            range1.Value2 = "This is my data"
        End If
    End Sub
End Class
[ComVisible(true)]
public interface IAddInUtilities
{
    void ImportData();
}

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class AddInUtilities : IAddInUtilities
{
    // This method tries to write a string to cell A1 in the active worksheet.
    public void ImportData()
    {
        Excel.Worksheet activeWorksheet = Globals.ThisAddIn.Application.ActiveSheet as Excel.Worksheet;

        if (activeWorksheet != null)
        {
            Excel.Range range1 = activeWorksheet.get_Range("A1", System.Type.Missing);
            range1.Value2 = "This is my data";
        }
    }
}

Exposing Classes to VBA

When you perform the steps provided above, VBA code can call only the methods that you declare in the interface. VBA code cannot call any other methods in your class, including methods that your class obtains from base classes such as Object.

You can alternatively expose the IDispatch interface by setting the ClassInterfaceAttribute attribute to the AutoDispatch or AutoDual value of the ClassInterfaceType enumeration. If you do this, you do not have to declare the methods in a separate interface. However, VBA code can call any public and non-static methods in your class, including methods obtained from base classes such as Object. In addition, out-of-process clients that use early binding cannot call your class.

Exposing Classes to Out-of-Process Clients

If you want to expose a class in your add-in to out-of-process clients, you should derive the class from StandardOleMarshalObject to ensure that out-of-process clients can call your exposed add-in object. Otherwise, attempts to get an instance of your exposed object in an out-of-process client might fail unexpectedly.

This is because all calls into the object model of an Office application must be made on the main UI thread, but calls from an out-of-process client to your object will arrive on an arbitrary RPC (remote procedure call) thread. The COM marshaling mechanism in the .NET Framework will not switch threads, and it will instead attempt to marshal the call to your object on the incoming RPC thread instead of the main UI thread. If your object is an instance of a class that derives from StandardOleMarshalObject, incoming calls to your object are automatically marshaled to the thread where the exposed object was created, which will be the main UI thread of the host application.

For more information about using threads in Office solutions, see Threading Support in Office.

Overriding the RequestComAddInAutomationService Method

The following code example demonstrates how to override RequestComAddInAutomationService in the ThisAddIn class in your add-in. This example assumes that you have defined a class named AddInUtilities that you want to expose to other solutions. To see this code in the context of a larger walkthrough, see Walkthrough: Calling Code in an Application-Level Add-in from VBA.

Private utilities As AddInUtilities

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

protected override object RequestComAddInAutomationService()
{
    if (utilities == null)
        utilities = new AddInUtilities();

    return utilities;
}

When your add-in is loaded, the Visual Studio Tools for Office runtime calls the RequestComAddInAutomationService method. The runtime assigns the returned object to the Object property of a COMAddIn object that represents your add-in. This COMAddIn object is available to other Office solutions, and to solutions that automate Office.

Accessing Objects from Other Solutions

To call the exposed object in your add-in, perform the following steps in the client solution:

  1. Get the COMAddIn object that represents the exposed add-in. Clients can access all of the available add-ins by using the Application.COMAddIns property in the object model of the host Office application.

  2. Access the Object property of the COMAddIn object. This property returns the exposed object from the add-in.

  3. Call the members of the exposed object.

The way that you use the return value of the COMAddIn.Object property is different for VBA clients and non-VBA clients. For out-of-process clients, additional code is necessary to avoid a possible race condition.

Accessing Objects from VBA Solutions

The following code example demonstrates how to use VBA to call a method that is exposed by an add-in. This VBA macro calls a method named ImportData that is defined in an add-in that is named ExcelImportData. To see this code in the context of a larger walkthrough, see Walkthrough: Calling Code in an Application-Level Add-in from VBA.

Sub CallVSTOMethod()
    Dim addIn As COMAddIn
    Dim automationObject As Object
    Set addIn = Application.COMAddIns("ExcelImportData")
    Set automationObject = addIn.Object
    automationObject.ImportData
End Sub

Accessing Objects from Non-VBA Solutions

In a non-VBA solution, you must cast the COMAddIn.Object property value to the interface it implements, and then you can call the exposed methods on the interface object. The following code example demonstrates how to call the ImportData method from a different add-in that was created by using the Office developer tools in Visual Studio.

Dim addIn As Office.COMAddIn = Globals.ThisAddIn.Application.COMAddIns.Item("ExcelImportData")
Dim utilities As ExcelImportData.IAddInUtilities = TryCast( _
    addIn.Object, ExcelImportData.IAddInUtilities)
utilities.ImportData()
object addInName = "ExcelImportData";
Office.COMAddIn addIn = Globals.ThisAddIn.Application.COMAddIns.Item(ref addInName);
ExcelImportData.IAddInUtilities utilities = (ExcelImportData.IAddInUtilities)addIn.Object;
utilities.ImportData();

In this example, if you try to cast the value of the COMAddIn.Object property to the AddInUtilities class rather than the IAddInUtilities interface, the code will throw an InvalidCastException.

See Also

Tasks

Walkthrough: Calling Code in an Application-Level Add-in from VBA

How to: Create Office Projects in Visual Studio

Concepts

Customizing UI Features By Using Extensibility Interfaces

Architecture of Application-Level Add-Ins

Other Resources

Programming Application-Level Add-Ins

Developing Office Solutions