有關 WMI 的文章和範例已經很多了,重點記錄一下就好。包括:如何使用 WMI 來做即時的系統監控,以及使用 Chart 控制項來顯示即時監控資訊。
(嗯...我也希望能夠直接用現成的工具,但是....)
2012-07-16 更新:後來,在重構我的 WMI 類別與介面時,我將本文中的靜態類別 WmiQueryHelper 改成了非靜態類別 WmiQuery,並且進一步用 C# 定義一些類別來包裝 WMI classes。結果後來又在網路上找到一篇文章,同時發現,自已的想法跟那篇文章還蠻像的。文章在此:How To: (Almost) Everything In WMI via C# - Part 3: Hardware。
我的作法跟那篇文章的主要差別在於,我的 Reader 實作類別有很多個,而且是明白定義自己要的屬性(例如文中範例的 TotalMemoryKBytes 屬性),此作法的優點是寫 code 時會有 IntelliSense 提示,且各屬性為強型別,使用時無須再經過一次轉型或資料轉換。那篇文章則是以一個通用的 WMIReader 類別來讀取所有 WMI 類別的欄位,並且將全部的欄位塞入一個 IList<string> 中保存;此作法的好處就是更為「通用」,而且可以少寫許多自訂 WMI 類別屬性的程式碼;而其缺點則是由於欄位資料皆以字串來儲存,取用時可能需要自行轉換成適當的資料型別。此外,那篇文章所包裝的 WMI 類別將近 30 個,比我目前實作完的多太多了。需要使用 WMI 偵測系統各項資訊的朋友不妨參考該文,可能更省時間。
下圖是我的初版範例程式的執行結果:
如果你打開 Windows 的資源監視器,也會看到類似的東西。此折線圖的 X 軸分成 60 個單位,代表 60 秒,因為我要每秒取一次 CPU 和記憶體的使用率。第一次抓取的 CPU 和記憶體使用率會從折線圖的最右方出現,然後每隔一秒再取一次 sample,連成線段。於是,折線圖會每隔一秒整個往左移動一個單位。
這裡的圖表是用 .NET 4 的 Chart 控制項來呈現。用法就不細說了,請參考官方文件之入門教學,以及 Spline Chart 和 Spline Area Chart。
如果不喜歡這個控制項,或者想要自己動手寫一個,可以參考這篇文章:Simple Performance Chart。
WMI
透過 WMI,你可以查詢到幾乎所有你想要知道的系統資訊,舉凡系統資源、網路通訊、磁碟檔案、MSMQ…等,什麼碗糕都有。
WMI 提供的 API 也很容易使用,你可以利用類似 SQL 的語法--叫做 WQL--來查詢你要的資訊。然而,就像對關連式資料庫查詢一樣,如果資料庫裏面的 table 數量有好幾百個,你得先知道 table 的名稱,才有辦法查到你想要的資料。WMI 也是這樣,你得知道哪些 WMI Classes有你需要的資訊。而且,查詢結果可能包含很多欄位,所以你還得知道那些欄位是你想要的。取得欄位值之後,又可能得做些運算,才能得出有意義的數值,例如 CPU 的總使用率。
所以,如果你也需要使用 WMI,底下這幾個工具和資源都會很有用:
知道有甚麼工具可用,以及去哪裡找資源,剩下寫程式的部分就容易多了。底下是一個簡易的輔助工具類別,可向某主機(hostName 參數)查詢指定的 WMI class( className 參數)。使用前,專案要先加入組件參考:System.Management.dll,程式碼要引用命名空間 System.Management。
其中的 Run 方法需要傳入四個參數。前三個參數都很容易明白,就不多解釋。最後一個參數的型別是 IWmiQueryResultReader 介面,目的是為了將讀取查詢結果的動作抽離出去。也就是說,我們可以有好多個實作此介面的類別,不同的類別負責解讀不同的查詢結果,並提供相關的屬性,例如 CPU 的實作類別可能就會提供 CpuID、Name、NumberOfCores 等屬性。
IWmiQueryResultReader 介面的原型如下:
然後,我們可以為各種 WMI Classes 撰寫不同的 reader 實作類別。例如解讀 CPU 資訊的 WmiProcessor,取得記憶體的 WmiMemory,取得 MSMQ 狀態的,也許就命名為 WmiMsmq。
底下僅列出 WmiMemory 類別的部分程式碼:
這樣的設計不見得很好,簡單的場合或作為參考範例還算堪用。
喔對了,抓取資料(透過 WMI)和顯示資料(利用 Chart 控制項)這兩項工作,我是用不同的執行緒來分別處理。如果用同一條執行緒,UI 會變得遲緩,尤其當你查詢的目標是遠端主機的時候,可能更明顯。
順便附上取得 CPU 總負載率的 WQL:
延伸閱讀
2012-07-16 更新:後來,在重構我的 WMI 類別與介面時,我將本文中的靜態類別 WmiQueryHelper 改成了非靜態類別 WmiQuery,並且進一步用 C# 定義一些類別來包裝 WMI classes。結果後來又在網路上找到一篇文章,同時發現,自已的想法跟那篇文章還蠻像的。文章在此:How To: (Almost) Everything In WMI via C# - Part 3: Hardware。
我的作法跟那篇文章的主要差別在於,我的 Reader 實作類別有很多個,而且是明白定義自己要的屬性(例如文中範例的 TotalMemoryKBytes 屬性),此作法的優點是寫 code 時會有 IntelliSense 提示,且各屬性為強型別,使用時無須再經過一次轉型或資料轉換。那篇文章則是以一個通用的 WMIReader 類別來讀取所有 WMI 類別的欄位,並且將全部的欄位塞入一個 IList<string> 中保存;此作法的好處就是更為「通用」,而且可以少寫許多自訂 WMI 類別屬性的程式碼;而其缺點則是由於欄位資料皆以字串來儲存,取用時可能需要自行轉換成適當的資料型別。此外,那篇文章所包裝的 WMI 類別將近 30 個,比我目前實作完的多太多了。需要使用 WMI 偵測系統各項資訊的朋友不妨參考該文,可能更省時間。
下圖是我的初版範例程式的執行結果:
如果你打開 Windows 的資源監視器,也會看到類似的東西。此折線圖的 X 軸分成 60 個單位,代表 60 秒,因為我要每秒取一次 CPU 和記憶體的使用率。第一次抓取的 CPU 和記憶體使用率會從折線圖的最右方出現,然後每隔一秒再取一次 sample,連成線段。於是,折線圖會每隔一秒整個往左移動一個單位。
這裡的圖表是用 .NET 4 的 Chart 控制項來呈現。用法就不細說了,請參考官方文件之入門教學,以及 Spline Chart 和 Spline Area Chart。
如果不喜歡這個控制項,或者想要自己動手寫一個,可以參考這篇文章:Simple Performance Chart。
WMI
透過 WMI,你可以查詢到幾乎所有你想要知道的系統資訊,舉凡系統資源、網路通訊、磁碟檔案、MSMQ…等,什麼碗糕都有。
WMI 提供的 API 也很容易使用,你可以利用類似 SQL 的語法--叫做 WQL--來查詢你要的資訊。然而,就像對關連式資料庫查詢一樣,如果資料庫裏面的 table 數量有好幾百個,你得先知道 table 的名稱,才有辦法查到你想要的資料。WMI 也是這樣,你得知道哪些 WMI Classes有你需要的資訊。而且,查詢結果可能包含很多欄位,所以你還得知道那些欄位是你想要的。取得欄位值之後,又可能得做些運算,才能得出有意義的數值,例如 CPU 的總使用率。
所以,如果你也需要使用 WMI,底下這幾個工具和資源都會很有用:
- WMI Tester。Windows 內建的工具,只要輸入 wbemtest 就可以開啟。
- WMI Code Creator (雖然年代有點久,還是可以用)
- WQL Query Runner (開源工具,裡面有附一個蠻好用的 WQL 編輯器)
- 官方提供的 WMI Classes 清單。前面圖中顯示的 CPU 和記憶體使用率,可以在 Win32 Classes 底下找到。
知道有甚麼工具可用,以及去哪裡找資源,剩下寫程式的部分就容易多了。底下是一個簡易的輔助工具類別,可向某主機(hostName 參數)查詢指定的 WMI class( className 參數)。使用前,專案要先加入組件參考:System.Management.dll,程式碼要引用命名空間 System.Management。
public static class WmiQueryHelper { public static void Run(string hostName, string userName, string password, IWmiQueryResultReader reader) { StringBuilder sb = new StringBuilder(); if (String.IsNullOrEmpty(hostName)) { hostName = "localhost"; } string scopeString = String.Format(@"\\{0}\root\cimv2", hostName); ManagementScope scope = new ManagementScope(scopeString); if ("127.0.0.1".Equals(hostName) == false && "localhost".Equals(hostName) == false) { ConnectionOptions co = new ConnectionOptions(); co.Username = userName; co.Password = password; co.Authentication = AuthenticationLevel.None; co.Impersonation = ImpersonationLevel.Impersonate; scope.Options = co; } scope.Connect(); ObjectQuery query = new ObjectQuery(reader.QueryString); using (ManagementObjectSearcher moSearcher = new ManagementObjectSearcher(scope, query)) { ManagementObjectCollection results = moSearcher.Get(); reader.Read(results); } } }
其中的 Run 方法需要傳入四個參數。前三個參數都很容易明白,就不多解釋。最後一個參數的型別是 IWmiQueryResultReader 介面,目的是為了將讀取查詢結果的動作抽離出去。也就是說,我們可以有好多個實作此介面的類別,不同的類別負責解讀不同的查詢結果,並提供相關的屬性,例如 CPU 的實作類別可能就會提供 CpuID、Name、NumberOfCores 等屬性。
IWmiQueryResultReader 介面的原型如下:
public interface IWmiQueryResultReader { string QueryString { get; } void Read(ICollection results); }
然後,我們可以為各種 WMI Classes 撰寫不同的 reader 實作類別。例如解讀 CPU 資訊的 WmiProcessor,取得記憶體的 WmiMemory,取得 MSMQ 狀態的,也許就命名為 WmiMsmq。
底下僅列出 WmiMemory 類別的部分程式碼:
public class WmiMemory : IWmiQueryResultReader { public string QueryString { get { return "select TotalVisibleMemorySize, TotalVisibleMemorySize from Win32_OperatingSystem"; } } public void Read(ICollection results) { foreach (ManagementObject mo in results) { TotalMemoryKBytes = Convert.ToInt64(mo["TotalVisibleMemorySize"]); AvailableMemoryKBytes = Convert.ToInt64(mo["FreePhysicalMemory"]); } } public long TotalMemoryKBytes { get; private set; } public long AvailableMemoryKBytes { get; private set; } public int Usage { get { return (int) UsedMemoryKBytes * 100 / TotalMemoryKBytes; } } }
這樣的設計不見得很好,簡單的場合或作為參考範例還算堪用。
喔對了,抓取資料(透過 WMI)和顯示資料(利用 Chart 控制項)這兩項工作,我是用不同的執行緒來分別處理。如果用同一條執行緒,UI 會變得遲緩,尤其當你查詢的目標是遠端主機的時候,可能更明顯。
順便附上取得 CPU 總負載率的 WQL:
select PercentProcessorTime from Win32_PerfFormattedData_PerfOS_Processor where Name='_Total'
延伸閱讀
- VB.Net 效能監視器運用 (數位儀表板) (這個 UI 好酷啊!)
- Getting CPU Usage in a Multiprocessor Machine
- An Implementation of System Monitor
- Monitoring Your Windows Server Metrics with WMI (這篇文章有明確指出各項系統資訊所對應的 WMI 類別及屬性名稱)
- Google 關鍵字:wmi processor total usage。
沒有留言: