【发布时间】:2021-04-11 10:35:16
【问题描述】:
我可以在脚本中创建非永久性 WMI 事件查询,例如这个,它记录下 5 个新 Notepad.exe 进程的 PID:
Set WMI = GetObject("winmgmts:\\.\ROOT\cimv2")
wql = "SELECT * FROM __InstanceCreationEvent WITHIN 2 " & _
"WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.Name = 'Notepad.exe'"
Set EventSource = WMI.ExecNotificationQuery(wql)
For i = 1 To 5
With EventSource.NextEvent(-1)
Wscript.Echo .TargetInstance.ProcessId
End With
Next
但我缺少一种明确取消EventSource 的方法。否则,事件通知将继续在 WMI 内无限期运行,即使侦听生成的事件的脚本因任何原因终止(*)。当脚本多次运行时,这会导致开销增加。
MSDN documentation of IWbemServices::ExecNotificationQuery 说:
IWbemServices::ExecNotificationQuery 方法执行查询以接收事件。调用立即返回,用户可以在事件到达时轮询返回的枚举器以获取事件。释放返回的枚举数会取消查询。
如何释放返回的枚举数?
EventSource 对象似乎不可枚举。尝试在其上使用 For Each 失败,“VBScript 运行时错误:对象不支持此属性或方法”,所以我不能在 @ 末尾使用隐式 Release 987654333@循环。
(*) 这来自于说明“释放返回的枚举数会取消查询”的文档。,这意味着不释放枚举器使查询持续存在 - 但也可以明确确认:
为此查询设置通知"SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_Directory' And TargetInstance.Name = 'C:\\Test'" 并使用Sysinternals Process Monitor 观察来自 WMI 服务 (WmiPrvSE.exe) 的文件系统访问。 WMI 服务将每 2 秒开始轮询名为“C:\Test”的文件夹,并在设置监控的脚本结束后继续轮询。
重新启动 WMI 服务可以摆脱轮询,但显然这不是解决这种情况的好方法。
【问题讨论】:
-
Set EventSource = Nothing呢? -
@user692942 不,这没有帮助。当脚本退出时,无论如何都会发生这种情况。但是 WMI 事件查询是这样的:您向 WMI 服务发送查询。 WMI 服务在内部设置对请求条件的监视,并返回一种方法来侦听生成的事件。在 VBScript 中将事件侦听器设置为
Nothing不会破坏机制的 WMI 内部部分。 -
是的,但这不是一个永久的事件消费者,你正在设置它是一个临时的,它们的工作方式不同。这就是为什么most examples use an infinite do while loop 通常会在脚本延迟的情况下阻止脚本退出。您是否确定在脚本完成后会保持事件侦听器,还是只是一个假设?
-
查看您最近的编辑,听起来它正在保留该事件,因此您可能想致电
ExecNotificationQueryAsync并传入the sink 和cancel that。无论如何,我都不是 WMI 方面的专家,我只是提供建议。 -
@user692942 不,你的想法是对的。取消异步接收器实际上是这样做的。好的。 :) 你能把它变成答案吗?