使用Python玩轉WMI


   最近在網上搜索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

Scripting API for WMI.

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM