合并 VBA 和文档级自定义

可以在文档中使用 Visual Basic for Applications(VBA)代码,该代码是Microsoft Office Word 或 Microsoft office Excel 文档级自定义的一部分。 可以从自定义程序集调用文档中的 VBA 代码,也可以将项目配置为在文档中启用 VBA 代码以调用自定义程序集中的代码。

适用于: 本主题中的信息适用于 Excel 和 Word 的文档级项目。 有关详细信息,请参阅 Office 应用程序和项目类型提供的功能

文档级自定义中 VBA 代码的行为

在 Visual Studio 中打开项目时,文档在设计模式下打开。 当文档处于设计模式时,VBA 代码不会运行,因此可以在不运行 VBA 代码的情况下处理文档和代码。

运行解决方案时,VBA 和自定义程序集中的事件处理程序会检测到文档中触发的事件,然后两组代码都会执行。 无法事先确定将在另一个代码之前运行的代码;必须在每个事例中通过测试来确定这一点。 如果未仔细协调和测试这两组代码,则可能会获得意外结果。

从自定义程序集调用 VBA 代码

可以在 Word 文档中调用宏,也可以在 Excel 工作簿中调用宏和函数。 为此,请使用下列方法之一:

  • 对于 Word,调用 Application 类的 Run 方法。

  • 对于 Excel,请调用 Application 类的 Run 方法。

    对于每个方法,第一个参数标识要调用的宏或函数的名称,其余可选参数指定要传递给宏或函数的参数。 第一个参数可以为 Word 和 Excel 提供不同的格式:

  • 对于 Word,第一个参数是一个字符串,可以是模板、模块和宏名称的任意组合。 如果指定文档名称,则代码只能在与当前上下文相关的文档中运行宏,而不仅仅是任何文档中的任何宏。

  • 对于 Excel,第一个参数可以是一个字符串,用于指定宏名称、指示 Range 函数的位置或已注册 DLL (XLL) 函数的寄存器 ID。 如果传递字符串,将在活动工作表的上下文中计算该字符串。

    下面的代码示例演示如何调用从 Excel 文档级项目命名 MyMacro 的宏。 此示例假定 MyMacroSheet1 中定义。

Globals.Sheet1.Application.Run("MyMacro", missing, missing, missing,
    missing, missing, missing, missing, missing, missing, missing,
    missing, missing, missing, missing, missing, missing, missing,
    missing, missing, missing, missing, missing, missing, missing,
    missing, missing, missing, missing, missing, missing);

注释

有关在 Visual C# 中使用全局 missing 变量代替可选参数的信息,请参阅 Office 解决方案中的编写代码

从 VBA 调用文档级自定义项中的代码

可以为 Word 或 Excel 配置文档级项目,以便文档中的 Visual Basic for Applications (VBA) 代码可以在自定义程序集中调用代码。 这在以下情况下很有用:

  • 你希望通过使用与同一文档关联的文档级自定义中的功能来扩展文档中的现有 VBA 代码。

  • 你希望使你在文档级自定义中开发的服务可供最终用户通过编写文档中的 VBA 代码来访问服务。

    Visual Studio 中的 Office 开发工具为 VSTO 外接程序提供了类似的功能。如果要开发 VSTO 外接程序,则可以从其他 Microsoft Office 解决方案调用 VSTO 外接程序中的代码。 有关详细信息,请参阅 来自其他 Office 解决方案的 VSTO 外接程序中的呼叫代码

注释

此功能不能用于 Word 模板项目。 它只能在 Word 文档、Excel 工作簿或 Excel 模板项目中使用。

要求

在允许 VBA 代码调用自定义程序集之前,项目必须满足以下要求:

  • 文档必须具有以下文件扩展名之一:

    • 对于 Word: .docm.doc

    • 对于 Excel: .xlsm.xltm.xls.xlt

  • 文档必须已包含包含 VBA 代码的 VBA 项目。

  • 必须允许文档中的 VBA 代码运行,而无需提示用户启用宏。 可以通过将 Office 项目的位置添加到 Word 或 Excel 信任中心设置中的受信任位置列表来信任要运行的 VBA 代码。

  • Office 项目必须至少包含一个公共类,该类包含向 VBA 公开的一个或多个公共成员。

    可以将方法、属性和事件公开到 VBA。 公开的类可以是宿主项类(例如 ThisDocument 用于 Word,或 ThisWorkbookSheet1 用于 Excel),或项目中定义的其他类。 有关主机项的详细信息,请参阅 主机项和主机控件概述

启用 VBA 代码以调用自定义程序集

