【進(jìn)階】學(xué)習(xí) WMI 從轉(zhuǎn)換代碼開始

作者:amnesiac 首發(fā):官方論壇中文版

導(dǎo)言:WMI 功能強(qiáng)大,尤其在系統(tǒng)管理方面,即使你不打算使用它,你也幾乎一定會(huì)遇到使用它的代碼。但由于 WMI 體系結(jié)構(gòu)龐大,因此初學(xué)者學(xué)習(xí) WMI 的難點(diǎn)在于如何找到適合的命名空間、類和相應(yīng)的屬性、方法或事件來(lái)實(shí)現(xiàn)我們需要的功能。不過(guò)很容易注意到,使用 WMI 的代碼結(jié)構(gòu)異常簡(jiǎn)單且網(wǎng)上(尤其是 MSDN)有大量現(xiàn)成的實(shí)現(xiàn)各種各樣功能的 WMI 代碼,所以如果能找到使用其他腳本語(yǔ)言的 WMI 代碼并將其轉(zhuǎn)換為 AutoHotkey,那么就繞過(guò)了這個(gè)難點(diǎn)。

目前關(guān)于 Windows 系統(tǒng)管理的腳本中,以 VBScript 居多(例如 MSDN 中的 WMI 代碼都是使用這種腳本語(yǔ)言),所以本文以 VBScript 代碼的 WMI 腳本為例說(shuō)明轉(zhuǎn)換為 AutoHotkey 代碼的過(guò)程,如果看到其他語(yǔ)言的代碼也可以參照這個(gè)過(guò)程,例如對(duì)于 VB、JScript、AutoIt 等。本文根據(jù) WMI 代碼的不同實(shí)現(xiàn)方式把它們分成三類:查看 WMI 屬性、執(zhí)行 WMI 方法和接收 WMI 事件。

注:本文重點(diǎn)說(shuō)明轉(zhuǎn)換過(guò)程中的一些情況,但不會(huì)解釋 WMI 代碼的含義,如果您尚不了解 WMI 基礎(chǔ)知識(shí),請(qǐng)先參閱 WMI 腳本第一階系列教程。

查看屬性

分析腳本

下面這個(gè) VBScript 腳本顯示操作系統(tǒng)的名稱:

strComputer = "." 
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\CIMV2") 
Set colItems = objWMIService.ExecQuery( _
    "SELECT * FROM Win32_OperatingSystem",,48) 
For Each objItem in colItems 
    Wscript.Echo "-----------------------------------"
    Wscript.Echo "Win32_OperatingSystem instance"
    Wscript.Echo "-----------------------------------"
    Wscript.Echo "Caption: " & objItem.Caption
Next

先分析一下這段 VBScript 代碼:

  • 第一行賦值,這里的一個(gè)句點(diǎn)表示本地計(jì)算機(jī)。
  • 第二行連接到 WMI 服務(wù)。
  • 第三、四行查詢 WMI 服務(wù)。
  • 第五行至代碼末尾為 For 循環(huán),并在循環(huán)中顯示內(nèi)容。

轉(zhuǎn)換腳本

現(xiàn)在直接看看轉(zhuǎn)換過(guò)來(lái)的 AutoHotkey 腳本:

strComputer := "." 
objWMIService := ComObjGet("winmgmts:\\" . strComputer . "\root\CIMV2") 
colItems := objWMIService.ExecQuery(""
    . "SELECT * FROM Win32_OperatingSystem", ComObjMissing(), 48) 
For objItem in colItems {
    MsgBox, %  "-----------------------------------"
    MsgBox, %  "Win32_OperatingSystem instance"
    MsgBox, %  "-----------------------------------"
    MsgBox, %  "Caption: " . objItem.Caption
}
return

對(duì)比這兩段代碼,簡(jiǎn)單說(shuō)說(shuō)基本的轉(zhuǎn)換方法:

  • 第一行,修改賦值語(yǔ)法:AutoHotkey 中的表達(dá)式賦值使用 :=,所以這里把 = 修改為 :=。
  • 第二行,修改賦值語(yǔ)法、對(duì)應(yīng)函數(shù)和字符串連接符:去除行首的 Set,在 AutoHotkey 中連接 WMI 服務(wù)使用 ComObjGet()函數(shù),并且字符串連接符為句點(diǎn)。
  • 第三、四行,修改續(xù)行方式:去除前一行末尾的下劃線并在下一行開始處加上一個(gè)句點(diǎn)及空格(這里用于連接字符串),由于前一行末尾沒有需要連接的變量或字符串,所以另加一對(duì)空字符串。
  • 第五行至代碼末尾,修改 For 循環(huán)語(yǔ)法:去除 For 的 Each 關(guān)鍵字,并在末尾加上左大括號(hào)(注意它的前面至少需要一個(gè)空格或 TAB),并把循環(huán)后面的 Next 替換為右大括號(hào)。
  • 在循環(huán)中,修改顯示命令:把 Wscript.Echo 替換為 MsgBox, %,因?yàn)榍罢咧苯咏颖磉_(dá)式,所以 MsgBox 后需加百分號(hào)。

