MMC Snap-in 筆記 (2)

目前寫的 MMC snap-in 已經有點樣子,總算對 MMC snap-in 的程式設計模型有些粗淺的了解,也累積了一些可供日後參考的程式碼。作為上一篇 MMC 筆記的續集,這篇可能會稍嫌雜亂,一方面因為大部分都是隨手記下,加以拼湊;另一方面,最近總覺得時間不太夠用,文章無暇細琢。但如果你是第一次跟 MCM snap-in 打交道,我想裡面有些東西應該會有幫助。當然啦,MSDN 上面的文件最好還是盡量 K,以免遺漏一些關鍵細節,浪費許多摸索和試誤的時間(說我自己啦!)。

MMC Snap-in 的主要元素

先了解 MMC snap-in 的框架和主要元素,寫程式的時候才會比較有方向。參考下圖:


你可以看到,基本上就是把一些管理的項目放在左邊的樹狀結構,當使用者點選樹狀結構中的某個節點(node),中間地帶的 result pane(結果面板)就會顯示該節點所對應的 view。此外,node 和 view 都可能有一些對應的操作,需要讓使用者方便點擊執行,所以右邊的 actions pane 就是用來放這些動作選項。

現在我們知道了,當你要設計一個 MMC snap-in 時,首先要決定哪些東西要擺進左邊的樹狀結構,以及它們的層次要怎麼安排。接著就是當使用者點擊某個 node 時,要以什麼方式呈現,比如說,以 ListView 來顯示其子節點的清單(這是預設的展現方式),或者嵌入一個 Windows Forms 的 user control,或者顯示一個網頁,甚至以 WPF 來呈現。這個中間區域的 view 是很多樣化的,隨你怎麼安排。

Node 與 View

開始寫程式時,至少要先知道兩個類別:ScopeNode 和 ViewDescription。

ScopeNode 可用來建立樹狀結構中的 node 物件。你可以直接使用 ScopeNode 類別,也可以從 ScopeNode 衍生新的子類別,以便加入你需要的屬性和方法。

當使用者點選某個 node 時,就會顯示該 node 所對應的預設 view。既然有預設 view,那就代表一個 node 可以有多種 view 囉?Yes!

在設計 view 的時候,得先知道 MMC 提供哪些類型的 view,共有下列幾種:

  • MmcListView:可在 result pane 中顯示一堆項目清單。
  • FormView:可讓你在 result pane 中嵌入 Windows Forms 或 WPF 控制項。
  • HtmlView:可嵌入一個網頁。
  • MessageView:用來顯示標準的 MMC 訊息。

比如說,如果你想用 Windows Forms 或 WPF 控制項來設計 UI,就選擇 FormView。選擇 FormView 的意思是寫一個 FormView 的子類別,以便提供額外的屬性或方法。

寫好了你的 FormView 類別,不是直接塞給 ScopeNode 物件,而是得先用一個 ViewDescription 物件將你的 FormView 物件包起來,然後才塞進 ScopeNode 物件的 ViewDescriptions 集合中。如欲指定 node 的預設 view,可設定該 node 物件的 ViewDescriptions.DefaultIndex 屬性。

偷懶一下,程式碼就不貼了,請看這裡:How-To Create a Snap-in That Uses WinForm View

剛開始練習時,可以直接使用現成的 ScopeNode 類別來建立樹狀節點,而且不去管它要用什麼 View。先觀察這種陽春設計的 UI 會是什麼樣子,然後,還是用現成的 ScopeNode,但這次加入自訂的 View 類別。讓節點跟某個自訂 view 綁在一起。觀察執行結果,試改一些 code,以確實了解 ScopeNode 跟 View 的關係。

接著設計不同的 views,讓一個節點有多種 view 可供使用者切換。這個部分可以參考微軟網站上的教學文件:How-To Develop Snap-ins Using MMC。你可以先試試 MMCLsitView,然後 FormView。

然後,你可能會發現,node 和 view 之間必須要能互相傳遞資料,至少 view 物件要能夠取得 node 物件的某些資訊。比如說,每個節點代表一台機器,它得提供 IP 位址、電腦名稱等資訊。於是你寫了一個類別,繼承自 ScopeNode,例如 ServerScopeNode,並且在此類別中提供額外的屬性:IPAddress、ComputerName  等等。於是,在你的 view 類別裡,便可透過 Node 屬性來取得與之關聯的節點物件,將它手動轉型為 ServerScopeNode,便可以取得你先前存入 node 物件的額外屬性。(謎之音:沒 sample code 誰看得懂這段繞口令在說啥啊!)

底下是一些實作時碰到的問題或值得注意的地方,大多和 FormView 有關。

FormView 的初始化過程

在使用 FormView (或繼承自 FormView 的自訂類別)時,我們會設計一個自訂的 user control 來嵌入這個 form。當使用者透過 MMC 管理介面開啟這個 view 時,會先觸發我們的 user control 的 Load 事件,然後才會呼叫 FormView 的 OnInitialize 方法。