可通过两种不同的方式向文档中的 VBA 代码公开自定义程序集中的成员:

  • 可以在 Visual Basic 项目中,将主机项目类的成员公开以供 VBA 使用。 为此,请在“属性”窗口中将宿主项的 EnableVbaCallers 属性设置为 True,而宿主项(即文档、工作表或工作簿)在设计器中打开。 Visual Studio 会自动执行使 VBA 代码能够调用类成员所需的所有工作。

  • 可以将 Visual C# 项目中任何公共类中的成员或 Visual Basic 项目中非主机项类中的成员公开到 VBA。 此选项使你可以更自由地选择向 VBA 公开哪些类,但还需要更多手动步骤。

    为此,必须执行以下主要步骤:

    1. 向 COM 公开类。

    2. 重写项目中主机项类的 GetAutomationObject 方法,以返回要向 VBA 公开的类的实例。

    3. 将项目中任何主机项类的 ReferenceAssemblyFromVbaProject 属性设置为 True。 这会将自定义程序集的类型库嵌入程序集,并将对类型库的引用添加到文档中的 VBA 项目。

    有关详细说明,请参阅如何:在 Visual Basic 项目中向 VBA 公开代码,以及如何:在 Visual C# 项目中向 VBA 公开代码

    EnableVbaCallersReferenceAssemblyFromVbaProject 属性仅在设计时在“属性”窗口中可用;不能在运行时使用它们。 若要查看属性,请在 Visual Studio 中打开主机项的设计器。 有关 Visual Studio 在设置这些属性时执行的特定任务的详细信息,请参阅 主机项属性执行的任务

注释

如果工作簿或文档尚未包含 VBA 代码,或者如果文档中的 VBA 代码不受信任,则会在将 EnableVbaCallersReferenceAssemblyFromVbaProject 属性设置为 True 时收到错误消息。 这是因为 Visual Studio 在这种情况下无法修改文档中的 VBA 项目。

使用 VBA 代码中的成员来调用自定义程序集

将项目配置为允许 VBA 代码调用自定义程序集后,Visual Studio 会将以下成员添加到文档中的 VBA 项目:

  • 对于所有项目,Visual Studio 将添加一个名为 GetManagedClass 的全局方法。

  • 对于在 Visual Basic 项目中使用 EnableVbaCallers 属性公开主机项类成员的情况,Visual Studio 还会在 VBA 项目的 CallVSTOAssemblyThisDocumentThisWorkbookSheet1Sheet2Sheet3 模块中添加一个属性。

    可以使用 CallVSTOAssembly 属性或 GetManagedClass 方法访问在项目中向 VBA 代码公开的类的公共成员。

注释

开发和部署解决方案时,文档有多个不同的副本,可在其中添加 VBA 代码。 有关详细信息,请参阅 将 VBA 代码添加到文档中的指南

在 Visual Basic 项目中使用 CallVSTOAssembly 属性

使用 CallVSTOAssembly 属性访问添加到主机项类的公共成员。 例如,以下 VBA 宏调用 Excel 工作簿项目中MyVSTOMethod类中定义的名为Sheet1的方法。

Sub MyMacro()
    Sheet1.CallVSTOAssembly.MyVSTOMethod()
End Sub

此属性比直接使用 GetManagedClass 方法更方便地调用自定义程序集。 CallVSTOAssembly 返回一个对象,该对象表示你向 VBA 公开的宿主项类。 返回的对象的成员和方法参数显示在 IntelliSense 中。

CallVSTOAssembly 属性具有类似于以下代码的声明。 此代码假设你已在一个名为ExcelWorkbook1的 Excel 工作簿项目中向 VBA 公开了Sheet1宿主项类。

Property Get CallVSTOAssembly() As ExcelWorkbook1.Sheet1
    Set CallVSTOAssembly = GetManagedClass(Me)
End Property

使用 GetManagedClass 方法

若要使用全局 GetManagedClass 方法,请传入与包含 GetAutomationObject 方法重写的主机项类对应的 VBA 对象。 然后,使用返回的对象访问向 VBA 公开的类。

例如,以下 VBA 宏调用了一个名为MyVSTOMethod的方法,该方法是在一个名为ExcelWorkbook1的 Excel 工作簿项目中的Sheet1主机项类中定义的。

Sub CallVSTOMethod
    Dim VSTOSheet1 As ExcelWorkbook1.Sheet1
    Set VSTOSheet1 = GetManagedClass(Sheet1)
    VSTOSheet1.MyVSTOMethod
End Sub

该方法 GetManagedClass 具有以下声明。

GetManagedClass(pdispInteropObject Object) As Object

此方法返回一个对象,该对象表示向 VBA 公开的类。 返回的对象的成员和方法参数显示在 IntelliSense 中。

将 VBA 代码添加到文档的指南

文档有多个不同的副本,可在其中添加调用文档级自定义项的 VBA 代码。

开发和测试解决方案时,可以在调试或运行 Visual Studio 中的项目时打开的文档中编写 VBA 代码(即生成输出文件夹中的文档)。 然而,由于 Visual Studio 会用主项目文件夹中的文档副本替换生成输出文件夹中的文档,此文档中您添加的任何 VBA 代码将在下次编译项目时被覆盖。

如果要在调试或运行解决方案时保存添加到文档的 VBA 代码,请将 VBA 代码复制到项目文件夹中的文档。 有关生成过程的详细信息,请参阅 生成 Office 解决方案

当您准备好部署解决方案时,可以在三个主要文档位置中选择添加 VBA 代码。

在开发用计算机上的项目文件夹中