調(diào)整轉(zhuǎn)換的腳本

這樣的轉(zhuǎn)換比較直接,除了個(gè)別部分其他都可以用腳本實(shí)現(xiàn)轉(zhuǎn)換。這里做些簡(jiǎn)單的修改, ExecQuery() 的參數(shù)不長(zhǎng)就不用續(xù)行了,循環(huán)中的幾個(gè)消息框相互關(guān)聯(lián)所以放在一起顯示(至于字符串連接符——句點(diǎn),可以保留,也可以去除,這里不管它了):

strComputer := "." 
objWMIService := ComObjGet("winmgmts:\\" . strComputer . "\root\CIMV2") 
colItems := objWMIService.ExecQuery("SELECT * FROM Win32_OperatingSystem", ComObjMissing(), 48) 
For objItem in colItems {
    MsgBox, %  "-----------------------------------"
      . "Win32_OperatingSystem instance"
      .  "-----------------------------------"
      .  "Caption: " . objItem.Caption
}
return

可選參數(shù)的默認(rèn)值

前面代碼的差異中,有重要的一點(diǎn)沒有提到:在 VBScript 代碼中 ExecQuery() 方法省略了第二個(gè)參數(shù)表示這個(gè)參數(shù)是可選的并且使用它的默認(rèn)值,但在 AutoHotkey 中在調(diào)用 COM 的方法時(shí)若可選參數(shù)后還有其他參數(shù),那么這個(gè)參數(shù)不能省略,可以使用默認(rèn)值或者用 ComObjMissing() 代替,所以下面兩者效果一樣(這個(gè)方法的這個(gè)參數(shù)的默認(rèn)值是字符串 WQL):

colItems := objWMIService.ExecQuery("SELECT * FROM Win32_OperatingSystem", "WQL", 48)
colItems := objWMIService.ExecQuery("SELECT * FROM Win32_OperatingSystem", ComObjMissing(), 48)

這點(diǎn)也適用于轉(zhuǎn)換 VB 家族其他語(yǔ)言的情況。

補(bǔ)充自定義函數(shù)

在一些情況下會(huì)遇到 AutoHotkey 中沒有與 VBScript 中相對(duì)應(yīng)的命令或函數(shù),此時(shí)要考慮自定義函數(shù)了。在下面這個(gè)例子中,由于這個(gè)屬性的值是數(shù)組,所以需要先進(jìn)行處理才能顯示出來(lái)(原來(lái)的 VBScript 代碼中使用 Join(),不過(guò)這是它的內(nèi)置函數(shù))。

strComputer := "." 
objWMIService := ComObjGet("winmgmts:\\" . strComputer . "\root\CIMV2") 
colItems := objWMIService.ExecQuery("SELECT * FROM Win32_BIOS", ComObjMissing(), 48) 
For objItem in colItems {
    MsgBox, % "-----------------------------------"
      . "`nWin32_BIOS instance"
      . "`n-----------------------------------"
      . "`nBiosCharacteristics: " . Join(objItem.BiosCharacteristics, ",")
}
return

Join(arrList, strDelimiter)
{
    For strItem in arrList
    {
        If (A_Index = 1)
          strList := strItem
        strList .= strDelimiter . strItem
    }
    Return, strList
}

在 WMI 中有許多屬性的值是數(shù)組,很可能需要這個(gè)函數(shù)。另外,許多 WMI 屬性的值是時(shí)間:

strComputer := "." 
objWMIService := ComObjGet("winmgmts:\\" . strComputer . "\root\CIMV2") 
colItems := objWMIService.ExecQuery("SELECT * FROM Win32_OperatingSystem", ComObjMissing(), 48) 
For objItem in colItems {
    MsgBox, % "-----------------------------------"
      . "`nWin32_OperatingSystem instance"
      . "`n-----------------------------------"
      . "`nInstallDate: " . objItem.InstallDate
}
return

