如何使用 Visual C# .NET 生成 Office COM 加载项

摘要

Microsoft Office XP、Microsoft Office 2003 和 Microsoft Office 2007 支持用于构建应用程序外接程序以增强和控制 Office 应用程序的统一设计体系结构。 这些加载项称为 Microsoft Component Object Model (COM) 加载项。本分步文章讨论 Office COM 加载项,并介绍如何使用 Microsoft Visual C# .NET 生成 Office COM 加载项。

IDTExensibility2 接口

COM 外接程序是一个进程内 COM 服务器或 ActiveX 动态链接库 (DLL) ,可实现 IDTExensibility2 接口,如 Microsoft 外接程序设计器类型库 (Msaddndr.dll) 中所述。 所有 COM 加载项都继承自此接口,必须实现其五种方法中的每个方法。

OnConnection

每当 COM 加载项连接时,就会触发 OnConnection 事件。 加载项可能在启动时、最终用户或自动化时进行连接。 如果 OnConnection 事件成功返回,则表示加载项已加载。 如果返回错误消息,主机应用程序会立即释放对加载项的引用,并销毁该对象。

OnConnection 事件采用以下四个参数:

  • 应用程序:对主机应用程序对象的引用。

  • ConnectMode:一个常量,指定加载项的连接方式。 可通过以下方式连接加载项:

    • ext_cm_AfterStartup:加载项由最终用户从 COM 加载项对话框启动。
    • ext_cm_CommandLine:加载项是从命令行连接的。 请注意,这不适用于为 Office 应用程序生成 COM 加载项。
    • ext_cm_External:外接程序由外部应用程序通过自动化连接。 请注意,这不适用于为 Office 应用程序生成 COM 加载项。
    • ext_cm_Startup:加载项由应用程序启动时主机启动。 此行为由注册表中的设置控制。
  • AddInInst:对 COMAddIn 对象的引用,该对象引用主机应用程序的 COMAddIns 集合中的此加载项。

  • 自定义:可保存用户定义数据的 Variant 类型值的数组。

OnDisconnection

当 COM 加载项断开连接时,就在它从内存中卸载之前,OnDisconnection 事件会触发。 加载项应在此事件中对资源执行任何清理,并还原对主机应用程序所做的任何更改。

OnDisconnection 事件采用以下两个参数:

  • RemoveMode:一个常量,指定外接程序的断开连接方式。 可通过以下方式断开加载项的连接:

    • ext_dm_HostShutdown:当主机应用程序关闭时,加载项断开连接。
    • ext_dm_UserClosed:加载项由最终用户或自动化控制器断开连接。
  • 自定义:可保存用户定义数据的 Variant 类型值的数组。

OnAddInsUpdate

当已注册的 COM 加载项集发生更改时,将触发 OnAddInsUpdate 事件。 换句话说,每当在主机应用程序中安装或删除 COM 加载项时,就会触发此事件。

OnStartupComplete 和 OnBeginShutdown

当主机应用程序离开或进入用户交互应避免的状态时,将调用 OnStartupComplete 方法和 OnBeginShutdown 方法,因为应用程序正忙于从内存中加载或卸载自身。 仅当加载项在启动期间连接时才调用 OnStartupComplete 方法,并且仅当主机在关闭期间断开外接程序连接时才调用 OnBeginShutdown 方法。

由于主机应用程序的用户界面在这些事件触发时处于完全活动状态,因此它们可能是执行某些操作的唯一方法,否则这些操作将无法从 OnConnection 事件和 OnDisconnection 事件中使用。

COM 加载项注册

除了正常的 COM 注册外,COM 加载项还需要向运行它的每个 Office 应用程序注册自身。 若要向特定应用程序注册自身,外接程序应使用其 ProgID 作为密钥的名称,在以下位置下创建子项:

HKEY_CURRENT_USER\Software\Microsoft\Office\OfficeApp\Addins\ProgID

