轉載後格式變化,建議大家移步原文閱讀:http://www.cnblogs.com/dreamlofter/p/5846966.html
最近在網上搜索Python和WMI相關資料時,發現大部分文章都千篇一律,並且基本上只說了很基礎的使用,並未深入說明如何使用WMI。本文打算更進一步,讓我們使用Python玩轉WMI。
1 什麼是WMI
具體請看微軟官網對WMI的介紹。這裏簡單說明下,WMI的全稱是Windows Management Instrumentation,即Windows管理規範。它是Windows操作系統上管理數據和操作的基礎設施。我們可以使用WMI腳本或者應用自動化管理任務等。
從Using WMI可以知道WMI支持如下語言:
Application language | Topic |
---|---|
Scripts written in Microsoft ActiveX script hosting, including Visual Basic Scripting Edition (VBScript) and Perl |
Start with Creating a WMI Script. For script code examples, see WMI Tasks for Scripts and Applications and the TechNet ScriptCenter Script Repository. |
Windows PowerShell |
Getting Started with Windows PowerShell WMI PowerShell Cmdlets, such as Get-WmiObject. |
Visual Basic applications |
|
Active Server Pages |
Start with Creating Active Server Pages for WMI. |
C++ applications |
Start with Creating a WMI Application Using C++ and WMI C++ Application Examples (contains examples). |
.NET Framework applications written in C#, Visual Basic .NET, or J# |
Classes in the Microsoft.Management.Infrastructure namespace. (The System.Management namespace is no longer supported). For more information, see WMI .NET Overview. |
很遺憾,WMI並不原生支持Python。不過沒有關係,它支持VB,而Python中的兩個第三方庫wmi和win32com,均能以類似VB的用法來使用。那麼接下來,我們來講講如何使用。
2 使用WMI
2.1 使用wmi庫操作WMI
以下是一個遍歷所有進程,所有服務的示例:
import wmi c = wmi.WMI () # 遍歷進程 for process in c.Win32_Process (): print process.ProcessId, process.Name
# 遍歷服務
for service in c.Win32_Service ():
print service.ProcessId, service.Name
可以看到,使用起來非常簡單。但是有兩個問題:一是wmi庫實在是太慢了,能不能快點?二是如何知道例子中process和service有哪些屬性(比如ProcessId等)?由於wmi庫是動態生成底層執行語句,用dir(process)這種方式是獲取不到ProcessId這種屬性的。
針對第一個問題,我們可以使用win32com這個庫來解決,它相較於wmi的速度快了很多。而第二個問題,先賣個關子,後文會有介紹。
2.2 使用win32com庫操作WMI
win32com能模仿VB的行爲,想了解如何使用win32com來操作WMI,最直接的方式是瞭解如何使用VB來操作WMI。在微軟的官網上提供了很多現成的例子:WMI Tasks: Processes, WMI Tasks: Services。
其中一個例子關於進程是這樣的:
strComputer = "." Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") Set colProcesses = objWMIService.ExecQuery("Select * from Win32_Process") For Each objProcess in colProcessesWscript.Echo </span><span style="color: #800000;">"</span><span style="color: #800000;">Process: </span><span style="color: #800000;">"</span> &<span style="color: #000000;"> objProcess.Name sngProcessTime </span>= (<span style="color: #0000ff;">CSng</span>(objProcess.KernelModeTime) + <span style="color: #0000ff;">CSng</span>(objProcess.UserModeTime)) / <span style="color: #800080;">10000000</span><span style="color: #000000;"> Wscript.Echo </span><span style="color: #800000;">"</span><span style="color: #800000;">Processor Time: </span><span style="color: #800000;">"</span> &<span style="color: #000000;"> sngProcessTime Wscript.Echo </span><span style="color: #800000;">"</span><span style="color: #800000;">Process ID: </span><span style="color: #800000;">"</span> &<span style="color: #000000;"> objProcess.ProcessID Wscript.Echo </span><span style="color: #800000;">"</span><span style="color: #800000;">Working Set Size: </span><span style="color: #800000;">"</span> &<span style="color: #000000;"> objProcess.WorkingSetSize Wscript.Echo </span><span style="color: #800000;">"</span><span style="color: #800000;">Page File Size: </span><span style="color: #800000;">"</span> &<span style="color: #000000;"> objProcess.PageFileUsage Wscript.Echo </span><span style="color: #800000;">"</span><span style="color: #800000;">Page Faults: </span><span style="color: #800000;">"</span> &<span style="color: #000000;"> objProcess.PageFaults
Next
它做了這樣一件事:首先通過GetObject連接到Win32_Process所在的名稱空間,然後執行WQL語句(類似SQL的查詢語句)查到所有的進程,再把每一個進程的相關信息打印出來。WQL的具體用法請見官網,這裏不詳細介紹。
那麼用win32com就可以這麼寫(例子中打印的屬性爲了簡便,就不像上面那麼多啦):
from win32com.client import GetObject
wmi = GetObject(‘winmgmts:/root/cimv2’)
# wmi = GetObject(‘winmgmts:’) #更簡單的寫法
processes = wmi.ExecQuery(‘Select * from Win32_Process’)
for process in processes:
print(process.ProcessID, process.Name)
看上去,VB和win32com的用法非常接近!那麼當我們想要使用win32com對WMI進行操作時,就可以參考微軟官網上VB的例子,然後比葫蘆畫瓢寫出Python版的代碼。
上例中,我們使用了查詢函數ExecQuery來查詢符合條件的內容,不過如果我們僅僅是想要獲得所有的數據,而沒有特定的限定條件,就可以使用更簡單的方式——InstancesOf,那麼就可以寫成下面這樣:
from win32com.client import GetObject
wmi = GetObject(‘winmgmts:/root/cimv2’)
processes = wmi.InstancesOf(‘Win32_Process’)
for process in processes:
print(process.ProcessID, process.Name)
有讀者可能會問,我們怎麼知道自己想要了解的內容在哪個名稱空間,我們應該獲取哪個實例,又該獲取實例中的哪些屬性呢?
3 WMI的名稱空間
使用下面的腳本可以獲得當前計算機上的名稱空間:
from win32com.client import GetObject import pywintypes
def enum_namespace(name):
try:
wmi = GetObject(‘winmgmts:/’ + name)
namespaces = wmi.InstancesOf(’__Namespace’)
for namespace in namespaces:
enum_namespace(’{name}/{subname}’.format(name=name,
subname=namespace.Name))
except pywintypes.com_error:
print(name, ‘limit of authority’)
else:
print(name)
enum_namespace(‘root’)
獲得的內容大概是這樣的(...表示省略了一些輸出內容):
root
root/subscription
root/subscription/ms_409
root/DEFAULT
root/DEFAULT/ms_409
root/CIMV2
root/CIMV2/Security
...
root/Cli
root/Cli/MS_409
root/SECURITY
...
root/WMI
root/WMI/ms_409
root/directory
root/directory/LDAP
root/directory/LDAP/ms_409
root/Interop
root/Interop/ms_409
root/ServiceModel
root/SecurityCenter
root/MSAPPS12
root/Microsoft
...
通用的名稱空間的簡單介紹:
root 是名稱空間層次結構的最高級。
CIMV2 名稱空間存放着和系統管理域相關(比如計算機以及它們的操作系統)的對象。
DEFAULT 名稱空間存放着默認被創建而不指定名稱空間的類。
directory 目錄服務的通用名稱空間,WMI 創建了名爲LDAP的子名稱空間。
SECURITY 用來支持Windows 9x計算機上的WMI的名稱空間。
WMI 使用Windows Driver Model providers的類所在的名稱空間。這是爲了避免和CIMV2名稱空間中類名衝突。
其中,root/CIMV2可以說是最爲基本和常用的名稱空間了。它的作用主要是提供關於計算機、磁盤、外圍設備、文件、文件夾、文件系統、網絡組件、操作系統、打印機、進程、安全性、服務、共享、SAM 用戶及組,以及更多資源的信息;管理 Windows 事件日誌,如讀取、備份、清除、複製、刪除、監視、重命名、壓縮、解壓縮和更改事件日誌設置。
4 類/實例和屬性/值
瞭解了名稱空間的獲取,每個名稱空間的主要功能,那麼如何獲取特定名稱空間下所有的類,以及它們的屬性和值呢?
Windows提供了一個WMI測試器,使得查詢這些內容變得尤爲方便。按下"win+R",輸入wbemtest,從而打開WMI測試器。打開後的界面如下:
點擊“連接”,輸入想要查詢的名稱空間,再點擊“連接”即可連到特定名稱空間。
然後點擊“枚舉類”,在彈出的界面中選擇“遞歸”,然後點擊“確定”,就會得到這個名稱空間下所有的類:
從上圖可以看到,之前舉例中提到的Win32_Process位列其中,我們不妨雙擊它,看看關於它的具體內容:
我們可以很容易地找到Win32_Process的屬性和方法。除了使用wbemtest查看特定名稱空間下的所有類,我們還可以在WMI/MI/OMI Providers中找到所有的類。我們依次在這個頁面中點擊CIMWin32, Win32, Power Management Events,Win32 Provider,Operating System Classes,Win32_Process 最終找到Win32_Process的屬性和方法:
對比上面兩張圖,裏面的方法都是一致的。
那麼如何獲得實例和它的值呢?我們繼續在剛剛打開的wbemtest界面中點擊右邊的“實例”按鈕,就會顯示所有的進程實例。雙擊某個具體的實例,然後在彈出的界面中點擊右側的“顯示MOF”按鈕就會顯示這個實例中具體屬性的值。
通過上述定位名稱空間、類、屬性的方法,我們就可以愉快地使用Python來玩耍WMI。
5 實戰,以IIS爲例
瞭解了這麼多內容,咱們就拿個對象練練手。現在有這麼個需求,我們想要獲取IIS的版本號以及它所有的站點名稱,怎麼辦?
在微軟官網上比較容易的找到IIS WMI的說明,根據直覺,我們要查詢的信息可能會是在類名中包含setting的類中,那麼看起來比較有可能的有IIsSetting (WMI), IIsWebServerSetting (WMI), IIsWebInfoSetting (WMI)。
對這些類都分別看一看,發現IIsSetting中提供了一個例子:
o = getobj("winmgmts:/root/microsoftiisv2") nodes = o.ExecQuery("select * from IIsWebServerSetting where name='w3svc/1'") e = new Enumerator(nodes) for(; ! e.atEnd(); e.moveNext()) { WScript.Echo(e.item().Name + " (" + e.item().Path_.Class + ")") } // The output should be: // w3svc/1 (IIsWebServerSetting)
nodes = o.ExecQuery(“select * from
IIsSetting where name=‘w3svc/1’”)
e = new Enumerator(nodes)
for(; ! e.atEnd(); e.moveNext()) {
WScript.Echo(e.item().Name + " (" + e.item().Path_.Class + “)”)
}
// The output should be:
// w3svc/1 (IIsIPSecuritySetting)
// w3svc/1 (IIsWebServerSetting)
從這個例子中,我們可以知道iis的名稱空間是‘/root/microsoftiisv2’,然後我們可以直接在這個空間中查詢各種相關類,比如說“IIsWebServerSetting”。
結合wbemtest和IIS管理器,我們可以看出IIsWebServerSetting實例中的ServerComment屬性值和網站名稱一致:
而版本信息則在類名包含setting的類中無法找到,那再去類名包含info的類中瞧一瞧。果然,在IIsWebInfo (WMI)中找到了MajorIIsVersionNumber和MinorIIsVersionNumber屬性,分別表示大版本和小版本。那麼我們就能比較輕鬆地寫出下面的Python代碼來獲得版本和站點名稱:
# coding:utf-8 from win32com.client import GetObject
wmi = GetObject(‘winmgmts:/root/microsoftiisv2’)
# 版本
webinfo = wmi.execquery('select * from IIsWebInfo ')[0]
version = ‘{major}.{min}’.format(major=webinfo.MajorIIsVersionNumber,
min=webinfo.MinorIIsVersionNumber)
print(version)
# 站點名稱
websettings = wmi.execquery('select * from IIsWebServerSetting ')
websites = ’ | '.join(setting.ServerComment for setting in websettings)
print(websites)
6 總結
使用Python操作WMI,最大的難點並不在於如何編寫Python語句,而在於如果獲知想要查詢的內容在哪個名稱空間以及對應的類和屬性。而這些內容則需要查閱官方文檔以及使用wbemtest進行探索。獲得了這些必要的信息後,再去編寫Python代碼就是一件非常輕鬆的事情。