В этой статье хочу рассмотреть подписку на события WMI, а точнее временные потребители событий. В данном случае, подписка на события не требует внедрения в саму систему, так как обработка будет происходить лишь до тех пор, пока работает сценарий. Подписка на временные потребители событий может производиться в трёх режимах: синхронный, полусинхроный и асинхронный.
Давайте рассмотрим каждый режим в отдельности.
Подписка на события в синхронном режиме
Как уже упоминалось в прошлой статье «События WMI«, при подписке на внутренние события используются, в зависимости от надобности, три разных класса – для работы с экземплярами, для работы с пространством имен и с самими классами, непосредственно. Каждый из этих классов позволяет реагировать на три типа событий: создание, изменение и уничтожение объекта.
Давайте сделаем так, ниже, я приведу два примера на языке VBScript и JScript, которые будут производить подписку на события, логика их работы идентична – реагировать на запуск процесса в системе:
'********************************************* ' Имя: process_creation_sync.vbs ' Подписка на события в синхронном режиме (запуск процесса) '********************************************* Option Explicit Dim objService, objEventSource, objEvent, strResult Set objService = GetObject("WinMgmts:\\.\Root\CIMV2") Set objEventSource = objService.ExecNotificationQuery ("SELECT * FROM __InstanceCreationEvent " & _ "WITHIN 2 WHERE TargetInstance ISA 'Win32_Process'") 'Ждем наступления события WMI и записываем в переменную objEvent Set objEvent = objEventSource.NextEvent WScript.Echo "Был запуск процесса " & objEvent.TargetInstance.Name |
//********************************************* // Имя: process_creation_sync.js // Подписка на события в синхронном режиме (запуск процесса) //********************************************* var objService, objEventSource, objEvent, strResult; objService = GetObject("WinMgmts:\\\\.\\Root\\CIMV2"); objEventSource = objService.ExecNotificationQuery ("SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_Process'"); //Ждем наступления события WMI и записываем в переменную objEvent objEvent = objEventSource.NextEvent(); WScript.Echo ("Был запуск процесса " + objEvent.TargetInstance.Name); |
Теперь давайте разберем все по пунктам.
Set objService = GetObject(«WinMgmts:\.RootCIMV2») — Сперва с помощью функции GetObject мы подключаемся к нужному нам пространству имен, переменная objService будет хранить ссылку на экземпляр объекта SWbemServices (Он возвращается каждый раз, когда происходит подключение к пространству WMI).
С помощью метода ExecNotificationQuery объекта SWbemServices мы выполняем WQL запрос в синхронном режиме, он собственно и производит подписку на события:
«SELECT * FROM __InstanceCreationEvent WITHIN 2 ISA ‘Win32_Process'»
Давайте разберем суть данного WQL запроса:
SELECT * FROM __InstanceCreationEvent – подключаемся к классу __InstanceCreationEvent (он отвечает за то, был ли создан экземпляр заданного класса).
WITHIN 2 – указываем временный интервал в 2 секунды – проверка, не произошло ли событие.
WHERE TargetInstance – тут свойство TargetInstance описывает текущее состояние управляемого ресурса, что бы просмотреть предыдущее состояние, надо использовать свойство PreviousInstance.
ISA ‘Win32_Process’ – запрос будет применен к подклассу Win32_Process.
В предпоследней строчке:
Set objEvent = objEventSource.NextEvent
Мы вызываем метод NextEvent объекта SWbemEventSource, ссылка на него хранится в переменной objEventSource. Метод NextEvent ждет наступления события, которому соответствует объект SWbemEventSource, после чего возвращается объект swbemobject с описанием произошедшего события (переменная objEvent).
После запуска сценария ничего не произойдет, просто появится процесс wscript.exe, как только произойдёт запуск какого-то процесса (приложения, программы), сценарий выдаст сообщение и завершит свою работу.
Подписка на события в полусинхронном режиме
Процесс подписки на события в полусинхронном режиме практически идентичен предыдущим примерам, разница лишь в том, что тут у метода ExecNotificationQuery есть дополнительные числовые параметры, их значение равно сумме двух констант: wbemFlagForwardOnly (значение 32) и wbemFlagReturnImmediately (значение 16). Ниже привожу пример для подписки на события в полусинхронном режиме на запуск любого процесса:
'********************************************* ' Имя: process_creation_polusync.vbs ' Подписка на события в полусинхронном режиме (запуск процесса) '********************************************* Option Explicit Dim objService, objEventSource, objEvent, strResult Const wbemFlagForwardOnly =32 Const wbemFlagReturnImmediately =16 'Подключвемся к пространству имен Set objService = GetObject("WinMgmts:\\.\Root\CIMV2") 'Выполняем WQL запросс Set objEventSource = objService.ExecNotificationQuery ("SELECT * FROM __InstanceCreationEvent " & _ "WITHIN 2 WHERE TargetInstance ISA 'Win32_Process'",,wbemFlagReturnImmediately+wbemFlagForwardOnly) 'Ждем наступления события WMI и записываем в переменную objEvent Set objEvent = objEventSource.NextEvent WScript.Echo "Был запуск процесса " & objEvent.TargetInstance.Name
//********************************************* // Имя: process_creation_polusync.js // Подписка на события в полусинхронном режиме (запуск процесса) //********************************************* var objService, objEventSource, objEvent, strResult; var wbemFlagForwardOnly = 0x20; //32 var wbemFlagReturnImmediately = 0x10; //16 //Подключвемся к пространству имен objService = GetObject("WinMgmts:\\\\.\\Root\\CIMV2"); //Выполняем WQL запросс objEventSource = objService.ExecNotificationQuery("SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_Process'","WQL",wbemFlagReturnImmediately | wbemFlagForwardOnly); //Ждем наступления события WMI и записываем в переменную objEvent objEvent = objEventSource.NextEvent(); WScript.Echo ("Был запуск процесса " + objEvent.TargetInstance.Name); |
Подписка на события в асинхронном режиме
При выполнении подписки на события в асинхронном режиме после подключения к пространству имен нужно дополнительно создать ссылку на экземпляр объекта SWbemSink, после чего будет производиться обработка событий, которые будут возникать в данном объекте при выполнении и завершении асинхронной операции. Далее, подписка на событие происходит с помощью метода ExecNotificationQueryAsync объекта swbemservices (ссылка на него появляется автоматически после подключения к пространству имен WMI), для него в качестве первого параметра указывается переменная-объект SWbemSink, а уже в качестве второго параметра – строка, которая содержит нужный нам WQL запрос.
Что бы прояснить ситуацию, давайте посмотрим на пример ниже:
'********************************************* ' Имя: process_creation_async.vbs ' Подписка на событие в асинхронном режиме (запуск процесса) '********************************************* Option Explicit Dim objService, objEventSource, objSink, bDone Set objService = GetObject("WinMgmts:\\.\Root\CIMV2") Set objSink = WScript.CreateObject("WbemScripting.SWbemSink", "Sink_") objService.ExecNotificationQueryAsync objSink,"SELECT * FROM __InstanceCreationEvent " & _ "WITHIN 2 WHERE TargetInstance ISA 'Win32_Process'" WScript.Echo "Работает сценаий" ' Приостанавляваем выполнение сценария до наступления события While Not bDone WScript.Sleep 1000 Wend ' Как только произойдет событие, будет выполнена процедура Sub Sink_OnObjectReady(oOutParams,oContext) WScript.Echo "Был запуск процесса " & oOutParams.TargetInstance.Name bDone = True End Sub |
Тут мы видим, что сначала произошло подключение к пространству WMI.
Set objSink = WScript.CreateObject(«WbemScripting.SWbemSink», «Sink_») – тут мы создали ссылку на объект SWbemSink, параметр Sink_ говорит о том, что все процедуры должны начинаться с этого префикса.
Далее следует выполнение метода ExecNotificationQueryAsync, где в качестве первого параметра используется префикс objSink.
Цикл While Not bDone WScript.Sleep 1000 Wend будет работать до тех пор, пока переменная bDone не примет значение TRUE, фактически, с помощью данного цикла мы просто приостанавливаем выполнение сценария, если этого не сделать, то сценарий завершит свою работу не дождавшись появления события.
Процедура Sink_OnObjectReady относится непосредственно к объекту SWbemSink, как только подписка на событие произойдет, то управление передастся в тело данной процедуры. Параметр oOutParams будет хранить информацию о происшедшем событии. Как видим, как только событие произошло, то переменной bDone было присвоено значение True, если этого не сделать, то сценарий будет не один раз создавать подписку на события, а в бесконечном цикле.
Ну и аналогичный пример на Jscript:
//********************************************* // Имя: process_creation_async.js // Подписка на событие в асинхронном режиме (запуск процесса) //********************************************* var objService, objEventSource, objEvent, strResult, objSink, bDone=false; objService = GetObject("WinMgmts:\\\\.\\Root\\CIMV2"); objSink = WScript.CreateObject("WbemScripting.SWbemSink", "Sink_"); objEventSource = objService.ExecNotificationQueryAsync (objSink,"SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_Process'"); WScript.Echo ("Работает сценаий") // Приостанавляваем выполнение сценария до наступления события while (!bDone){ WScript.Sleep (1000); } // Как только произойдет событие, будет выполнена процедура function Sink_OnObjectReady(oOutParams,oContext){ WScript.Echo ("Был запуск процесса " + oOutParams.TargetInstance.Name); bDone = true; } |