天天看點

Visual Studio 2008 可擴充性開發(三):Add-In運作機制解析(下)

前言

Add-In的事件

Add-In是事件驅動的,可以猜到的事件有加載、解除安裝、狀态改變等等。事實上,這些事件都與IDTExtensibility2接口有關,也就是該接口的5個方法:

Visual Studio 2008 可擴充性開發(三):Add-In運作機制解析(下)

如果要了解這些方法如何執行,一個辦法是在這些方法中加一個MessageBox,然後通過Add-In Manager進行一些操作,來觀察事件的執行。現在使用Add-In向導建立一個簡單的Add-In,名字為LifeCycleAddin,不要選擇在Tools菜單顯示指令,也不要選擇在VS啟動時加載。然後把Connect類的代碼簡化一下:

Visual Studio 2008 可擴充性開發(三):Add-In運作機制解析(下)
Visual Studio 2008 可擴充性開發(三):Add-In運作機制解析(下)
Visual Studio 2008 可擴充性開發(三):Add-In運作機制解析(下)

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

    /// Receives notification that the Add-in is being unloaded.

    public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)

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

    /// Receives notification when the collection of Add-ins has changed.

    public void OnAddInsUpdate(ref Array custom)

        MessageBox.Show("OnAddInsUpdate");

    /// Receives notification that the host application has completed loading.

    public void OnStartupComplete(ref Array custom)

        MessageBox.Show("OnStartupComplete");

    /// Receives notification that the host application is being unloaded.

    public void OnBeginShutdown(ref Array custom)

        MessageBox.Show("OnBeginShutdown");

    private DTE2 _applicationObject;

    private AddIn _addInInstance;

}

Visual Studio 2008 可擴充性開發(三):Add-In運作機制解析(下)

每個方法的注釋說明了相應的事件何時觸發。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的結構圖(點選檢視大圖):

Visual Studio 2008 可擴充性開發(三):Add-In運作機制解析(下)

不出意外的是,結構很複雜。原因有二:首先VS本身很複雜,DTE用來表示VS中的元素,不能不複雜;其次,AOM和DTE源自COM,在.NET和COM間的互操作增強一些額外的工作。不過不用擔心,這些類封裝得非常之好,用起來還是比較容易的。

這個類型結構的頂端是DTE/DTE2,它是所有其它類型的容器。DTE主要包含5部分内容:解決方案和項目、指令(Command)、事件、文檔、調試器,通過這些,我們就能夠操作VS的方方面面(可以先看一下圖中類的名字)。在後續的随筆中,你将看到這些内容的詳細用法。

再議IDTExtensibility2接口

現在回到IDTExtensibility2接口,仔細了解一下它的各個方法。

1)OnConnection

在實作這個接口的時候,我們需要獲得DTE對象,這樣才能操作VS,這件事要在OnConnection中去做。

Visual Studio 2008 可擴充性開發(三):Add-In運作機制解析(下)
Visual Studio 2008 可擴充性開發(三):Add-In運作機制解析(下)

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啟動加載的時候才會觸發。

Visual Studio 2008 可擴充性開發(三):Add-In運作機制解析(下)
Visual Studio 2008 可擴充性開發(三):Add-In運作機制解析(下)

void OnStartupComplete(ref Array custom)

如果一個Add-In随VS啟動而加載,OnConnection并非總是進行初始化的好地方——比如,Add-In加載的較早,而Add-In需要通路的VS元件尚未加載完畢。

3)OnAddInsUpdate

Visual Studio 2008 可擴充性開發(三):Add-In運作機制解析(下)
Visual Studio 2008 可擴充性開發(三):Add-In運作機制解析(下)

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

Visual Studio 2008 可擴充性開發(三):Add-In運作機制解析(下)
Visual Studio 2008 可擴充性開發(三):Add-In運作機制解析(下)

void OnBeginShutdown(ref Array custom)

如果在一個Add-In運作的時候關閉VS,OnBeginShutDown事件會觸發。我們在這個時候可以做一些必要的清理工作。

5)OnDisconnection

Visual Studio 2008 可擴充性開發(三):Add-In運作機制解析(下)
Visual Studio 2008 可擴充性開發(三):Add-In運作機制解析(下)

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的事件和生命周期介紹完畢。

我們身在何處?

參考

《Professional Visual Studio® 2008 Extensibility》

《Working with Microsoft Visual Studio® 2005》