如果完全控制文档中的 VBA 代码和自定义代码,则此位置很方便。 由于文档位于开发计算机上,因此,如果更改自定义代码,可以轻松修改 VBA 代码。 生成、调试和发布解决方案时,添加到此文档副本的 VBA 代码将保留在文档中。

在设计器中打开 VBA 代码时,无法将 VBA 代码添加到文档中。 必须先关闭设计器中的文档,然后在 Word 或 Excel 中直接打开文档。

注意

如果添加在打开文档时运行的 VBA 代码,在极少数情况下,此代码可能会损坏文档或阻止它在设计器中打开。

在发布或安装文件夹中

在某些情况下,可能适合将 VBA 代码添加到发布或安装文件夹中的文档。 例如,如果在未安装 Visual Studio 的计算机上由其他开发人员编写并测试 VBA 代码,则可以选择此选项。

如果用户直接从发布文件夹安装解决方案,则必须在每次发布解决方案时将 VBA 代码添加到文档。 发布解决方案时,Visual Studio 会覆盖发布位置中的文档。

如果用户从不同于发布文件夹的安装文件夹安装解决方案,则可以避免每次发布解决方案时在文档中添加 VBA 代码。 当发布更新准备好从发布文件夹移动到安装文件夹时,请将所有文件复制到安装文件夹(文档除外)。

在最终用户计算机上

如果最终用户是 VBA 开发人员,并且他们正在调用您在文档级自定义中提供的服务,您可以通过告诉他们在其文档副本中如何使用CallVSTOAssembly属性或GetManagedClass方法来调用您的代码。 将更新发布到解决方案时,最终用户计算机上的文档中的 VBA 代码不会被覆盖,因为发布更新不会对该文档进行修改。

主机项目属性所执行的任务

使用 EnableVbaCallersReferenceAssemblyFromVbaProject 属性时,Visual Studio 将执行不同的任务集。

EnableVbaCallers

在 Visual Basic 项目中将主机项的 EnableVbaCallers 属性设置为 True 时,Visual Studio 将执行以下任务:

  1. 它将属性ComClassAttributeComVisibleAttribute添加到宿主项类。

  2. 它替代主机项类的 GetAutomationObject 方法。

  3. 它将主机项的 ReferenceAssemblyFromVbaProject 属性设置为 True

    EnableVbaCallers 属性设置为 False 时,Visual Studio 将执行以下任务:

  4. 它会从ComClassAttribute类中删除ComVisibleAttributeThisDocument特性。

  5. 它将从主机项类中删除 GetAutomationObject 方法。

    注释

    Visual Studio 不会自动将 ReferenceAssemblyFromVbaProject 属性设置为 False。 可以使用“属性”窗口手动将此属性设置为 False

ReferenceAssemblyFromVbaProject

当 Visual Basic 或 Visual C# 项目中任何主机项的 ReferenceAssemblyFromVbaProject 属性设置为 True 时,Visual Studio 将执行以下任务:

  1. 它为自定义程序集生成一个类型库,并将类型库嵌入程序集中。

  2. 它会在文档中的 VBA 项目中添加对以下类型库的引用:

    • 自定义程序集的类型库。

    • Microsoft Visual Studio Tools for Office 执行引擎 9.0 类型库。 此类型库包含在 Visual Studio Tools for Office 运行时中。

    ReferenceAssemblyFromVbaProject 属性设置为 False 时,Visual Studio 将执行以下任务:

  3. 它会从文档中的 VBA 项目中删除类型库引用。

  4. 它将从程序集中删除嵌入的类型库。

疑难解答

下表列出了修复错误的一些常见错误和建议。

错误 Suggestion
设置 EnableVbaCallersReferenceAssemblyFromVbaProject 属性后,错误消息指出文档不包含 VBA 项目,或者您没有访问文档中 VBA 项目的权限。 确保项目中的文档至少包含一个 VBA 宏,VBA 项目有足够的信任才能运行,并且 VBA 项目不受密码保护。
设置 EnableVbaCallersReferenceAssemblyFromVbaProject 属性后,错误消息指出 GuidAttribute 声明缺失或已损坏。 确保 GuidAttribute 声明位于项目中 的AssemblyInfo.csAssemblyInfo.vb 文件中,并将此属性设置为有效的 GUID。
设置 EnableVbaCallersReferenceAssemblyFromVbaProject 属性后,错误消息指出版本号 AssemblyVersionAttribute 无效。 请确保在项目中的 AssemblyInfo.csAssemblyInfo.vb 文件中,将 AssemblyVersionAttribute 声明设置为有效的程序集版本号。 有关有效程序集版本号的信息,请参阅该 AssemblyVersionAttribute 类。
重命名自定义程序集后,调用自定义程序集的 VBA 代码将停止工作。 如果在向 VBA 代码公开自定义程序集后更改自定义程序集的名称,则文档中的 VBA 项目和自定义程序集之间的链接将断开。 若要解决此问题,请将项目中的 ReferenceFromVbaAssembly 属性更改为 False ,然后返回 True,然后将 VBA 代码中对旧程序集名称的任何引用替换为新的程序集名称。