从其他 Office 解决方案调用应用程序级外接程序中的代码

您可以向其他解决方案(包括其他 Microsoft Office 解决方案)公开外接程序中的对象。如果外接程序提供了您希望使其他解决方案能够使用的服务,这一点非常有用。例如,如果某个 Microsoft Office Excel 外接程序从 Web 服务中执行财务数据计算,则其他解决方案可以通过在运行时调入该 Excel 外接程序来执行这些计算。

**适用于:**本主题中的信息适用于 Microsoft Office 2013 和 Microsoft Office 2010 的应用程序级项目。有关更多信息,请参见按 Office 应用程序和项目类型提供的功能

此过程包括以下两个主要步骤:

  • 在外接程序中,向其他解决方案公开对象。

  • 在其他解决方案中,访问由外接程序公开的对象,然后调用对象的成员。

可调用外接程序中的代码的解决方案类型

您可以向以下类型的解决方案公开外接程序中的对象:

  • 在与外接程序相同的应用程序进程中加载的文档中的 Visual Basic for Applications (VBA) 代码。

  • 在与外接程序相同的应用程序进程中加载的文档级自定义项。

  • 使用 Visual Studio 中的 Office 项目模板创建的其他外接程序。

  • COM 外接程序(即直接实现 IDTExtensibility2 接口的外接程序)。

  • 在不同于您的外接程序的进程中运行的任何解决方案(这些类型的解决方案也称为“进程外客户端”)。其中包括使 Office 应用程序实现自动化的应用程序(如 Windows 窗体或控制台应用程序),以及在其他进程中加载的外接程序。

向其他解决方案公开对象

若要向其他解决方案公开外接程序中的对象,请在外接程序中执行下列步骤:

  1. 定义要向其他解决方案公开的类。

  2. 在 ThisAddIn 类中重写 RequestComAddInAutomationService 方法。返回要向其他解决方案公开的类的实例。

Bb608621.collapse_all(zh-cn,VS.110).gif定义要向其他解决方案公开的类

要公开的类必须至少是公共类,必须具有设置为 true 的 ComVisibleAttribute 特性,并且必须公开 IDispatch 接口。

建议通过执行以下步骤公开 IDispatch 接口:

  1. 定义一个接口,该接口声明要向其他解决方案公开的成员。可以在外接程序项目中定义此接口。不过,如果要向非 VBA 解决方案公开类,以便调用外接程序的解决方案无需引用外接程序项目即可引用此接口,则可能需要在单独的类库项目中定义此接口。

  2. ComVisibleAttribute 特性应用到此接口,并将此特性设置为 true。

  3. 修改您的类以实现此接口。

  4. ClassInterfaceAttribute 特性应用到该类,并将此特性设置为 ClassInterfaceType 枚举的 None 值。

  5. 如果要向进程外客户端公开类,可能还需要执行以下操作:

    • StandardOleMarshalObject 派生类。有关更多信息,请参见向进程外客户端公开类。

    • 在定义此接口的项目中设置**“为 COM 互操作注册”**属性。仅当您希望让客户端使用早期绑定来调入外接程序时,才有必要这么做。有关更多信息,请参见管理编译属性

下面的代码示例演示一个 ImportData 类,该类具有可由其他解决方案调用的 AddInUtilities 方法。若要在更大的演练的上下文中查看此代码,请参见演练:从 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";
        }
    }
}

Bb608621.collapse_all(zh-cn,VS.110).gif 向 VBA 公开类

执行上述步骤时,VBA 代码只能调用在接口中声明的方法。VBA 代码无法调用类中的任何其他方法,包括类从基类(如 Object)中获取的方法。

或者,您可以通过以下方式公开 IDispatch 接口:将 ClassInterfaceAttribute 特性设置为 ClassInterfaceType 枚举的 AutoDispatchAutoDual 值。如果执行此操作,则不必在单独的接口中声明方法。不过,VBA 代码可以调用类中的任何公共方法和非静态方法,包括从基类(如 Object)获取的方法。此外,使用早期绑定的进程外客户端不能调用您的类。

