目前关于 Windows 系统管理的脚本中,以 VBScript(现在似乎升级成 VB.NET,我不甚了解)居多(例如 MSDN 中的 WMI 代码都是使用这种脚本语言,微软 MVP 们写的这些教程也挺生动有趣、通俗易懂的),所以本文以 VBScript 代码的 WMI 脚本为例说明转换为 AutoHotkey 代码的过程,如果看到其他语言的代码也可以参照这个过程,例如对于 VB、JScript、AutoIt 等。本文根据 WMI 代码的不同实现方式把它们分成三类:查看 WMI 属性、执行 WMI 方法和接收 WMI 事件。
注:本文重点说明转换过程中的一些情况,但不会解释 WMI 代码的含义,如果您尚不了解 WMI 基础知识,请先参阅 WMI 脚本第一阶系列教程。
查看属性
分析脚本
下面这个 VBScript 脚本显示操作系统的名称:
Code: Select all
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
- 第一行赋值,这里的一个句点表示本地计算机。
- 第二行连接到WMI服务。
- 第三、四行查询WMI服务。
- 第五行至代码末尾为For循环,并在循环中显示内容。
现在直接看看转换过来的 AutoHotkey 脚本:
Code: Select all
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
- 第一行,修改赋值语法:AutoHotkey 中的表达式赋值使用 :=,所以这里把 = 修改为 :=。
- 第二行,修改赋值语法、对应函数和字符串连接符:去除行首的 Set,在 AutoHotkey 中连接 WMI 服务使用 ComObjGet() 函数,并且字符串连接符为句点。
- 第三、四行,修改续行方式:去除前一行末尾的下划线并在下一行开始处加上一个句点及空格(这里用于连接字符串),由于前一行末尾没有需要连接的变量或字符串,所以另加一对空字符串。
- 第五行至代码末尾,修改 For 循环语法:去除 For 的 Each 关键字,并在末尾加上左大括号(注意它的前面至少需要一个空格或 TAB),并把循环后面的 Next 替换为右大括号。
- 在循环中,修改显示命令:把 Wscript.Echo 替换为 `MsgBox, %`,因为前者直接接表达式,所以 MsgBox 后需加百分号。
这样的转换比较直接,除了个别部分其他都可以用脚本实现转换。这里做些简单的修改, ExecQuery() 的参数不长就不用续行了,循环中的几个消息框相互关联所以放在一起显示(至于字符串连接符——句点,可以保留,也可以去除,这里不管它了):
Code: Select all
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
前面代码的差异中,有重要的一点没有提到:在 VBScript 代码中 ExecQuery() 方法省略了第二个参数表示这个参数是可选的并且使用它的默认值,但在 AutoHotkey 中在调用 COM 的方法时若可选参数后还有其他参数,那么这个参数不能省略,可以使用默认值或者用 ComObjMissing() 代替,所以下面两者效果一样(这个方法的这个参数的默认值是字符串 WQL):
Code: Select all
colItems := objWMIService.ExecQuery("SELECT * FROM Win32_OperatingSystem", "WQL", 48)
colItems := objWMIService.ExecQuery("SELECT * FROM Win32_OperatingSystem", ComObjMissing(), 48)
补充自定义函数
在一些情况下会遇到 AutoHotkey 中没有与 VBScript 中相对应的命令或函数,此时要考虑自定义函数了。在下面这个例子中,由于这个属性的值是数组,所以需要先进行处理才能显示出来(原来的 VBScript 代码中使用 Join(),不过这是它的内置函数)。
Code: Select all
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
}
Code: Select all
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
}
执行方法
下面这个 VBScript 脚本把计算机名称从 MS-201012201636 修改为 NewComputerName:
Code: Select all
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
Code: Select all
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
接收事件
同步监听
下面这个 VBScript 脚本监听进程创建、关闭事件:
Code: Select all
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
Code: Select all
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
异步监听
下面这个 VBScript 脚本与前一个的用途相同,也是监听进程的创建和关闭事件,不过这里使用异步方法:
Code: Select all
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
Code: Select all
#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."
}
Code: Select all
Set MySink = WScript.CreateObject( _
"WbemScripting.SWbemSink","SINK_")
Code: Select all
MySink := ComObjCreate("WbemScripting.SWbemSink")
ComObjConnect(MySink, "SINK_")
对于 WMI 事件,建议采用后面这种异步方式,这样一个脚本中可以同时监听多个事件,还可以在监听事件的同时执行其他操作(虽然使用多个脚本或 AutoHotkey_H 的多线程也可以实现,然而会复杂多了)。
小结
从前面的例子可以看出,转换过程中语法的转换比较简单,在实际情况中应根据需要灵活进行转换。此外,WMI 是 COM 的一个部分,所以这样的转换方法也可以扩展到使用 COM 的脚本。
同时,为了方便大家,除了 MSDN,我还发现 List of Windows Management Instrumentation (WMI) classes with examples 中有大量的 WMI 脚本示例,在这里初学者能根据自己需要的功能找到代码,不容错过。