WMI介紹

1、WMI是什麼?
WMI——Windows管理規範(Windows Management instrumentation)。
  • 是一項核心的Windows管理技術。
  • 採用統一的、基於開放標準的、可擴充套件的面向物件介面的系統管理基礎結構。
  • 支援指令碼程式設計,使管理員的日常管理自動化。
  • 提供了統一的機制讓使用者來管理本地和遠端的計算機。
 
2、WMI和WBEM
WBEM——基於Web的企業管理規範(Web-Based Enterprise Management)。
  • 由微軟在1996年首先提出,由DMTF(Distributed Management Task Force,分散式管理任務組)管理維護。
  • 其資料模型稱為CIM(Common Information Model,公共資訊模型),是一個面向物件的模型,統一定義了一組類和名稱空間。
WMI是微軟實現的WBEM版本
 
3、WMI能做什麼?
WMI可以使使用者通過程式設計、指令碼或各類已有工具來訪問其提供的功能和服務。
例如:
  • 在計算機上啟動一個程序。
  • 設定在特定時間執行特定服務。
  • 遠端關閉計算機。
  • 當磁碟餘額小於預定值時記錄到事件日誌。
  • 管理、監測IIS/SQL/Exchange/SMS/MOM等微軟系列產品。
  • 管理提供相應WMI Provider的第三方應用。
  • ……
 
4、WMI指令碼
WMI和WMI指令碼
使用VBScript等指令碼語言和WSH,就可以寫出通過WMI進行系統管理的指令碼。
指令碼是管理員利用WMI的最主要方式,所以很多時候提到WMI即指WMI指令碼。
 
示例:檢視C盤的可用容量,單位B
Set wmi = GetObject("winmgmts:\\")
Set objDisk = wmi.get("Win32_LogicalDisk.DeviceID='C:'")
Wscript.Echo objDisk.FreeSpace
 
5、WMI架構圖
 
6、WMI指令碼的基本結構
嚴格說來,WMI由四部分組成:
1. 公共資訊模型物件管理器——CIMOM
2. 公共資訊模型——CIM
3. WMI提供程式
4. WMI指令碼物件庫
 
其中其第1、2、3三個部分,在使用中很少加以區別,我們一般統稱為CIM庫。
 
所以我們可以認為WMI實際是由兩部分組成:CIM庫和WMI指令碼物件庫。在具體使用過程中,我們是通過WMI指令碼物件庫去訪問CIM庫,管理託管的資源。也就是說,在我們編寫指令碼的過程大致可以分為這麼幾步:
1. 建立WMI物件指令碼庫的指標例項;
2. 呼叫其例項的方法,連線到CIM庫,並指明需要訪問的資源的邏輯位置;
3. 獲得託管資源也就是類的例項的集合;
4. 列舉例項,完成工作。
這幾個步驟在我們將來編寫的程式碼中可以明確的反映出來。
 

名稱空間與路徑

在WMI內部,使用了類似於驅動器中的資料夾與檔案的管理方式。名稱空間類似於資料夾,其中的CIM類則類似於資料夾。所有的CIM類都被分門別類地歸入相應的名稱空間,同一名稱空間內不允許有重名的類存在,不同的兩上名稱空間下則允許重名的類出現。整個空間的根是名為root,其路徑也採用了類似於驅動器路徑的表達方式,比如\\MyWorkStation\root\cimv2:Win32_Processor就表示了MyWorkStation這臺主機中root下cimv2空間中的Win32_Processor這個CIM類。這樣的路徑表示支援相對路徑、絕對路徑的表示方法,以當前連線的主機、當前開啟的空間作為相對路徑的原點,並忽略路徑名稱的大小寫。
這個名稱空間的結構可以通過一個類似於WMI CIM Studio這樣的工具檢視,還可以輸入互動式的WQL語句進行聯機查詢。同一名稱空間中的類保持了相應的繼承關係,我們也可以新增自定義的名稱空間和類。
 
據微軟稱,WMI的名稱空間共有16個,不過不用擔心,我們常用的只有兩個:
1. root\cimv2 在這個名稱空間裡包括了絕大多數與計算機、作業系統相關聯的類。
2. root\default 管理登錄檔的類
 

常用的指令碼物件庫