Bb608621.collapse_all(zh-cn,VS.110).gif向进程外客户端公开类

如果要向进程外客户端公开外接程序中的类,则应从 StandardOleMarshalObject 派生该类,以确保进程外客户端可以调用公开的外接程序对象。否则,尝试在进程外客户端中获取已公开对象的实例可能会意外失败。

这是因为对 Office 应用程序的对象模型的所有调入都必须在主 UI 线程上执行,但从进程外客户端对您的对象进行的调用则可以在任意 RPC(远程过程调用)线程上执行。.NET Framework 中的 COM 封送处理机制不会切换线程,而是尝试将对您的对象的调用封送到传入 RPC 线程(而不是主 UI 线程)中。如果您的对象是从 StandardOleMarshalObject 派生的类的实例,则对您的对象的传入调用会自动封送到用于创建公开对象的线程中,该线程将是宿主应用程序的主 UI 线程。

有关在 Office 解决方案中使用线程的更多信息,请参见 Office 中的线程支持

Bb608621.collapse_all(zh-cn,VS.110).gif重写 RequestComAddInAutomationService 方法

下面的代码示例演示如何在外接程序的 ThisAddIn 类中重写 RequestComAddInAutomationService。此示例假设您已经定义了一个要向其他解决方案公开的名为 AddInUtilities 的类。若要在更大的演练的上下文中查看此代码,请参见演练:从 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;
}

加载外接程序后,Visual Studio Tools for Office Runtime将调用 RequestComAddInAutomationService 方法。运行时将返回的对象分配给表示外接程序的 COMAddIn 对象的 Object 属性。此 COMAddIn 对象可供其他 Office 解决方案以及使 Office 实现自动化的解决方案使用。

从其他解决方案中访问对象

若要调用外接程序中的已公开对象,请在客户端解决方案中执行下列步骤:

  1. 获取表示所公开外接程序的 COMAddIn 对象。通过在宿主 Office 应用程序的对象模型中使用 Application.COMAddIns 属性,客户端可以访问所有可用的外接程序。

  2. 访问 COMAddIn 对象的 Object 属性。此属性从外接程序返回已公开的对象。

  3. 调用已公开对象的成员。

对于 VBA 客户端和非 VBA 客户端,COMAddIn.Object 属性返回值的使用方法是不同的。对于进程外客户端,需要其他代码以避免可能的争用情况。

Bb608621.collapse_all(zh-cn,VS.110).gif从 VBA 解决方案中访问对象

下面的代码示例演示如何使用 VBA 调用外接程序公开的方法。此 VBA 宏将调用一个名为 ImportData 的方法,该方法是在名为 ExcelImportData 的外接程序中定义的。若要在更大的演练的上下文中查看此代码,请参见演练:从 VBA 中调用应用程序级外接程序中的代码

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

Bb608621.collapse_all(zh-cn,VS.110).gif从非 VBA 解决方案中访问对象

在非 VBA 解决方案中,必须将 COMAddIn.Object 属性值转换为它实现的接口,然后可以针对接口对象调用已公开的方法。下面的代码示例演示如何从使用 Visual Studio 中的 Office 开发人员工具创建的其他外接程序调用 ImportData 方法。

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();

在此示例中,如果您尝试将 COMAddIn.Object 属性的值强制转换为 AddInUtilities 类而不是 IAddInUtilities 接口,则该代码将引发 InvalidCastException

请参见

任务

演练:从 VBA 中调用应用程序级外接程序中的代码

如何:在 Visual Studio 中创建 Office 项目

概念

应用程序级外接程序的体系结构

使用扩展性接口自定义 UI 功能

其他资源

应用程序级外接程序编程

开发 Office 解决方案