使用Python玩轉WMI

   最近在網上搜索Python和WMI相關資料時,發現大部分文章都千篇一概,而且基本上只說了很基礎的使用,並未深刻說明如何使用WMI。本文打算更進一步,讓咱們使用Python玩轉WMI。html

1 什麼是WMI

  具體請看微軟官網對WMI的介紹。這裏簡單說明下,WMI的全稱是Windows Management Instrumentation,即Windows管理規範。它是Windows操做系統上管理數據和操做的基礎設施。咱們可使用WMI腳本或者應用自動化管理任務等。node

  從Using WMI能夠知道WMI支持以下語言:web

Application language  Topic

Scripts written in Microsoft ActiveX script hosting, including Visual Basic Scripting Edition (VBScript) and Perl安全

Scripting API for WMI.網絡

Start with Creating a WMI Script.app

For script code examples, see WMI Tasks for Scripts and Applications and the TechNet ScriptCenter Script Repository.ide

Windows PowerShell函數

Getting Started with Windows PowerShell測試

WMI PowerShell Cmdlets, such as Get-WmiObject.網站

Visual Basic applications

Scripting API for WMI.

Active Server Pages

Scripting API for WMI.

Start with Creating Active Server Pages for WMI.

C++ applications

COM API for WMI.

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: ProcessesWMI Tasks: Services

  其中一個例子關於進程是這樣的:

strComputer = "."
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colProcesses = objWMIService.ExecQuery("Select * from Win32_Process")
For Each objProcess in colProcesses

    Wscript.Echo "Process: " & objProcess.Name
    sngProcessTime = (CSng(objProcess.KernelModeTime) + CSng(objProcess.UserModeTime)) / 10000000
    Wscript.Echo "Processor Time: " & sngProcessTime
    Wscript.Echo "Process ID: " & objProcess.ProcessID
    Wscript.Echo "Working Set Size: " & objProcess.WorkingSetSize
    Wscript.Echo "Page File Size: " & objProcess.PageFileUsage
    Wscript.Echo "Page Faults: " & 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 EventsWin32 ProviderOperating System ClassesWin32_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代碼就是一件很是輕鬆的事情。

 

轉載請註明出處: http://www.cnblogs.com/dreamlofter/p/5846966.html

相關文章
相關標籤/搜索