WMI指令碼物件庫由24個物件組成,在指令碼中心有一副指令碼庫物件模型的圖,作為入門,我們一般只用到其中的四個物件,其繼承和層級關係如下:
SwbemLocator教本庫物件→SwbemServicesWMI服務物件→SwbemObjectSet類例項集合物件→SwbemObject類的例項
現在讓我們來舉個例子,詳細說明一下這四個物件在指令碼中的應用方法:
例一:用來檢索計算機上安裝的光碟機:
Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator")
Set objSWbemServices = objSWbemLocator.ConnectServer
Set colItems = objSWbemServices.ExecQuery("Select * from Win32_CDROMDrive")
For Each objItem in colItems
WScript.Echo "光碟驅動器的型別: " & objItem.Caption
WScript.Echo "碟符是: " & objItem.Id
Next
例二:用來檢索CPU型號
Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator")
Set objSWbemServices = objSWbemLocator.ConnectServer
Set objSWbemObjectSet = objSWbemServices.InstancesOf("Win32_Processor")
For Each objSWbemObject In objSWbemObjectSet
Wscript.echo "CPU的型號為:" & objSWbemObject.name
Next
 
1、WMI服務的連線方法
方法一:
步驟一、建立SwbemLocator物件的例項。程式碼為:
Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator")
然後用SwbemLocator物件的ConnectServer方法(SwbemLocator物件只有1個只讀屬性Security_和1個方法ConnectServer)建立WMI服務的連線,返回一個名稱空間的連線(SwbemServices物件),程式碼為:
Set objSWbemServices = objSWbemLocator.ConnectServer()
ConnectServer方法共有8個引數,所有引數都是可選的,其引數格式如下:
ConnectServer([strComputName],[strNamespace],[strUser],[strPassword],[strLocale],[strAuthority],[iSecurityFlags],[objwbemNamedValueSet])
 
考慮到WMI的複雜性,在使用中我們如果只是在本地計算機上進行檢索和查詢,那麼我們只需要設定第1、2個引數,其它引數都可以省略;如果想連線到遠端計算機,一般需要對前4個引數進行設定,我們也只對此做個簡單的介紹。
strServer——計算機名,預設為本機,本機也可以用”.”
strNamespace——需要登入的CIM名稱空間,例如:"root\CIMV2",預設為"root\CIMV2"。
 
方法二:用moniker名字法建立WMI服務的連線,這也是微軟推薦的連線方法
moniker名字法是利用GetObject函式直接建立WMI服務的連線,它的要點就是通過編寫一個moniker字串作為GetObject函式的引數,然後返回一個SwbemServices物件。
關於moniker字串的完整格式如下:
"winmgmts:[{SecuritySettings}!][\\ComputerName][\Namespace][:ClassName][.KeyProperty='Value']"
"winmgmts:"是字首,表示為WMI服務,必須使用;
第二部分用來驗證許可權和假冒級別的,省略。
第三部分為計算機名字:"\\.\"是計算機名字,預設可省略,其餘同上;
第四部分CIM名稱空間:預設的名稱空間為"root\CIMV2",預設可省略。
第五部分為類名。
第六部分為屬性值。注意:當該moniker字串不包括最後2項時(即為:"winmgmts:[\\ComputerName][\Namespace]"),則GetObject(moniker字串)返回的是一個名稱空間的已驗證的連線(SwbemServices物件);當不包括最後1項時,返回的是一個CIM類(SWbemObject物件);當包括最後2項時,返回的是一個類的單獨例項(SWbemObject物件)。
常見的moniker名字法建立WMI服務的連線:
1.連線到本機
Set objSWbemServices = GetObject("winmgmts:")
2.連線到遠端計算機(預設名稱空間)
Set objSWbemServices = GetObject("winmgmts:\\atl-dc-01\")
3.連線到遠端計算機的特定名稱空間
Set objSWbemServices = GetObject("winmgmts:\\atl-dc-01\root\default")
4.連線到遠端計算機的特定類
Set objSWbemServices = GetObject("winmgmts:\\atl-dc-01\root\cimv2:Win32_OperatingSystem")
5.連線到遠端計算機的特定例項
Set objSWbemServices = GetObject_
("winmgmts:\\atl-dc-01\root\cimv2:Win32_LogicalDisk.DeviceID='C:'")
 
2、獲得類例項的方法
我們有4種方法獲得類的例項,其中方法1和方法2是通過SwbemServices物件的InstancesOf方法和ExecQuery方法來獲得某個類的多個例項組成的集合物件。方法3和方法4則是返回單獨的類的例項,即返回的是一個SWbemObject物件。
1)InstancesOf方法獲得類的例項集合
 
InstancesOf方法的語法引數格式如下:
SwbemServices.InstancesOf(strClass)
strClass為類名,例如"Win32_Service"
回顧例二,就是用語句:Set objSWbemObjectSet = objSWbemServices.InstancesOf("Win32_Processor") 來獲得"Win32 Processor "類的所有例項集合,然後我們可以用
For Each objSWbemObject In objSWbemObjectSet
……
Next
語句獲得每一個類的例項SWbemObject物件,然後就可以根據我們的需要,進行相應的操作。
 