WMIDateStringToDate(dtmDate)
{
    WMIDateStringToDate := SubStr(dtmDate, 5, 2) . "/"
    . SubStr(dtmDate, 7, 2) . "/" . SubStr(dtmDate, 1, 4) 
    . " " . SubStr(dtmDate, 9, 2) . ":" . SubStr(dtmDate, 11, 2) . ":" . SubStr(dtmDate,13, 2)
    Return, WMIDateStringToDate
}

WMI 中時(shí)間格式類似于 20101220164120.000000+480,看起來(lái)不太方便,這時(shí)一般需要進(jìn)行適當(dāng)?shù)奶幚怼?/p>

執(zhí)行方法

下面這個(gè) VBScript 腳本把計(jì)算機(jī)名稱從 MS-201012201636 修改為 NewComputerName:

strComputer = "." 
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\CIMV2") 
' Obtain an instance of the the class 
' using a key property value.
Set objShare = objWMIService.Get("Win32_ComputerSystem.Name='MS-201012201636'")

' Obtain an InParameters object specific
' to the method.
Set objInParam = objShare.Methods_("Rename"). _
    inParameters.SpawnInstance_()

' Add the input parameters.
objInParam.Properties_.Item("Name") =  "NewComputerName"
objInParam.Properties_.Item("Password") =  ""
objInParam.Properties_.Item("UserName") =  "Administrator"

' Execute the method and obtain the return status.
' The OutParameters object in objOutParams
' is created by the provider.
Set objOutParams = objWMIService.ExecMethod("Win32_ComputerSystem.Name='MS-201012201636'", "Rename", objInParam)

' List OutParams
Wscript.Echo "ReturnValue: " & objOutParams.ReturnValue

轉(zhuǎn)換并進(jìn)行簡(jiǎn)單調(diào)整后的 AutoHotkey 腳本:

strComputer := "." 
objWMIService := ComObjGet("winmgmts:\\" . strComputer . "\root\CIMV2") 
; Obtain an instance of the the class 
; using a key property value.
objShare := objWMIService.Get("Win32_ComputerSystem.Name='MS-201012201636'")

; Obtain an InParameters object specific
; to the method.
objInParam := objShare.Methods_("Rename").inParameters.SpawnInstance_()

; Add the input parameters.
objInParam.Properties_.Item("Name") := "NewComputerName"
objInParam.Properties_.Item("Password") := ""
objInParam.Properties_.Item("UserName") := "Administrator"

; Execute the method and obtain the return status.
; The OutParameters object in objOutParams
; is created by the provider.
objOutParams := objWMIService.ExecMethod("Win32_ComputerSystem.Name='MS-201012201636'", "Rename", objInParam)

; List OutParams
MsgBox, % "ReturnValue: " . objOutParams.ReturnValue
return

除了把行注釋符替換為分號(hào),這里不進(jìn)行更多的解釋了。把其中的兩處 MS-201012201636 替換為您計(jì)算機(jī)的名稱(請(qǐng)?jiān)赱系統(tǒng)屬性]對(duì)話框中查看計(jì)算機(jī)名),就可以執(zhí)行這個(gè)代碼來(lái)修改計(jì)算機(jī)名稱了(其中輸入?yún)?shù)中的用戶名和密碼可能需要根據(jù)具體情況進(jìn)行調(diào)整)。

接收事件

同步監(jiān)聽

下面這個(gè) VBScript 腳本監(jiān)聽進(jìn)程創(chuàng)建、關(guān)閉事件:

strComputer = "." 
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\CIMV2") 
Set objEvents = objWMIService.ExecNotificationQuery _
("SELECT * FROM Win32_ProcessTrace")

Wscript.Echo "Waiting for events ..."
Do While(True)
    Set objReceivedEvent = objEvents.NextEvent

    'report an event
    Wscript.Echo "Win32_ProcessTrace event has occurred."
Loop

轉(zhuǎn)換并進(jìn)行簡(jiǎn)單調(diào)整后的 AutoHotkey 腳本:

strComputer := "." 
objWMIService := ComObjGet("winmgmts:\\" . strComputer . "\root\CIMV2") 
objEvents := objWMIService.ExecNotificationQuery("SELECT * FROM Win32_ProcessTrace")

MsgBox, % "Waiting for events ..."
Loop {
    objReceivedEvent := objEvents.NextEvent

    ; report an event
    ToolTip, % "Win32_ProcessTrace event has occurred."
}
return

為什么這里把 Wscript.Echo 替換為 ToolTip 而不是 MsgBox 呢?因?yàn)?MsgBox 是阻塞的,在顯示對(duì)話框時(shí)可能會(huì)錯(cuò)過(guò)其他事件。里面包含的無(wú)限循環(huán)用來(lái)持續(xù)進(jìn)行監(jiān)視,對(duì)于所發(fā)生事件的相關(guān)信息可以從接收到的事件對(duì)象中獲取(即 objReceivedEvent)。使用同步方法監(jiān)聽事件這個(gè)腳本就沒辦法做別的事情了,請(qǐng)看后面的解決方法。

異步監(jiān)聽

下面這個(gè) VBScript 腳本與前一個(gè)的用途相同,也是監(jiān)聽進(jìn)程的創(chuàng)建和關(guān)閉事件,不過(guò)這里使用異步方法:

strComputer = "." 
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\CIMV2") 
Set MySink = WScript.CreateObject( _
    "WbemScripting.SWbemSink","SINK_")

objWMIservice.ExecNotificationQueryAsync MySink, _
    "SELECT * FROM Win32_ProcessTrace"

WScript.Echo "Waiting for events..."

While (True)
    Wscript.Sleep(1000)
Wend

Sub SINK_OnObjectReady(objObject, objAsyncContext)
    Wscript.Echo "Win32_ProcessTrace event has occurred."
End Sub

Sub SINK_OnCompleted(objObject, objAsyncContext)
    WScript.Echo "Event call complete."
End Sub

轉(zhuǎn)換并進(jìn)行簡(jiǎn)單調(diào)整后的 AutoHotkey 腳本:

#Persistent

strComputer := "." 
objWMIService := ComObjGet("winmgmts:\\" . strComputer . "\root\CIMV2") 
MySink := ComObjCreate("WbemScripting.SWbemSink")
ComObjConnect(MySink, "SINK_")

objWMIservice.ExecNotificationQueryAsync(MySink, "SELECT * FROM Win32_ProcessTrace")

MsgBox, % "Waiting for events..."
return

SINK_OnObjectReady(objObject, objAsyncContext) {
    ToolTip, % "Win32_ProcessTrace event has occurred."
}

SINK_OnCompleted(objObject, objAsyncContext) {
    ToolTip, % "Event call complete."
}

其中需要重點(diǎn)注意的是把

Set MySink = WScript.CreateObject( _
    "WbemScripting.SWbemSink","SINK_")

替換為了:

MySink := ComObjCreate("WbemScripting.SWbemSink")

ComObjConnect(MySink, "SINK_") 還有原來(lái) VBScript 腳本中的無(wú)限空循環(huán)被替換為 #Persistent 指令,這樣可以在腳本中執(zhí)行其他操作。從這里可以看出,在具體情況中需要進(jìn)行靈活的替換,不應(yīng)該拘泥于某種固定的模式。

對(duì)于 WMI 事件,建議采用后面這種異步方式,這樣一個(gè)腳本中可以同時(shí)監(jiān)聽多個(gè)事件,還可以在監(jiān)聽事件的同時(shí)執(zhí)行其他操作(雖然使用多個(gè)腳本或多線程也可以實(shí)現(xiàn),然而會(huì)復(fù)雜多了)。

小結(jié)

從前面的例子可以看出,轉(zhuǎn)換過(guò)程中語(yǔ)法的轉(zhuǎn)換比較簡(jiǎn)單,在實(shí)際情況中應(yīng)根據(jù)需要靈活進(jìn)行轉(zhuǎn)換。此外,WMI 是 COM 的一個(gè)部分,所以這樣的轉(zhuǎn)換方法也可以擴(kuò)展到使用 COM 的腳本。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,837評(píng)論 18 139
  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個(gè)線程,因...
    小菜c閱讀 6,497評(píng)論 0 17
  • 真的是天才與庸人的區(qū)別。30幾年前的讀書方法,用在如今還是那么實(shí)用。那么在只讀了一遍的情況下,看看自己...
    小朋友嘀嗒閱讀 202評(píng)論 0 0
  • 人生啦,總有那么順或不順,我不能因?yàn)槟承┬∈虑橛绊懳乙恢钡男那椋@世上除了生死,哪件事不是小事?雖然,我老是當(dāng)垃圾...
    1大太陽(yáng)閱讀 201評(píng)論 0 0
  • 夢(mèng)中的你依舊溫柔,確再也找不到相守的理由, 紅塵癡戀,皆化做祭奠, 說(shuō)好的再也不見,我在夢(mèng)中食言, 多少細(xì)水流年,...
    淺歲桉年閱讀 1,642評(píng)論 0 1