• Visual Studio 2008 可扩展性开发(三):AddIn运行机制解析(下)


    前言

    在上篇Add-In运行机制解析(上)中,我分析了Add-In向导生成的代码,从中我们知道只要创建一个类库,它包含实现了IDTExtensibility2接口的类,然后为其建立.addin配置文件,就可以实现一个Add-In了。本文将更进一步,介绍Add-In的事件和生命周期,为今后的开发打下基础。

    Add-In的事件

    Add-In是事件驱动的,可以猜到的事件有加载、卸载、状态改变等等。事实上,这些事件都与IDTExtensibility2接口有关,也就是该接口的5个方法:

    IDTExtensibility2

    如果要了解这些方法如何执行,一个办法是在这些方法中加一个MessageBox,然后通过Add-In Manager进行一些操作,来观察事件的执行。现在使用Add-In向导建立一个简单的Add-In,名字为LifeCycleAddin,不要选择在Tools菜单显示命令,也不要选择在VS启动时加载。然后把Connect类的代码简化一下:

    复制代码
    C# Code - Add-In事件演示
    /// <summary>The object for implementing an Add-in.</summary>
    public class Connect : IDTExtensibility2
    {
        
    public Connect()
        {
        }

        
    /// <summary>
        
    /// Receives notification that the Add-in is being loaded.
        
    /// </summary>
        public void OnConnection(object application, ext_ConnectMode connectMode,
            object
     addInInst, ref Array custom)
        {
            _applicationObject 
    = (DTE2)application;
            _addInInstance 
    = (AddIn)addInInst;

            MessageBox.Show(
    string.Format("Event: OnConnection, connectMode: {0}", connectMode));
        }

        
    /// <summary>
        
    /// Receives notification that the Add-in is being unloaded.
        
    /// </summary>
        public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
        {
            MessageBox.Show(
    string.Format("Event: OnDisconnection, connectMode: {0}", disconnectMode));
        }

        
    /// <summary>
        
    /// Receives notification when the collection of Add-ins has changed.
        
    /// </summary>
        public void OnAddInsUpdate(ref Array custom)
        {
            MessageBox.Show(
    "OnAddInsUpdate");
        }

        
    /// <summary>
        
    /// Receives notification that the host application has completed loading.
        
    /// </summary>
        public void OnStartupComplete(ref Array custom)
        {
            MessageBox.Show(
    "OnStartupComplete");
        }

        
    /// <summary>
        
    /// Receives notification that the host application is being unloaded.
        
    /// </summary>
        public void OnBeginShutdown(ref Array custom)
        {
            MessageBox.Show(
    "OnBeginShutdown");
        }
        
    private DTE2 _applicationObject;
        
    private AddIn _addInInstance;
    }
    复制代码


    每个方法的注释说明了相应的事件何时触发。OnConnection是在Add-In加载的时候;OnDisconnection是在Add-In卸载的时候;OnAddInsUpdate是在所有Add-In的集合状态发生改变的时候;OnStartupComplete是在宿主环境加载完成的时候;OnBeginShutdown则是在宿主环境将被关闭的时候。现在编译项目,然后关闭VS。

    打开VS,开始第一回合的观察。由于没有选择在VS启动时加载,所以现在什么也不会发生。打开Add-In Manager,对于LifeCycleAddin,将其设置为可用,确定。这时触发了OnConnection,connectMode为ext_cm_AfterStartup,也就是说在VS启动之后才加载的;然后还触发了OnAddinsUpdate,因为LifeCycleAddin的状态改变了。再次打开Add-In Manager,对于LifeCycleAddin,将其设置为启动时加载,确定,再次触发OnAddinsUpdate。现在关闭VS,由于Add-In已经加载,所以会触发OnBeginShutdown,然后是OnDisconnection,说明Add-In已经被卸载。

    打开VS,开始第二回合的观察。由于选择了在VS启动时加载,所以此时触发了OnConnection,connectMode为ext_cm_Startup,也就是说是在VS启动时加载的;之后连续触发了OnAddinsUpdate和OnStartupComplete,只有设置为在VS启动时加载才可能触发该事件。至此,5个事件都已经触发过了。

    比较有意思的是OnAddinsUpdate。现在打开Add-In Manager,改变另一个Add-In的设置,点击确定,该事件也会触发,这就是前面所说的“所有Add-In的集合状态发生改变的时候”。

    由前面的介绍可以了解到,实现IDTExtensibility2接口是每个Add-In的核心所在。但是仅仅这些显然还不够。我们不仅需要知道VS合适启动、卸载或改变了Add-In,我们还要能够在这些时候访问VS,否则开发VS的Add-In也就没意义了。这就用到了VS的自动化对象模型(Automation Object Model)

    VS自动化对象模型简介

    在Connect.cs文件的顶部using部分,可以看到两个命名空间:EnvDTE和EnvDTE80。EnvDTE表示开发环境工具扩展(Environment Development Tools Extensibility,常简称为DTE),就是在这里定义了VS的自动化对象模型(以下简称AOM)。而EnvDTE80的80表示8.0版本的AOM,其实还有一个表示9.0版本的EnvDTE90,但没有引用进来。

    简单说一下它们的关系。EnvDTE表示VS2005之前的DTE版本,在每个版本中微软都会修复一些bug或添加新的功能,到了VS2005,微软使用了EnvDTE80表示新版本的变化(包括修复和增强),同时对于那些旧版本中已经存在的类,在后面加了一个数字2表示该类的新版本,如CodeFunction2表示是EnvDTE80中的新类型,而CodeFunction则表示EnvDTE中对应的那个类。EnvDTE90与此类似,比如Solution3、Solution2和Solution分别表示三个版本中表示解决方案的类。对于这些不同版本的类,微软的做法是用新版本的类继承旧的版本,然后进行扩展

    但是相对于EnvDTE80与EnvDTE之间的变化,EnvDTE90的变化要小很多。大部分时候EnvDTE80就够用了,所以默认情况下,Connect.cs文件没有引用EnvDTE90。以后当你看到带着2或3后缀的类型,就能明白它的来历了。下面是AOM的结构图(点击查看大图):

    点击查看大图

    不出意外的是,结构很复杂。原因有二:首先VS本身很复杂,DTE用来表示VS中的元素,不能不复杂;其次,AOM和DTE源自COM,在.NET和COM间的互操作增强一些额外的工作。不过不用担心,这些类封装得非常之好,用起来还是比较容易的。

    这个类型结构的顶端是DTE/DTE2,它是所有其它类型的容器。DTE主要包含5部分内容:解决方案和项目、命令(Command)、事件、文档、调试器,通过这些,我们就能够操作VS的方方面面(可以先看一下图中类的名字)。在后续的随笔中,你将看到这些内容的详细用法。

    再议IDTExtensibility2接口

    现在回到IDTExtensibility2接口,仔细了解一下它的各个方法。

    1)OnConnection

    在实现这个接口的时候,我们需要获得DTE对象,这样才能操作VS,这件事要在OnConnection中去做。

    C# Code - Method Signature
    public void OnConnection(object application, ext_ConnectMode connectMode, 
                
    object addInInst, ref Array custom)


    application参数
    持有AOM根对象的引用,它同时实现了EnvDTE.DTE和EnvDTE80.DTE2接口,所以在我们的例子中,它被转换为DTE2。connectMode参数告诉Add-In是以何种方式加载的,它的值来自Extensibility.ext_ConnectMode枚举:

    • ext_cm_AfterStartup:在VS启动之后加载
    • ext_cm_Startup:在VS启动之时加载
    • ext_cm_External:在VS外部加载(VS已经不再使用该值)
    • ext_cm_CommandLine:从命令行加载
    • ext_cm_Solution:在解决方案内加载
    • ext_cm_UISetup:在建立用户界面时加载

    我们可以根据该参数值的不同进行相应的操作,比如如果是ext_cm_UISetup,可以在菜单上添加一条命令(就像Add-In向导所做的那样)。

    Add-In本身是AddIn接口的一个实例,addInInst参数持有该实例的引用,我们可以将该值保存下来备用。最后,IDTExtensibility2接口的每个方法都有一个custom参数,Add-In的宿主环境可以通过它来传递宿主相关的信息,不过VS总是传递一个空的数组(汗。。。)。

    2)OnStartupComplete

    OnStartupComplete事件仅仅在Add-In随VS启动加载的时候才会触发。

    C# Code - Method Signature
    void OnStartupComplete(ref Array custom)


    如果一个Add-In随VS启动而加载,OnConnection并非总是进行初始化的好地方——比如,Add-In加载的较早,而Add-In需要访问的VS组件尚未加载完毕。

    3)OnAddInsUpdate

    C# Code - Method Signature
    void OnAddInsUpdate(ref Array custom)


    在某个Add-In被加载或卸载的时候,OnAddInsUpdate事件会触发。OnAddInsUpdate事件没有提供被加载或卸载Add-In的信息,不过我们有办法获取到。大体原理是:通过DTE.AddIns/DTE2.AddIns集合我们能够获取到所有的Add-In,里面的元素类型为AddIn,AddIn有个Connected属性,用以表示该Add-In是否处于加载状态,我们在首次触发OnAddInsUpdate事件的时候记录所有Add-In的状态,在下次触发的时候就知道那些Add-In状态改变了,这里就不再给出代码了。

    4)OnBeginShutDown

    C# Code - Method Signature
    void OnBeginShutdown(ref Array custom)


    如果在一个Add-In运行的时候关闭VS,OnBeginShutDown事件会触发。我们在这个时候可以做一些必要的清理工作。

    5)OnDisconnection

    C# Code - Method Signature
    void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)


    在Add-In的生命周期结束的时候,OnDisconnection事件会触发。它跟OnBeginShutDown事件的不同之处在于,这里结束的是Add-In而不是VS。disconnectMode参数的值来自Extensibility.ext_DisconnectMode枚举:

    • ext_dm_HostShutdown:因为VS关闭而卸载
    • ext_dm_UserClosed:在VS运行时卸载
    • ext_dm_UISetupComplete:在用户界面创建完毕后卸载
    • ext_dm_SolutionClosed:在解决方案关闭时卸载

    它的作用类似于ext_ConnectMode,我们可以根据Add-In卸载方式的不同采取不同的动作。

    唔,至此Add-In的事件和生命周期介绍完毕。

    我们身在何处?

    本文主要介绍了VS Add-In的事件和生命周期,通过这些知识,我们能够知道在何时获取需要的信息;同时还简单介绍了VS自动化对象模型。加上Add-In运行机制解析(上),我们应当对Add-In的运行机制有个基本的了解,为接下来的开发打下基础。到现在我们还不足以写出真正有用的Add-In,从下一篇开始我将介绍如何开发真正有用的Add-In。

    参考

    《Professional Visual Studio® 2008 Extensibility》
    《Working with Microsoft Visual Studio® 2005》

    from:http://www.cnblogs.com/anderslly/archive/2009/03/03/vs-addin-explained-part2.html

  • 相关阅读:
    C# 获得word中某一段落所在页的页码
    写一个安全的Java单例
    递归算法
    redis连接池连接失败的问题
    A query was run and no Result Maps were found for the Mapped Statement .....................
    解决maven默认JDK1.5报错
    mybatis控制台不报错数据库却没有值返回的问题
    LNMP 环境更换Nginx 服务器为Tengine
    简易的phpexcel导出柱状图
    Laravel学习基础篇之--路由
  • 原文地址:https://www.cnblogs.com/lidabo/p/2831841.html
Copyright © 2020-2023  润新知