外接程序可以在此键位置为友好的显示名称和完整说明提供值。 此外,外接程序还应使用名为 LoadBehavior 的 DWORD 值来指定其所需的负载行为。 此值确定主机应用程序加载外接程序的方式,并由以下值的组合组成:

  • 0 = 断开连接 - 未加载。
  • 1 = 已连接 - 已加载。
  • 2 = 启动负载 - 在应用程序启动时加载。
  • 8 = DemandLoad - 仅当用户请求时加载。
  • 16 = ConnectFirstTime - 在下一次启动) 上仅加载 (一次。

指定的典型值0x03 (已连接|启动负载) 。

实现 IDTExtensibility2 的外接程序还应指定一个名为 CommandLineSafe 的 DWORD 值,以指示加载项对于不支持用户界面的操作是否安全。 值0x00表示 False,值为 0x01 表示 True。

如何使用 Visual C# .NET 生成 COM 加载项

如前所述,Office COM 加载项是一个进程内 COM 服务器,由 Office 应用程序通过 COM 运行时层激活。 因此,在 .NET 中开发 COM 外接程序需要在 .NET 中实现外接程序组件,然后向 COM 客户端公开 (即 Office 应用程序通过 COM 互操作层) 。

若要在 Visual C# .NET 中创建 COM 加载项,请执行以下步骤:

  1. 在 Visual C# .NET 中,创建类库项目。
  2. 添加对实现 IDTExtensibility2 的类型库的引用。 此项的主要互操作程序集已以扩展性名称提供。
  3. 添加对 Microsoft Office 对象库的引用。 此项的主要互操作程序集已以 Office 名称提供。
  4. 在实现 IDTExtensibility2 的类库中创建公共类。
  5. 生成类库后,注册 COM 互操作库。 为此,请为此类库生成强命名程序集,然后将其注册到 COM 互操作。 可以使用Regasm.exe注册用于 COM 互操作的 .NET 组件。
  6. 创建注册表项,以便 Office 应用程序能够识别和加载加载项。

可以选择完成所有这些步骤,也可以创建属于共享加载项类型的 .NET 项目。 这会启动扩展性向导,帮助你在 .NET 中创建 COM 加载项。

扩展性向导创建 Visual C# .NET 类库项目以及实现 IDTExtensibility2 接口的 Connect 类。 还生成实现 IDTExtensibility 的空成员的主干代码。 此项目具有对扩展性和 Office 程序集的引用。 项目的生成设置已选择“注册 COM 互操作”。 生成程序集密钥 (.snk) 文件,并在 Assemblyinfo.vb 中的 AssemblyKeyfile 属性中引用。

与类库项目一起,向导会生成一个安装项目,可用于在其他计算机上部署 COM 加载项。 如果需要,可以删除此项目。

分步示例

  1. 在 Microsoft Visual Studio .NET 的“文件”菜单上,单击“新建”,然后单击“项目”。

  2. 在“新建项目”对话框中,展开“项目类型”下的其他项目,选择“扩展项目”,然后选择“共享外接程序”模板。

  3. 键入 MyCOMAddin 作为加载项的名称,然后单击“确定”。

  4. 显示扩展性向导时,请执行以下步骤:

    1. 在第 1 页上,选择“使用 Visual C# 创建加载项”,然后单击“下一步”。

    2. 在第 2 页上,选择以下主机应用程序,然后单击“下一步”:

      • Microsoft Word
      • Microsoft PowerPoint
      • Microsoft Outlook
      • Microsoft Excel
      • Microsoft Access
    3. 在第 3 页上,提供加载项的名称和说明,然后单击“下一步”。

      注意 外接程序的名称和说明显示在 Office 应用程序的 COM 外接程序对话框中。

    4. 在第 4 页上,选择所有可用选项,然后单击“下一步”。

    5. 单击"完成"。

  5. 在"项目"菜单上单击"添加引用"。 单击组件列表中的System.Windows.Forms.DLL,单击“选择”,然后单击“确定”。

  6. 将以下内容添加到 Connect 类中的命名空间列表:

    using System.Reflection;
    
  7. 将以下成员添加到 Connect 类:

    private CommandBarButton MyButton;
    
  8. 实现 Connect 类中 IDTExtensibility2 成员的代码,如下所示:

    public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom) {
       applicationObject = application;
       addInInstance = addInInst;
    
    if(connectMode != Extensibility.ext_ConnectMode.ext_cm_Startup)
       {
          OnStartupComplete(ref custom);
       }
    
    }
    
    public void OnDisconnection(Extensibility.ext_DisconnectMode disconnectMode, ref System.Array custom) {
       if(disconnectMode != Extensibility.ext_DisconnectMode.ext_dm_HostShutdown)
       {
          OnBeginShutdown(ref custom);
       }
       applicationObject = null;
    }
    
    public void OnAddInsUpdate(ref System.Array custom)
    {
    }
    
    public void OnStartupComplete(ref System.Array custom)
    {
       CommandBars oCommandBars;
       CommandBar oStandardBar;
    
    try
       {
       oCommandBars = (CommandBars)applicationObject.GetType().InvokeMember("CommandBars", BindingFlags.GetProperty , null, applicationObject ,null);
       }
       catch(Exception)
       {
       // Outlook has the CommandBars collection on the Explorer object.
       object oActiveExplorer;
       oActiveExplorer= applicationObject.GetType().InvokeMember("ActiveExplorer",BindingFlags.GetProperty,null,applicationObject,null);
       oCommandBars= (CommandBars)oActiveExplorer.GetType().InvokeMember("CommandBars",BindingFlags.GetProperty,null,oActiveExplorer,null);
       }
    
    // Set up a custom button on the "Standard" commandbar.
       try
       {
       oStandardBar = oCommandBars["Standard"];        
       }
       catch(Exception)
       {
       // Access names its main toolbar Database.
       oStandardBar = oCommandBars["Database"];      
       }
    
    // In case the button was not deleted, use the exiting one.
       try
       {
       MyButton = (CommandBarButton)oStandardBar.Controls["My Custom Button"];
       }
       catch(Exception)
       {
          object omissing = System.Reflection.Missing.Value ;
          MyButton = (CommandBarButton) oStandardBar.Controls.Add(1, omissing , omissing , omissing , omissing);
          MyButton.Caption = "My Custom Button";
          MyButton.Style = MsoButtonStyle.msoButtonCaption;
       }
    
    // The following items are optional, but recommended. 
       //The Tag property lets you quickly find the control 
       //and helps MSO keep track of it when more than
       //one application window is visible. The property is required
       //by some Office applications and should be provided.
       MyButton.Tag = "My Custom Button";
    
    // The OnAction property is optional but recommended. 
       //It should be set to the ProgID of the add-in, so that if
       //the add-in is not loaded when a user presses the button,
       //MSO loads the add-in automatically and then raises
       //the Click event for the add-in to handle. 
       MyButton.OnAction = "!<MyCOMAddin.Connect>";
    
    MyButton.Visible = true;
       MyButton.Click += new Microsoft.Office.Core._CommandBarButtonEvents_ClickEventHandler(this.MyButton_Click);
    
    object oName = applicationObject.GetType().InvokeMember("Name",BindingFlags.GetProperty,null,applicationObject,null);
    
    // Display a simple message to show which application you started in.
       System.Windows.Forms.MessageBox.Show("This Addin is loaded by " + oName.ToString()   , "MyCOMAddin");
       oStandardBar = null;
       oCommandBars = null;
    }
    
    public void OnBeginShutdown(ref System.Array custom)
    {
       object omissing = System.Reflection.Missing.Value ;
       System.Windows.Forms.MessageBox.Show("MyCOMAddin Add-in is unloading.");
       MyButton.Delete(omissing);
       MyButton = null;
    }
    
    private void MyButton_Click(CommandBarButton cmdBarbutton,ref bool cancel) {
       System.Windows.Forms.MessageBox.Show("MyButton was Clicked","MyCOMAddin"); }
    
  9. 生成并测试 COM 加载项。 为此,请按照下列步骤操作:

    1. 在"构建"菜单上,单击"构建解决方案"。 请注意,生成 COM 加载项会将 .NET 类注册到 COM 互操作。
    2. 启动选择作为外接程序 (的主机应用程序之一的 Office 应用程序,例如 Microsoft Word 或 Microsoft Excel) 。
    3. 加载项启动后,将触发加载项的 OnStartupComplete 事件,并收到一条消息。 关闭消息框。 请注意,外接程序向标准工具栏添加了一个新的自定义按钮,标题为“我的自定义按钮”。
    4. 单击“我的自定义”按钮。 该按钮的 Click 事件由加载项处理,你将收到一个消息框。 关闭消息框。
    5. 退出 Office 应用程序。
    6. 退出应用程序时,OnBeginShutDown 事件会触发,你将收到一条消息。 关闭消息框以结束演示。