2)ExecQuery方法獲得類的例項集合
與InstancesOf方法不一樣的是,ExecQuery方法可以通過查詢語句,只返回匹配部分例項和屬性。ExecQuery方法的語法引數格式如下:
SwbemServices.ExecQuery(strQuery)
strQuery為WMI查詢語言(WQL)構造的一個查詢語句字串。
例如:
Set objSWbemObjectSet = objSWbemServices.ExecQuery("select ProcessorId from Win32_Processor where DeviceID='cpu0'")
 
3)Get方法獲得一個類的例項(SWbemObject物件)
此方法也就不必再用 For Each objSWbemObject In objSWbemObjectSet :……:Next
語句從SWbemObjectSet物件中獲得每一個類的例項SWbemObject物件,Get方法的語法引數格式如下:
SwbemServices.Get([strObjectPath][.KeyProperty='Value'])
strObjectPath是類的名字
KeyProperty是主鍵屬性名
Value是指定的主鍵屬性值
這裡要注意的是如果要獲得一個類的例項,則strObjectPath.KeyProperty='Value'中的任何一項都不能省略,例如:
Set objSWbemServices = GetObject("winmgmts:")
Set objSWbemObject = objSWbemServices.Get("Win32_Processor.DeviceID='cpu0'")
Wscript.echo “CPU的型號為”:" & objSWbemObject.ProcessorId
結果一樣,指令碼卻簡化了不少。
 
4)直接用moniker名字法獲得一個類的例項
在說明Moniker名字法的時候我們說過,當包括最後2項時,返回的是一個類的單獨例項,如:Set objSWbemObject = GetObject("winmgmts:Win32_Processor.DeviceID='cpu0'")
Wscript.echo "首枚CPU序列號:" & objSWbemObject.ProcessorId
是不是更加簡單?僅僅2條語句就獲得了CPU的序列號。
 

WQL查詢語言

1、WQL簡介
WQL就是WMI中的查詢語言,WQL全稱是WMI Query Language,簡稱為WQL,翻譯成中文好像可以成為Windows管理規範查詢語言。熟悉SQL語言會感覺它和SQL非常相似。
WQL其實非常簡單,它有如下特點:
  • 每個WQL語句必須以SELECT開始
  • SELECT後跟你要查詢的屬性名(對應SQL的欄位名),也可以像SQL一樣,以*表示所有屬性值
  • FROM後跟關鍵字
  • 查詢的類名字
  • 另外,如果你想精確查詢結果還可以加上WHERE條件從句。比如某個類有Enable屬性,你可以在查詢的時候加上WHERE ENABLE=true。
  • WQL是SQL的一個子集,但不支援更新、刪除和排序等操作,可用於返回選定例項的選定屬性
 
2、WQL詳述
1.語法
SELECT properties[,properties] FROM class [where clause]
 
2.引數
  • SELECT 必選項。代表WQL語句的開始,
  • properties 必選項。代表想查詢的屬性名字。可以是多個屬性名,也可查詢所有屬性值,用*代替。
  • FROM 必選項。跟在properties的後面。
  • Class 必選項。代表想要查詢的類的名稱。
  • where clause 可選項。where 從句和相關條件語句,用來縮小查詢範圍。
 
2.邏輯運算
  • AND運算子,連線兩個邏輯運算,當同時滿足條件時通過。
  • OR運算子,連線兩個邏輯運算,當至少一個滿足條件時通過。
例子:
SELECT * FROM Win32_LogicalDisk WHERE (DriveType = 2) OR (DriveType = 3 AND FreeSpace < 1000000)
 
4.比較運算
  • =                等於
  • >               大於
  • <             小於
  • <=      小於等於
  • >=      大於等於
  • <> 或 != 不等於
  • Is [not]      [不]是,僅僅用於比較NULL的時候
例子:
SELECT * FROM Win32_LogicalDisk WHERE FileSystem IS NULL
SELECT * FROM Win32_LogicalDisk WHERE FileSystem IS NOT NULL
SELECT * FROM Win32_LogicalDisk WHERE DriveType IS 5
SELECT * FROM Win32_LogicalDisk WHERE FileSystem IS NOT "NTFS"
 
