使用 VB.NET 开发 Microsoft Office COM 加载项






3.33/5 (7投票s)
本文介绍 VB.NET 开发者如何为 Microsoft Office 2000、2002 (XP)、2003 和 2007 创建 COM 加载项,以自定义 Office 应用程序,包括添加 UI 元素和事件处理。
什么是 COM 加载项?
COM 加载项是一个实现 IDTExtensibility2
COM 接口的 COM DLL。COM 加载项在其容器应用程序(又称宿主应用程序或宿主)的进程中运行。
如何在 VB.NET 中创建 COM 加载项?
您可以使用 Visual Studio 中的共享加载项向导、VSTO 2005 / VSTO 2005 SE 和 Add-in Express 2007 for .NET。您也可以从头开始:只需在 Visual Studio 2003 或 2005 中创建一个类库项目并实现 IDTExtensibility2
。
要启动共享加载项向导,请运行 Visual Studio 2003 或 2005,然后选择菜单中的“文件 | 新建 | 项目”。然后,在“新建项目”对话框中,在“项目类型”窗格中选择“其他项目类型 / 扩展性”,并在“模板”窗格中找到“共享加载项”。
Add-in Express 支持 VS 2003 和 2005。它也在“其他项目类型 / 扩展性”中显示其向导。
VSTO 2005 是一个基于 VS 2005 的产品。它将其向导安装到以下分支:Visual Basic / Office。VSTO 2005 SE - 仅适用于 VS 2005 的免费插件 - 使用 Visual Basic / Office / 2003 加载项和 Visual Basic / Office / 2007 加载项分支。
共享加载项和 Add-in Express 都支持开发多宿主加载项。Add-in Express 允许在同一个程序集中开发多个加载项。VSTO 允许创建一个支持单个 Office 应用程序的单个版本(仅限 2003 或 2007)的加载项。此外,VSTO 仅允许为有限数量的 Office 应用程序开发加载项。
对于共享加载项,您需要处理由加载项启动和关闭时 IDTExtensibility2
接口不一致引起的一些问题。
Office 应用程序的哪些区域可以自定义?
您可以通过以下方式自定义 Office 应用程序的用户界面:
在支持命令栏的应用程序中
- 添加自定义命令栏。
- 将自定义或内置命令栏控件添加到内置命令栏(例如,向菜单或上下文菜单添加项目)。
- 拦截内置命令栏控件的操作。
- 将自定义 .NET 控件添加到命令栏(仅限 Add-in Express)。
在支持功能区(Office 2007 only)的应用程序中
- 添加自定义功能区选项卡。
- 在内置功能区选项卡上添加自定义功能区组。
- 拦截内置功能区控件的操作。
- 添加自定义任务窗格。
在所有 Office 应用程序中
- 创建自定义键盘快捷方式或拦截现有快捷方式(仅限 Add-in Express)。
- 处理宿主对象模型中提供的所有事件。
仅在 Outlook 中
- 向“工具/选项”对话框添加自定义页面。
- 向“文件夹选项”对话框添加自定义页面。
- 向 Outlook Bar 中的“快捷方式”区域添加自定义项目。
- 用自定义 .NET 表单替换给定文件夹的文件夹视图(仅限 Add-in Express)。
- 将自定义 .NET 表单嵌入到以下区域(仅限 Add-in Express)
- 在导航窗格(Outlook Bar)、阅读窗格和待办事项栏显示/隐藏/移动时收到通知(仅限 Add-in Express)。
- 在 Office 2007 颜色方案更改时收到通知(仅限 Add-in Express)。
特殊情况
- 在基于 VSTO 2005(非 SE!)开发的文档级加载项中,在 Word 2003 和 Excel 2003 中添加自定义操作窗格。
请注意,对于上述每个条目,Add-in Express 都提供了特殊的组件或事件。
什么是命令栏和命令栏控件?
在 Office 中,工具栏、菜单和上下文菜单都是命令栏。命令栏通过其名称标识。然而,在测试加载项时,您会发现可以创建几个同名的命令栏。通常,宿主在其 Application
类中提供 CommandBars
属性。CommandBar
类提供命令栏控件的 Controls
集合。
命令栏控件分为两大类:内置和自定义。内置命令栏控件基于 ID:每个命令栏控件都有 ID 属性,对于内置控件,它们的 ID 是预定义的。要查找此或彼命令栏控件的 ID 以及命令栏名称,您可以使用我们的免费 内置控件扫描器。
Office.MsoControlType
枚举表明您可以创建多种控件类型。然而,对于开发人员来说,命令栏控件只是一个按钮、组合框和弹出窗口。
命令栏按钮允许显示标题和/或图标。按钮的外观取决于 Office 版本和 Style
属性的值。按钮提供的唯一事件是 Click
。
命令栏组合框是带标签的真实组合框的精简版。失去其下拉部分后,它就变成了一个精简的带标签文本框。唯一可用的事件是 Change
。此事件在您更改文本并将焦点移离控件后触发。
命令栏弹出窗口是一种可以显示弹出菜单的控件。最简单的例子是 Word 菜单上的“文件”。此控件没有事件。
在命令栏中显示真实控件的需求促使我们创建了 Microsoft Office 的工具栏控件,该控件现已包含在 Add-in Express 包中。通过此产品,您可以在工具栏上显示任何 .NET 控件。请参阅以下 将 UserControl 添加到工具栏的示例加载项。
如何在我的加载项中创建命令栏?
Office 对象模型中与命令栏相关的部分看起来非常简单,并且不会带来任何麻烦。但是,当您调试您的第一个命令栏以使其稳定和功能正常时,您将了解真相:Office 中的命令栏和命令栏控件需要付出太多努力才能获得如此简单的结果。首先,您将了解到 Office 应用程序对工具栏的处理方式不同。甚至同一个应用程序也可以对命令栏进行不同的处理(Word 和 Outlook)。然后您将了解到宿主可以通过 UI 和通过自动化启动。当然,任何像样的命令栏都必须支持这些情况。至于 Outlook,它还可以通过另一种方式运行:程序可以通过自动化启动 Outlook,然后用户可以以通常的方式运行它。唉,在这种情况下,没有适合添加命令栏的事件。然后您会发现命令栏控件的行为因命令栏的停靠状态而异。当您的加载项通过 COM 加载项对话框关闭(参见 Add-in Express 论坛上的 将 COM 加载项命令添加到工具栏或菜单)或卸载时,您必须删除您的命令栏。另一个问题是用户可以删除您的命令栏。
在共享加载项和 VSTO 中,您可以从 OutlookCode.com 上的以下 示例代码 开始。如果您选择这种方式,我建议您关注此帖子回复的数量。并且不要认为这些答案完全涵盖了主题(命令栏和控件)。四个 Office 版本、两种启动类型(通过 UI、通过自动化,加上 Outlook 的自动化和 UI 混合)、三种 Outlook 编辑器类型、许多 Office 应用程序:只需将这些数字相乘即可获得您需要测试工具栏的变体数量。
在 Add-in Express 中,您将 CommandBar 组件添加到加载项模块,并使用其可视化设计器用 CommandBar 控件组件填充命令栏。这些组件已在上述所有场景中进行了测试。
对于 Outlook,Add-in Express 包含两个特殊的命令栏组件(用于资源管理器和检查器窗口),它们支持多资源管理器和多检查器模式,并为 Outlook 命令栏添加上下文敏感功能。例如,您可以指定邮件类别和/或文件夹,以便您的工具栏将显示。这些针对 Outlook 的命令栏组件几乎解决了 WordMail 中命令栏的所有问题(当 Word 设置为默认邮件编辑器时)。
什么是功能区?
在几个 Office 2007 应用程序中,他们添加了一个新的功能区用户界面,与命令栏相比,它提供了一种非常不同的 Office 自定义方法。这些应用程序是 Access、Excel、Outlook、PowerPoint 和 Word。在 Outlook 2007 中,功能区 UI 仅适用于检查器窗口。
要利用功能区自定义功能,加载项必须实现 IRibbonExtensibility
COM 接口。当此类加载项启动时,Office 会查询该接口,以便加载项返回描述由加载项创建的功能区控件以及与控件相关的回调过程名称的 XML 标记。
无法动态创建功能区控件(功能区项目除外:菜单、组合框、下拉列表和库项目)。您不能将您的控件添加到内置功能区组。相反,您可以创建一个自定义功能区组并将其添加到内置选项卡。
无论您使用何种工具自定义功能区,您都需要从 Microsoft 网站下载 Office 2007 控件 ID 列表。
如何在我的加载项中自定义功能区界面?
如果您需要为开发 VSTO 和共享加载项提供一个良好的起点,请查看 MSDN 上的 自定义 2007 Office Fluent 功能区以供开发人员使用(第 1 部分,共 3 部分)。
功能区 UI 的灵活性与命令栏的灵活性无法相比。在 Add-in Express 中,我们为功能区界面提供了真正的设计时功能。使用可视化设计器,您可以在几分钟内创建一个功能区选项卡。此外,您无需关心您的加载项运行的 Office 版本,因为功能区 UI 将在支持功能区的应用程序中显示,而命令栏组件在此情况下默认不会显示。Add-in Express 还提供了一种简单的方法来跨多个加载项共享功能区控件。如果您需要完全的灵活性,您可以在适当的事件处理程序中修改功能区 XML。
如何创建任务窗格?
在 VSTO 和共享加载项中,您需要实现 ICustomTaskPaneConsumer
接口。当然,这很简单。特别是,如果您知道其中的奥秘。VSTO 开发人员可能希望阅读 Andrew Whitechapel 博客上的帖子。
Add-in Express 实现了此接口,并允许您通过三个简单步骤创建真正的任务窗格:将 UserControl 添加到项目,然后向 TaskPanes
集合添加一个项,最后通过在属性编辑器中选择该项将其绑定到 UserControl。Add-in Express 还为您处理任务窗格的不一致问题。
对于 2007 年之前的 Outlook 开发人员,Add-in Express 提供了 Add-in Express Extensions for Microsoft Outlook。该工具将自定义表单窗口嵌入到 Outlook 2000、Outlook XP、Outlook 2003 和 Outlook 2007 的窗口链中!
托管加载项如何加载到 Office 应用程序?
基于 .NET 的加载项由于自动内存管理(.NET Framework 的一个特性,它是一个庞大的运行时库,取决于版本大小为 20-100 MB)而被称为托管加载项。
为了使加载项被宿主应用程序识别和加载,注册表必须包含 下面 讨论的注册表项之一。使用 VB6 或其他支持 COM 的开发工具,您只需在您选择的键中指定您的 ActiveX DLL。对于托管程序集,这不起作用,因为 Office 是完全非托管的,它对 .NET 一无所知。解决方案是一个称为“shim”的非托管 DLL。当它在上述任何一个键中被引用时,宿主会加载 shim,shim 又会加载公共语言运行时 (CLR) 并创建一个应用程序域(参见 MSDN 中的 AppDomain
类)。然后,shim 将 Office 到加载项的调用以及加载项到 Office 的调用进行重定向。当然,每个 shim 都实现 IDTExtensibility2
。
共享加载项基于 MSCOREE.DLL,这是一个包含在 .NET Framework 中的通用 shim。VSTO 加载项使用 VSTO 加载器,它是 VSTO 运行时的一部分,是 VSTO 加载项的先决条件。Add-in Express 生成同时支持 MSCOREE.DLL 和 VSTO 加载器的加载项项目。它还提供了一个名为 Add-in Express Loader 的专有 shim。请参阅下面的 Shim 比较表。
如果您熟悉 C++,您可以自己编写 shim:请参阅 Microsoft 网站上的 使用 COM Shim Wizard 版本 2.3 隔离 Microsoft Office 扩展。
托管加载项如何引用宿主应用程序的对象模型
您需要通过互操作程序集(请参阅 MSDN 上的 COM 互操作)引用 COM 类型库,方法是向您的项目添加相应的 .NET 引用。
Microsoft 为 Office 2002 到 2007 提供免费的主互操作程序集 (PIA)。对于 Office 2003-2007,PIA 包含在 Office 安装包中。对于 Office 2002,您可以从 Microsoft 网站下载它们(请参阅 使用 Office XP 主互操作程序集)。为了支持 Office 2000(及更高版本),您有两种方法:自己为 Office 应用程序创建互操作(请参阅 下面手动创建互操作)或使用 Add-in Express 提供的版本无关互操作程序集。
如果您使用 PIA 开发加载项,该加载项将支持您开发 PC 上安装的 Office 版本以及所有更高版本的 Office。使用版本无关互操作时,无论您的 PC 上安装了哪个 Office 版本,您的加载项都将支持 Office 2000(前提是宿主应用程序在 Office 2000 中可用)及更高版本。要访问更高版本中引入的属性和方法,您可以使用后期绑定(请参阅 MSDN 中的 System.Type.Invoke()
方法)。要访问更高版本的事件,您要么需要连接到它们(请参阅 如何在 .NET Framework 中使用 Visual C# .NET 或 Visual C# 2005 建立带返回值的 COM 事件接收器),要么使用 Add-in Express 提供的版本感知事件类和应用程序级事件组件。
手动创建互操作:在 Visual Studio 中,只需将 Office 2000 套件中应用程序的 COM 引用添加到项目中。这会自动创建您需要的互操作。但这就是您问题的开始:大量的类和事件无法访问,因为 Office 类型库中存在许多相同的错误,导致 Studio 创建的互操作无法为您工作。您可以反汇编互操作,使不可用的类和事件公开,然后重新编译它(ildasm.exe 和 ilasm.exe)。这正是 Add-in Express 版本中立互操作的创建方式。
托管加载项如何使用宿主对象模型中的 COM 对象
.NET 和 COM 的区别在于内存管理。COM 使用确定性对象销毁:当没有对给定 COM 对象的引用时(您将变量设置为 Nothing
或它离开作用域),对象被销毁并释放相应的内存。在 .NET 中,对象以不确定的方式销毁:当变量设置为 Nothing
或离开作用域时,相应的对象被销毁,其内存将在垃圾回收器 (GC) 下次运行时,发现该对象未使用时被释放。
当您引用一个 COM 类时,.NET Framework 会创建一个运行时可调用包装器 (RCW)。RCW 是引用 COM 对象的变量与 COM 对象本身之间的中介。也就是说,您有以下链:您的变量引用 RCW,RCW 又引用 COM 对象。当您将变量设置为 Nothing
(或它离开作用域)时,RCW 将在 GC 下次运行时被销毁。销毁时,RCW 会释放它引用的 COM 对象。
请注意,如果您以 VB6 风格编写代码,例如 OutlookApp.ActiveExplorer.Selection.Item(1).Subject
,这将创建另外三个 RCW,它们将不与任何 .NET 变量关联。因此,相应的 COM 对象将在不可预测的时间被释放。
通常,释放变量和释放相应 COM 对象之间的时间差没有关系。然而,有时您必须自己释放相应的 COM 对象。在这种情况下,您可以使用位于 System.Runtime.InteropServices
命名空间中的 Marshal.ReleaseComObject()
方法。您将变量传递给此方法,它会使关联的 RCW 释放 COM 对象。
一个需要强制使用 ReleaseComObject
的示例场景包括以下步骤:
- 用户打开电子邮件、约会项等的检查器。
- 加载项处理
InspectorsNewInspector
或Inspector.Activate
事件,以获取对检查器中显示的项目引用。 - 用户更改项目的主题(例如)并单击保存。
- 加载项处理
Item.Write
事件并取消操作。 - 用户关闭检查器而不保存更改,检查器窗口关闭。
- 加载项没有在
Inspector
Close
事件中释放项目;相应的Inspector
对象仍然保留在Inspectors
集合中,等待 GC 的下一次运行。
结果是,用户可以在资源管理器窗口中看到项目主题未更改,但当用户重新打开它时,隐藏的检查器会弹出,电子邮件主题仍然反映了刚刚取消的更改。
VB 6 和 VB.NET 之间还有另一个显著区别。在 VB 6 中,当您将引用 COM 类型的变量分配给另一个相同类型的变量时,COM 基础结构知道 COM 对象被引用了两次。要在 VB6 中释放 COM 对象,您必须将两个变量都设置为 Nothing
。当您在 .NET 中重现相同的场景时,COM 对象只会引用一次,因为变量将共享相同的 RCW。当您对其中一个变量使用 ReleaseComObject
时,这会释放 COM 对象。因此,当您尝试使用第二个变量时,您将收到以下异常:“已与其底层 RCW 分离的 COM 对象不能使用”。
当然,即使您的加载项没有释放单个 COM 对象,宿主应用程序也不会关闭。在 Office 2003 和 2007 中,它们有一个功能:当用户关闭 Office 应用程序时,应用程序会在超时后终止未释放的 COM 资源。Add-in Express 的实践表明,此功能有时不起作用。至于 Office 2000 和 2002:如果您不释放 COM 对象,这些 Office 版本将挂起。
考虑到 Microsoft 通过发布更新和服务包(例如 http://support.microsoft.com/kb/899919)来改变 Office 应用程序的行为这一事实,Add-in Express 团队坚持对您从宿主对象模型中获取的每个 COM 对象使用 ReleaseComObject
。这种方法保证您永远不会遇到上述情况。再次强调,对于 Outlook 2000 和 XP,这种方法是绝对必须的。
如何调试我的加载项?
无论您使用什么工具,您都在项目属性的“启动外部程序”文本框中指定宿主应用程序的路径和文件名。然后,使用共享加载项和 VSTO,您只需运行项目。使用 Add-in Express,您单击“生成 / 注册 Add-in Express 项目”并运行项目。
要研究宿主事件,我们建议使用 DebugView - 一个可从 Microsoft 网站免费获取的实用程序。它显示您通过 System.Diagnostics.Debug.WriteLine()
方法发送的消息。因此,只需在适当的事件处理程序中添加适当的语句。发现有多少程序写入调试信息是很有趣的!
要了解 COM 对象的状态,您可以设置断点并使用“局部变量”窗口。但是,此方法仅适用于类型化变量。
请注意,您可以使用任何启用 VBA 的 Office 应用程序中提供的旧版 VBA 环境。
第一步,您创建一个 Excel 工作簿(Word 文档、PowerPoint 演示文稿),然后进入 VBA 环境(在宿主应用程序中,选择菜单“工具/宏/Visual Basic 编辑器”或直接按 Alt+F11)。在该环境中,选择“工具 | 引用:”并检查您的加载项所属的 Office 应用程序条目。然后您编写一个宏。
例如,在一个引用(如上述方式)Microsoft Outlook 11.0 对象库的 Excel 工作簿中,您在名为 ThisWorkbook 的模块中(您可以创建自己的模块或使用任何现有模块)编写以下代码:
Sub OutlookApplication()
Dim olApp As Outlook.Application
Set olApp = New Outlook.Application
Stop
End Sub
当您运行此代码时,它会在 Stop
语句处停止。此时,您选择“视图/本地窗口”并开始探索 Outlook.Application
类的属性(以及属性的属性,等等)。当然,您可以创建更精巧的宏。
如何部署我的加载项
VSTO、共享加载项向导和 Add-in Express 会自动为您的加载项生成安装项目。
为当前 PC 上所有用户安装的加载项应注册在
HKEY_LOCAL_MACHINE\Software\Microsoft\Office\%APPLICATION%\Addins\%YourAddin%
当然,此类加载项必须由管理员安装。顺便说一下,此类加载项不会显示在 COM 加载项对话框中。
要由当前用户安装,加载项必须注册在
HKEY_CURRENT_USER \Software\Microsoft\Office\%APPLICATION%\Addins\%YourAddin%
共享加载项根据您的选择创建其中一个键。VSTO 在安装项目中创建最少的键集。请注意,VSTO 不允许在 HKLM 中注册加载项!不过,还有其他键。您需要它们来在 Outlook 中显示选项页面。Add-in Express 在安装过程中创建加载项所需的所有键。
与 VSTO 和共享加载项在其安装项目中生成加载项所需的注册表项不同,Add-in Express 提供了一个设计时属性,允许将加载项从 HKLM 重新定位到 HKCU,反之亦然。属性值保存到由即用型自定义操作 DLL 解析的 XML 文件中。同一个 DLL 创建加载项所需的键。这种方法允许您通过 regsvr32 注册您的加载项!这样,您可以将您的加载项包含到非 Studio 的安装项目中。
如何更新我的加载项
Add-in Express 提供了一项独特功能:ClickOnce 加载项部署。此功能允许创建面向用户的可从 Internet 下载的安装程序。该功能包括一个发布向导和一个预编译的 ClickOnce 应用程序,该应用程序在加载项注册、注销、更新或恢复到以前版本时通知开发人员。它为用户提供了上述功能,并允许开发人员显示自定义 GUI 而不是内置 GUI。为了控制部署,加载项提供了 CheckForUpdates
方法。
对于共享加载项和 VSTO 加载项,您需要自己开发类似的 инфраструктура。
结论
加载项开发的复杂性可分类如下:
- 加载项功能:诚然可能很复杂。对此无能为力。
- UI:Add-in Express 包含许多可在所有 Office 版本上运行并加快开发的组件。您编写的代码越少,犯的错误就越少!
- 与 Office 对象模型交互:Add-in Express 团队为其客户提供非常强大的支持,包括 Office 对象模型咨询。
- 加载项部署:Add-in Express 也简化了这部分。它生成安装项目并包含许多即用型自定义操作。它还提供了一个独特的功能——真正的 ClickOnce 加载项解决方案!
在上述项目中,我没有机会提到共享加载项和 VSTO,因为它们在这些方面根本没有提供任何东西。不过,我可能带有偏见。是这样吗?你觉得呢?
Shim 比较表
Add-in Express 加载器 | VSTO 加载器 | 非隔离 (MSCOREE.DLL) | |
支持者 | Add-in Express 2007 for .NET |
VSTO 2005 (SE) Add-in Express 2007 for .NET |
共享加载项 Add-in Express 2007 for .NET |
Visual Studio 版本 | VS 2003,VS 2005 | VS 2005 | VS 2003,VS 2005 |
Office 版本 | Office 2000, 2002, 2003, 2007 | Office 2003, 2007 | Office 2000, 2002, 2003, 2007 |
加载项是否隔离在单独的应用程序域中? | 是 | 是 | 否,您的加载项与其他非隔离加载项共享同一个应用程序域。 |
Shim 是否需要添加到安装项目中? | Add-in Express 项目向导自动添加 | 否,它已随 VSTO 运行时预安装 | 否,它已随 .NET Framework 1.1 和 2.0 预安装 |
哪个 Add-in Express 程序集为安装项目提供即用型自定义操作? | adxloader.dll | adxregaddin.exe | AddinExpress.Install.dll |
Shim 可以签名吗? | 是 | 否 | 否 |
Shim 是否允许在加载项运行时更新加载项 DLL? | 是的。 | 否 | 否 |
Shim 可配置吗? | 是的,您可以编辑清单文件。 | 是的,您可以编辑清单文件。 | 否 |