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

Applies to

The information in this topic applies only to the specified Visual Studio Tools for Office projects and versions of Microsoft Office.

Project type

  • Application-level projects

Microsoft Office version

  • 2007 Microsoft Office system

  • Microsoft Office 2003

For more information, see Features Available by Application and Project Type.

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.

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 Visual Studio Tools for Office add-ins.

  • 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. At a minimum, this class must be public, it must have the ComVisibleAttribute attribute set to true, and it must expose the IDispatch interface.

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

The following code example demonstrates how to override RequestComAddInAutomationService. 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.

The requirements for exposing the IDispatch interface in your exposed object is different for VBA clients and non-VBA clients. There are also additional requirements to expose your object to out-of-process clients.

Exposing IDispatch to VBA Solutions

There are several ways the object in your add-in can expose the IDispatch interface to VBA solutions. Typically, the recommended way is to perform the following steps:

  1. Define an interface that declares the methods you want to expose to other solutions.

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

The following code example demonstrates how to define a class that exposes the IDispatch interface. 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";
        }
    }
}

When you perform these steps, 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 applying the ClassInterfaceAttribute attribute to the class, and by setting this attribute to the value AutoDispatch or AutoDual. 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.

Exposing IDispatch to Non-VBA Solutions

If you want to expose an object in your add-in to non-VBA solutions, such as other Visual Studio Tools for Office add-ins, you must perform the following steps to ensure that your object exposes the IDispatch interface: 

  1. Define an interface that declares the methods you want to expose to other solutions. Other solutions can call only the methods that you declare in this interface.

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

  5. Build your add-in project with the Register for COM interop option. For more information, see How to: Register a Component for COM Interop.

Avoiding a Race Condition in Out-of-Process Clients

If you want to expose an object in your add-in to out-of-process clients, follow the steps listed above to expose the IDispatch interface to non-VBA solutions. However, to ensure that out-of-process clients can call your exposed add-in object, your class must also derive from StandardOleMarshalObject.

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 Visual Studio Tools for Office solutions, see Threading Support in 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 COMAddIns property of the Application class.

  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 COMAddInObject 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 COMAddInObject 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 Visual Studio Tools for Office add-in.

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 instead cast the COMAddInObject property value to the class that implements the IAddInUtilities interface in the original add-in, the code will throw an InvalidCastException.

Accessing Objects from Out-of-Process Clients

To access an exposed object in an add-in from an out-of-process client, follow the same steps listed above for non-VBA clients. However, if the out-of-process client tries to call into an add-in shortly after the Office application loads, the client should handle the case where the COMAddInObject property returns null. In this case, the client should retry the operation after a brief wait (for example, 100 milliseconds).

When an Office application starts, the application exposes COMAddIn objects to clients just before the add-ins that correspond to those objects are loaded by the application. This means that an out-of-process client that tries to access a COMAddIn object just after an application starts might retrieve the object before the add-in has loaded. In this case, the COMAddInObject property will return null, because the add-in is not available to return the requested object.

See Also

Tasks

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

How to: Create Visual Studio Tools for Office Projects

Concepts

Programming Application-Level Add-Ins

Calling Code in Document-Level Customizations from VBA

Developing Office Solutions

AddIn Host Item

Customizing UI Features By Using Extensibility Interfaces

Architecture of Application-Level Add-Ins

Change History

Date

History

Reason

August 2008

Expanded guidance about exposing objects to different types of client solutions.

Information enhancement.