5.Like運算
描述:模糊匹配查詢的條件
萬用字元
  • "%"代表一個或者多個字元。 例如,%Office%可代表"My Offices," "Office VPN,"或"Office." Office%可代表"Offices"或"Office VPN,"但是不能代表"My Offices,"
  • "[ ]”返回引數範圍。 例如"[A-Z]ars"可代替"Mars," "Wars," and "Tars,"但是不能代替"Stars."
  • "^"取範圍的反面。 例如"[^A-M]ars"可代替"Wars"和"Tars,"但是不能代替"Mars"因為"M"不再指定範圍內。
  • "_"代替單個字元。 "M_rs"可代替"Mars," "M3rs,"
 
6.判斷運算
描述:有的類的屬性只有TRUE和FALSE兩種狀態,這個時候可以使用TRUE和FALSE來判斷,記住這個時候不能使用is TRUE來判斷,因為他只適用於判斷NULL。
例子:
SELECT * FROM Win32_NetworkAdapterConfiguration WHERE DHCPEnabled = TRUE
 
7.相關查詢
描述:檢索與指定內容相關的所有例項並且返回查詢結果。
語法:ASSOCIATORS OF
引數:
ASSOCIATORS OF {描述}
ASSOCIATORS OF {ObjectPath}
說明:
假設有四個例項,A,B,X和Y,其中A與X相關,B與Y相關
執行ASSOCIATORS OF {A}僅僅會返回一個X,如果還有其他的關係,也許會返回兩個或者以上的例項。
例子:
Query:
ASSOCIATORS OF {Win32_LogicalDisk.DeviceID="C:"}
Results:
Win32_Directory.Name="C:\\"
Win32_ComputerSystem.Name="mycomputer"
Win32_DiskPartition.DeviceID="Disk #0, Partition #0"
 
其他WQL關鍵字
  • __CLASS 在查詢結果集中物件繼承的類物件引用。
  • GROUP Clause GROUP子句使WMI生成一個表示一組事件的通知。
  • ISA ISA運算子是WQL特定的運算子,可以在事件查詢中使用。當ISA包含在事件查詢的WHERE子句中時,它將請求類層次結構中而不是特定事件類中所有類的事件通知。
  • WITHIN 指定輪詢的間隔或者分組區間,一般在event查詢中使用此子句。
  • REFERENCES OF 獲取所有引用特定源例項的關聯例項集,一般在schema和data查詢中使用,REFERENCES OF語句與ASSOCIATORS OF 語句類似,。
  • KEYSONLY 在REFERENCES OF和ASSOCIATORS OF 查詢中用來確認返回的結果例項集中只有關鍵的例項/屬性被填充。以此來降低查詢的呼叫成本。
 
示例1:查詢服務(Win32_Service)中所有正在執行的例項的服務名稱、服務描述
strComputer = "."
Set objSWbemServices = GetObject("winmgmts:\\"&strComputer&"\root\cimv2")
Set colServices = objSWbemServices.ExecQuery("SELECT * FROM Win32_Service Where state = 'Running'")
WScript.Echo "服務名稱---服務描述"
For Each objService In colServices
WScript.Echo objService.name&"---"&objService.Description
Next
 
示例2:查詢賬戶(Win32_Account)中所有例項的賬戶名稱、賬戶描述、賬戶SID
strComputer = "."
Set objSWbemServices = GetObject("winmgmts:\\"&strComputer&"\root\cimv2")
Set colAccounts = objSWbemServices.ExecQuery("SELECT * FROM Win32_Account")
WScript.Echo "賬戶名稱---賬戶描述---賬戶SID"
For Each objAccount In colAccounts
WScript.Echo objAccount.name&"---"&objAccount.Description&"---"&objAccount.SID
Next
 
示例3:查詢當前程序的名稱、PID、佔用記憶體空間(單位MB)
strComputer = "."
Set objSWbemServices = GetObject("winmgmts:\\"&strComputer&"\root\cimv2")
Set colProcess = objSWbemServices.ExecQuery("SELECT * FROM Win32_Process")
WScript.Echo "程序名稱---PID---所佔記憶體"
For Each objProcess In colProcess
m = Int(CSng(objProcess.WorkingSetSize)/1024/1024*100)/100
WScript.Echo objProcess.name&"---"&objProcess.ProcessId&"---"& CStr(m)&"MB"
Next
 

WMI事件監控與處理

很多人在平常會有事件監控的需要,如監控程序的執行監視移動磁碟的插入、監視檔案系統的操作等等。WMI可以很容易地實現上述功能。
那麼WMI是如何實現事件監控功能的呢?原來,WMI能訪問Windows下的API元件並能接收其執行時的InstanceCreationEvent(建立或啟動事件)、InstanceDeletionEvent(刪除或關閉事件)、InstanceNotificationEvent(修改或變更事件)、InstanceOperationEvent(所有事件)的返回狀態,從而判斷事件的性質而實現對事件的監控。
 