進一步說,user control 的初始化過程如下:
  1. 執行 user control 的類別建構子。
  2. 執行 user control 的 Initialize 方法。此方法定義於 Microsoft.ManagementConsole.IFormViewControl。我們的 user control 除了繼承 UserControl 類別,還要實作這個 IFormControl 介面。實作該介面的 Initialize 方法,我們就可以從該方法的 View 參數來取得此 user control 外層的 FormView 物件。然後,如果需要的話,又可以透過該 FormView 物件取得其關聯的 Node 物件。
  3. 觸發 Load 事件。

類別名稱衝突

Microsoft.ManagementConsole 命名空間裡面的類別 Action 跟 System 命名空間的 Action 類別名稱相同,為了避免名稱衝突,又不用寫一長串的類別全名,我在程式碼的上方加入兩行 using 敘述:

using Microsoft.ManagementConsole;
using MMC=Microsoft.ManagementConsole;

這樣的話,碰到名稱衝突時,就用 MMC.* 來指明使用 Microsoft.ManagementConsole 命名空間裡的類別。例如:MMC.Action。

多執行緒的注意事項

MMC 是 MDI 應用程式,因此儘管使用者同時間只操作或看到其中一個 view,但其實背後可能已經同時開啟多個 view。如果這些 view 物件可能又有額外建立多個工作執行緒,那麼同時間可能會有很多執行緒在運行。

在我的 MMC snap-in 中,我同時使用了 System.Windows.Forms.Timer 和 System.Timers.Timer 來分別負責 UI 更新以及背景資料讀取的工作。如果使用者開了很多個 view,背後會有好多執行緒在跑。

對於這種多執行緒的場合,就得特別注意減輕不必要的負荷,並且確實做好資源回收的工作。當使用者關閉 MMC snap-in 時,要先把所有的工作執行緒結束掉,否則 MMC 可能會出現錯誤訊息或丟出 ThreadAbortException(執行緒被強迫終止)。

如何得知 FormView 已不可見

當使用者從節點 A 的 view 切換到另一個節點 B 或其他 view 之後,節點 A 裡面的執行緒其實還在背後持續運作。為了避免無謂的 UI 更新或減少執行緒的處理負擔,我們會需要偵測目前的 FormView 物件是否仍為可見。

作法很簡單:改寫 FormView 的 OnHide 和 OnShow 虛擬方法,並在其中設定其內含之使用者控制項的 Visible 屬性。例如:

protected override void OnHide()
{          
    base.OnHide();
    this.Visible = false;
}

protected override void OnShow()
{
    base.OnShow();
    this.Visible = true;
}

使用者控制項如需在 Visible 屬性變更時立即做出對應處置,則可訂閱自己的 VisibleChanged 事件。

如何以程式碼點選特定 ScopeNode 

View 物件有提供 SelectScopeNode 方法,讓你用程式碼來將目前的焦點切到指定的 node,就像使用者用滑鼠點選某個節點的效果。

以 FormView 來說,實際的 UI 控制項都是放在一個 user control 裡面,可是 user control 就只是一般的 UserControl 子類別,它沒有 SelectScopeNode 方法,所以如果想要讓 user control 中的某個按鈕被按下去時切換到特定的 node,我的作法是在我的 user control 類別中 expose 一個事件,例如:

public event Action<string> Button1Clicked;

然後,在我的自訂 FormView 類別中訂閱 user control 的 Button1Clicked 事件(FormView 類別有個 Control 屬性可取得嵌入其中的控制項),例如:

public class SelectionFormView : FormView
{
    private MyUserControl myControl = null;

....

    protected override void OnInitialize(AsyncStatus status)
    {
        base.OnInitialize(status);

        // Get a typed reference to the hosted control
        // that is set up by the form view description.
        myControl = (MyUserControl)this.Control;
        myControl += new Action<string>(ControlButtonClicked)
    
        Refresh();
    }

    void ControlButtonClicked(string nodeName)
    {
        // 這裡用比較"純樸"的方法尋找樹狀結構中的特定節點.
        ScopeNode parentNode = this.ScopeNode.Children[0];
        for (int i = 0; i < Node.Children.Count; i++ )
        {
            MyScopeNode aNode = parentNode.Children[i] as MyScopeNode;
            if (aNode != null)
            {
                if (aNode.Name.Equals(nodeName))
                {
                    this.SelectScopeNode(aNode);
                }
            }
        }
    }
}

小結

用來寫一些管理類型的工具,MMC snap-in 真是挺好用的。這篇筆記還有不少東西沒提到,例如 property page、action 等。

先這樣吧!日後有空再回頭補充。


延伸閱讀

沒有留言:

技術提供:Blogger.
回頂端⬆️