示例1:監測是否新建了一個記事本
strComputer = "."
Set objSWbemServices = GetObject("winmgmts:\\"&strComputer&"\root\cimv2")
Set objEventSource = objSWbemServices.ExecNotificationQuery( _
"SELECT * FROM __InstanceCreationEvent " _
& "WITHIN 5" _
& "WHERE TargetInstance ISA 'Win32_Process'" _
& "AND TargetInstance.Name = 'NotePad.exe'")
Set objEventObject = objEventSource.NextEvent()
WScript.Echo "記事本已開啟"
 
事件處理的函式與方法:
ExecNotificationQuery 函式
執行查詢以接收事件。 呼叫會立即返回,並且呼叫方可以在返回的事件發生時輪詢返回的列舉器。 釋放返回的列舉器將取消查詢。
SWbemEventSource.NextEvent方法
如果事件是可用的,NextEvent所述的方法SWbemEventSource物件檢索從事件查詢該事件。
 
分析:
第1、2行,定義空間名稱並連線WMI服務
第3行,使用ExecNotificationQuery函式執行查詢接收事件
第4行,查詢建立或啟動事件類物件,注意__InstanceCreationEvent前面是兩條下劃線
第5行,WITHIN指監測的一個週期,WITH 5表示每隔5秒進行一次監測
第6、7行,將查詢範圍定在Notepad.exe程序
第8、9行,如果事件可用,即打開了Notepad.exe,輸出訊息"記事本已開啟"
 
示例2:監控U盤的狀態
On Error Resume Next
Dim objWMI, objSHELL, objEvents, objEvent
Set objWMI = GetObject("Winmgmts:\\.\Root\Cimv2")
Set objSHELL = CreateObject("Wscript.Shell")
Set objEvents = objWMI.ExecNotificationQuery _
("Select * From __InstanceOperationEvent Within 5 " _
& "Where TargetInstance Isa 'Win32_LogicalDisk' " _
& "And TargetInstance.DriveType = 2")
Do While True
Set objEvent = objEvents.NextEvent()
Select Case objEvent.Path_.Class
Case "__InstanceCreationEvent"
objSHELL.Popup "檢測到碟符號為" & objEvent.TargetInstance.Name & "的U盤插入",3,"系統提示"
Case "__InstanceDeletionEvent"
objSHELL.Popup "碟符號為" & objEvent.TargetInstance.Name & "的U盤被移除",3,"系統提示"
End Select
Loop
 
分析:
第1行,該語句的作用是,如果後面的程式出現"執行時錯誤"時,會繼續執行,不中斷的
第2行,宣告變數
第3行,定義空間名稱並連線WMI服務
第4行,宣告WshShell物件,用於後面的彈窗顯示
第5行,使用ExecNotificationQuery函式執行查詢接收事件
第6行,查詢所有事件類物件
第7、8行,從中篩選出屬於Win32_LogicalDisk元件中移動磁碟(TargetInstance.DriveType = 2)發生的屬性例項(TargetInstance)
第9、17行,設定了一個Do While…Loop迴圈實現對事件的無限監控
第10行,如果事件可用,執行後面語句
第11-16行,使用Switch選擇語句,若插入U盤,則觸發InstanceCreationEvent事件,執行第13行程式碼,彈出顯示框;若拔出U盤,則觸發InstanceDeletionEvent事件,執行第15行程式碼,彈出顯示框
 
示例3:實時監控新增程序
On Error Resume Next
Dim objWMI, objSHELL, objEvents, objEvent, Message, Return
Set objWMI = GetObject("Winmgmts:\\.\Root\Cimv2")
Set objSHELL = CreateObject("Wscript.Shell")
Set objEvents = objWMI.ExecNotificationQuery _
("Select * From __InstanceCreationEvent Within 3 " _
& "Where TargetInstance Isa 'Win32_Process'")
Do While True
Set objEvent = objEvents.NextEvent()
Message = "新程序" & objEvent.TargetInstance.Name _
& "被啟動,請在10秒內點選確認允許,否則系統將強行關閉!"
Return = objSHELL.Popup(Message,10,"系統警告",0+48)
If Return = -1 Then
Return = objEvent.TargetInstance.Terminate
If return <> 0 Then
Message = "程序" & objEvent.TargetInstance.Name _
& "關閉失敗,請嘗試手動關閉!"
objSHELL.Popup Message,3,"系統警告",0+48
End If
End If
Loop