Contents
- Introduction
- WQL Event Queries
- Event Helper Classes
- WITHIN and GROUP Clauses
- Temporary Event Consumers
- Permanent Event Subscription
- Using WMI Tools
- ActiveScriptEventConsumer Class
- Note about Strings
- TargetEvent and TargetInstance
- SMTPEventConsumer Class
- LogFileEventConsumer Class
- CommandLineEventConsumer Class
- Win32_LocalTime Class
- Final Note
- Useful Links
The WMI Event Subsystem allows you to subscribe to WMI events. WMI events represent changes in WMI data: if you start Notepad, an instance of the Win32_Process
WMI class is created, and an instance creation WMI event is also created. If you delete a file from the disk, an instance of the CIM_DataFile
class is deleted, and an instance deletion WMI event is created. In fact, any change in WMI data can be used to create an event, so it is easy to see how WMI events can be useful in system administration.
WMI will not create events for you unless you subscribe to them. Applications that register their interest in WMI events are called event consumers.
There are two types of event consumers: temporary and permanent. Temporary event consumers are typically applications that use the .NET Framework and its System.Management
namespace or WMI scripting library to receive WMI events, which means that they receive events only when they are started by a user. Permanent event consumers are different - they are designed to receive events at all times. Both temporary and permanent event consumers use WMI event queries to subscribe to events they are interested in.
Just like other WMI queries, WMI event queries are issued using WQL (WMI Query Language). There are several differences between event queries and other query types, but the most significant one is that WMI event queries use WMI event classes. A WMI class is an event class if it is derived from the __Event
system class. So, in order to see what tasks can be accomplished using WMI events, you first need to examine the WMI event classes. But, how can you do that? Since all event classes are derived from the __Event
system class, you can use a query like this one:
Select * From Meta_Class Where __This Isa "__Event"
Although this query includes a reference to the __Event
class, it is not an event query. Actually, it is a WMI schema query - it uses Meta_Class
, a special class that represents all classes in a WMI namespace. Since you don't want all the classes, but just the __Event
derived classes, you also need to add the WHERE
clause. When issued, the query returns a list of WMI classes that looks like this:
. . .
MSFT_WMI_GenericNonCOMEvent
MSFT_WmiSelfEvent
Msft_WmiProvider_OperationEvent
Msft_WmiProvider_ComServerLoadOperationEvent
Msft_WmiProvider_InitializationOperationFailureEvent
Msft_WmiProvider_LoadOperationEvent
Msft_WmiProvider_OperationEvent_Pre
Msft_WmiProvider_DeleteClassAsyncEvent_Pre
Msft_WmiProvider_GetObjectAsyncEvent_Pre
Msft_WmiProvider_AccessCheck_Pre
Msft_WmiProvider_CreateClassEnumAsyncEvent_Pre
Msft_WmiProvider_ExecQueryAsyncEvent_Pre
Msft_WmiProvider_CreateInstanceEnumAsyncEvent_Pre
Msft_WmiProvider_NewQuery_Pre
Msft_WmiProvider_DeleteInstanceAsyncEvent_Pre
Msft_WmiProvider_CancelQuery_Pre
Msft_WmiProvider_PutInstanceAsyncEvent_Pre
. . .
On my test Windows XP SP2 machine, the query returns a total of 136 classes. The number may be different on your computer, but if you examine the list closely, you'll notice that most commonly used WMI classes like Win32_Process
or Win32_Service
are not on it.
So, the classes that you are really interested in are not derived from the __Event
class, but it is still possible to use them in WMI event queries. You can use all WMI classes in event queries, only not directly. In order to use a class that is not derived from __Event
in an event query, you need to use one of the helper classes like:
__InstanceCreationEvent
__InstanceModificationEvent
__InstanceDeletionEvent
All of the above classes are derived from __InstanceOperationEvent
, and have a TargetInstance
property, which is a reference to the class instance you want to receive event notifications from. So, if you use a query like this:
Select * From __InstanceCreationEvent
Where TargetInstance Isa "Win32_Process"
the TargetInstance
property of the returned event will contain a reference to the Win32_Process
instance that was created. If you want to refer to the Win32_Process.ExecutablePath
property, use the __InstanceCreationEvent.TargetInstance.ExecutablePath
property. In addition, the __InstanceModificationEvent
class has the PreviousInstance
property that contains a reference to a copy of the WMI class instance before it was modified. Classes derived from __InstanceOperationEvent
and their TargetInstance
property enable you to use all WMI classes in event queries.
The WMI event subsystem uses a polling mechanism for event delivery. To specify the polling interval, use the WITHIN
keyword followed by the polling interval (in seconds):
Select * From Win32_Process
Within 10
Where TargetInstance Isa "Win32_Process"
In this example, WMI initially enumerates all Win32_Process
instances, and polls for changes every ten seconds. This means that it is possible to miss some events: if a process is created and destroyed in less than ten seconds, it will not raise an event.
The Group
clause causes WMI to create only one event notification to represent a group of events. For example, this query:
Select * From __InstanceModificationEvent
Within 10
Where TargetInstance Isa
"Win32_PerfFormattedData_PerfOS_Processor"
Group Within 5
Having NumberOfEvents > 3
will create one event that represents all the modification events for Win32_PerfFormattedData_PerfOS_Processor
that occurred within 5 seconds, but only if the number of events is greater than 3.
To summarize:
- WQL event queries use classes derived from the
__Event
system class. - WQL event queries use
Within
and Group
clauses. - WQL event queries use the
Isa
keyword to specify classes to receive events from. - WQL event queries use
TargetInstance
and PreviousInstance
to access WMI data of interest.
A temporary event consumer is any application that requests event notifications from WMI. In most cases, it is a VBScript or code that uses the System.Management
namespace. Here is a sample VBScript that subscribes to the Win32_Process
creation events:
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "\\" & strComputer & "\root\cimv2")
Set colMonitoredProcesses = objWMIService. _
ExecNotificationQuery("select * from __InstanceCreationEvent " _
& " Within 1 Where TargetInstance isa 'Win32_Process'")
Do
Set objLatestProcess = colMonitoredProcesses.NextEvent
Wscript.Echo objLatestProcess.TargetInstance.Name
Loop
Although this code works, it has at least three disadvantages:
- The script process needs to run all the time. The above script will receive WMI events only while it is running, and while it is running, it consumes system resources. This may become a problem if you have multiple scripts running at the same time.
- The process can be easily interrupted. Scripts such as the above are typically run from the Command Prompt, so they can easily be interrupted, either by closing the Command Prompt box, or by pressing Ctrl+C.
- Even if the CScript process is interrupted, the event notification is not canceled. This may be the most serious disadvantage.
Here is a sample of the third case:
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "\\" & strComputer & "\root\cimv2")
Set colEvents = objWMIService.ExecNotificationQuery _
("Select * From __InstanceCreationEvent Within 2" _
& "Where TargetInstance Isa 'Win32_Directory' " _
& "And TargetInstance.Path = '\\Scripts\\'")
Do
Set objEvent = colEvents.NextEvent()
WScript.Echo objEvent.TargetInstance.Name
Loop
Scripts like this one are typically run from the Command Prompt. But, even if you stop the script, the event notification is not canceled - you can easily observe that, because the floppy disk drive is still flashing every two seconds (I call this the 'FDD Light Show'). This is true not only of the file system monitoring scripts, but every other. The only way to cancel the event notification in this case is to stop the Winmgmt service itself, using:
net stop winmgmt
The Windows Firewall service depends on Winmgmt, so it is easy to imagine a situation where this can become a problem.
WMI permanent event subscription can remedy all these problems. It doesn't depend on a running process (save for svchost.exe that hosts the Winmgmt service). To interrupt it, you need knowledge of WMI, so it is not easy to stop it accidentally, and you can cancel it anytime, without having to restart the Winmgmt service. In its basis, permanent event subscription is a set of static WMI classes stored in a CIM repository. Of course, you can use VBScript or the .NET Framework System.Management
classes to create these instances and set up a permanent event subscription, but the easiest way is (at least in my opinion) to use MOF. Here is a sample MOF that you can use as a template for creating permanent event subscriptions:
// 1. Change the context to Root\Subscription namespace
// All standard consumer classes are
// registered there.
#pragma namespace("\\\\.\\root\\subscription")
// 2. Create an instance of __EventFilter class
// and use it
// your WQL event query.
instance of __EventFilter as $EventFilter
{
Name = "Event Filter Instance Name";
EventNamespace = "Root\\Cimv2";
Query = "WQL Event query text";
QueryLanguage = "WQL";
};
// 3. Create an instance of __EventConsumer
// derived class. (ActiveScriptEventConsumer
// SMTPEventConsumer etc...)
instance of __EventConsumer derived class as $Consumer
{
Name = "Event Consumer Instance";
// Specify any other relevant properties.
};
// 4. Join the two instances by creating
// an instance of __FilterToConsumerBinding
// class.
instance of __FilterToConsumerBinding
{
Filter = $EventFilter;
Consumer = $Consumer;
};
To create a permanent WMI event subscription, you need to follow these steps:
- Change the WMI context to the
Root\Subscription
namespace. Starting with Windows XP, it is Microsoft's recommendation to store all permanent event subscriptions there. - Create an instance of the
__EventFilter
class. The __EventFilter
class is derived from the __IndicationRelated
system class, and its main purpose is to hold a WQL event query. To create an instance of __EventFilter
, use 'instance of
' keywords. First, you give the class instance a unique name using its Name
property. Second, specify its EventNamespace
property - typically, this will be the Root\Cimv2
namespace, as the majority of useful classes are located there. Third, set its Query
property to the WQL event query you want to use. - Create an instance of the
__EventConsumer
derived class. This is the instance that represents the event consumer. Although it is possible to create custom __EventConsumer
derived classes, this requires creating a COM object that reacts to the specified event, which is probably not a viable solution for most system administrators. Luckily, Microsoft provides a set of standard event consumer classes, like ActiveScriptEventConsumer
, LogFileEventConsumer
, etc. In this article, I will use only standard event consumer classes. - Associate the instances of
__EventFilter
and __EventConsumer
derived classes by creating an instance of the __FilterToConsumerBinding
class. Its Filter
property is a reference to the previously created instance of the __EventFilter
class, and its Consumer
property is a reference to one of the standard event consumer class instances.
In the rest of this article, I will attempt to walk you through several samples of permanent event subscription that use standard event consumer classes.
A tool named WMI Event Registration is included with WMI Tools, and it is very helpful when working with permanent subscription: it allows you to explore existing filters, consumers, or timers, and also create new ones using a user-friendly interface. You can also cancel event subscriptions using this tool.
When this tool is first opened, you are offered to connect to the Root\Cimv2
namespace, but connect to Root\Subscription
instead - this is where you will create most of the permanent event subscriptions. Once connected, if you select 'Consumers' from the leftmost dropdown, you will see a list of all available standard event consumer classes listed in the left hand pane, as they are already registered there.
If an instance of any of the standard event consumer classes already exists, by selecting it, you can view the available __EventFilter
instances in the right hand pane. If any of the __EventFilter
instances is joined with the selected consumer instance, it is checked, so, the green checkmark actually represents an instance of the __FilterToConsumerBinding
class.
All permanent event subscription samples presented here are created using MOF - you need a tool called mofcomp.exe to store instance definitions contained in a MOF file into the CIM repository. Mofcomp.exe is stored in the Windows directory (typically C:\Windows\System32\Wbem\) and its basic syntax is:
mofcomp FileName.mof
The ActiveScriptEventConsumer
class is one of the standard event consumer classes: it allows you to run ActiveX script code whenever an event is delivered to it. To create an instance of the ActiveScriptEventConsumer
class, you need to assign values to its properties:
Name
: Gives a unique name to an instance of ActiveScriptEventConsumer
. ScriptingEngine
: This is the name of the scripting engine that will be used. Although the documentation states that this can be any arbitrary scripting engine, the only ones I have used are 'VBScript' and 'JScript'. ScriptText
: This is a string property that contains a VBScript or JScript code to be executed when an event is delivered to the ActiveScriptEventConsumer
instance. ScriptFileName
: This property holds the full path to the VBScript or JSCript file to be executed on event arrival. ScriptText
and ScriptFileName
properties are mutually exclusive.
As a test, you can create an event consumer that executes some arbitrary VBScript code whenever an instance of Win32_Process
named 'notepad.exe' is created. To create a permanent event subscription that uses ActiveScriptEventConsumer
:
- Change the current WMI namespace to
Root\Subscription
:
#pragma namespace("\\\\.\\root\\subscription")
- Create an instance of the
__EventFilter
class to monitor the Win32_Process
creation:
instance of __EventFilter as $EventFilter
{
EventNamespace = "Root\\Cimv2";
Name = "New Process Instance Filter";
Query = "Select * From __InstanceCreationEvent Within 2"
"Where TargetInstance Isa \"Win32_Process\" "
"And Targetinstance.Name = \"notepad.exe\" ";
QueryLanguage = "WQL";
};
In this case, you will receive a __InstanceCreationEvent
class instance, but from the Root\Cimv2
namespace, as the Win32_Process
class is located there.
- Create an instance of the
ActiveScriptEventConsumer
class:
instance of ActiveScriptEventConsumer as $Consumer
{
Name = "TestConsumer";
ScriptingEngine = "VBScript";
ScriptText =
"Set objFSO = CreateObject(\"Scripting.FileSystemObject\")\n"
"Set objFile = objFSO.OpenTextFile(\"c:\\log.txt\", 8, True)\n"
"objFile.WriteLine Time & \" \" & \" Notepad started\"\n"
"objFile.Close\n";
};
The VBScript code that is assigned to its ScriptText
property simply logs the time of the notepad.exe process creation to a text file.
- Bind the two previously created instances using the
__FilterToConsumerBinding
class:
instance of __FilterToConsumerBinding
{
Consumer = $Consumer;
Filter = $EventFilter;
}
When you compile the above MOF using mofcomp.exe, every time Notepad is opened, the time of the notepad.exe process creation is logged to the c:\log.txt file. If the file doesn't already exist, it is created when the first event notification is received.
Instead of ScriptText
, you can also use the ScriptFileName
property:
instance of ActiveScriptEventConsumer as $Consumer
{
Name = "ExternalScriptConsumer";
ScriptingEngine = "VBScript";
ScriptFileName = "C:\\Consumer.vbs";
};
In that case, you also need an external script file: c:\Consumer.vbs.
When creating VBScript or JScript scripts to use with ActiveScriptEventCosumer
, you need to be aware of some limitations:
ActiveScriptEventConsumer
doesn't use the Windows Script Host (WSH), which is widely used in system administration scripts. This means that you can not use the WScript
object or any of its properties and methods (like WScript.CreateObject
, WScript.Sleep
etc.). - The script can not generate any screen output, which means that you can not use the VBScript
MsgBox
function. - The script does not have network access.
- The script can't use any user specific data, such as environment variables or network shares.
When setting up a permanent event subscription, it is likely that you will need to use strings, so here is a quick note:
An MOF string is a sequence of characters enclosed in double quotes. Successive strings are joined together, so this:
"Select * From __InstanceCreationEvent "
"Within 30 "
becomes:
"Select * From __InstanceCreationEvent Within 30 "
You can also use the following escape sequences:
\b backspace
\t horizontal
\n linefeed
\f form feed
\r carriage return
\" double quote
\' single quote
\\ backslash
A script executed by an ActiveScriptEventConsumer
instance can access an environment variable called TargetEvent
, which holds a reference to the event class:
instance of ActiveScriptEventConsumer as $Consumer
{
Name = "TargetEventConsumer";
ScriptingEngine = "VBScript";
ScriptText =
"Const ForReading = 1\n"
"Const ForWriting = 2\n"
"\n"
"Set objFso = CreateObject(\"Scripting.FileSystemobject\")\n"
"Set objStream = objFso.OpenTextFile( _\n"
" TargetEvent.TargetInstance.Name, ForReading, False)\n"
"\n"
"strContent = objStream.ReadAll()\n"
"objStream.Close\n"
"\n"
"Set objStream = objFso.OpenTextFile( _\n"
" TargetEvent.TargetInstance.Name, ForWriting, False)\n"
"\n"
"objStream.Write( _\n"
" Replace(strContent, \"127.0.0.1\", \"Localhost\"))\n"
"objStream.Close\n";
};
The event class is typically one of various __InstanceOperationEvent
derived classes, whose TargetInstance
property, in turn, is a reference to the actual class instance of what was created. If that class is, for example, CIM_DataFile
, you need to use the following to access its Name
property:
TargetEvent.TargetInstance.Name
This class sends an e-mail message each time an event is delivered to it. To create an instance of the SMTPEventConsumer
class, assign values to its properties:
Name
: Gives a unique name to the instance. SMTPServer
: Name of the SMTP server through which the message will be sent. ToLine
: To line of the e-mail message. FromLine
: From line of the e-mail message. Subject
: Subject line of the mail message. Message
: The body of the e-mail message.
As an example, set up a permanent event subscription that uses the SMTPEventConsumer
class to send an e-mail message each time a printer status changes. To use SMTPEventConsumer
in permanent event subscription:
- Change the context to the
Root\Subscription
namespace:
#pragma namespace("\\\\.\\root\\subscription")
- Create an instance of the
__EventFilter
class:
instance of __EventFilter as $EventFilter
{
EventNamespace = "Root\\Cimv2";
Name = "SMTPEventFilter";
Query = "Select * From __InstanceModificationEvent "
"Within 2 "
"Where TargetInstance Isa \"Win32_Printer\" "
"And (TargetInstance.PrinterStatus = 1 "
"Or TargetInstance.PrinterStatus = 2) "
"And Not (PreviousInstance.PrinterStatus = 1 "
"Or PreviousInstance.PrinterStatus = 2)";
QueryLanguage = "WQL";
};
With the above WQL query, you subscribe to modification events of the Win32_Printer
class instances. Note the usage of the __InstanceModificationEvent.PreviousInstance
property, which contains a copy of the Win32Printer
instance before it was changed. It is useful for comparing the instance properties before and after it was modified. In this case, we are only interested in the events in which the Win32_Printer.PrinterStatus
value is changed from anything else to 1 or 2.
- Create an instance of the
SMTPEventConsumer
class:
instance of SMTPEventConsumer as $Consumer
{
Name = "Printer Error Event Consumer";
SMTPServer = "SMTPServerName";
ToLine = "Recipient@nn.com";
FromLine = "Sender@nn.com";
Subject = "Printer Error!";
Message = "An error is detected in one of the printers!\n"
"Printer Name: %TargetInstance.Name%\n"
"Server: %TargetInstance.__Server%\n"
"Event Date: %TIME_CREATED%";
};
If you look at the SMTPEventConsumer
class MOF code, you will see that most of its properties are marked with a Template
qualifier. This means that you can use WMI standard string templates when setting their values. Using standard string templates, you can access the event class properties, just as you can use the TargetEvent
environment variable with ActiveScriptEventConsumer
. So, for example, if TargetInstance
is Win32_Printer
, this:
"Printer Name: %TargetInstance.Name%"
will be translated into something like:
"Printer Name: HP LaserJet III PostScript Plus v2010.118"
Also, this:
"Event Date: %TIME_CREATED%"
will become:
"Event Date: 128611400690000000"
__InstanceModificationEvent.Time_Created
is the number of 100-nanosecond intervals after January 1, 1601, so if you want to convert it to a readable format, it will probably take a bit of work.
- Bind the two instances by creating an instance of the
__FilterToConsumerBinding
class:
instance of __FilterToConsumerBinding
{
Consumer = $Consumer;
Filter = $EventFilter;
};
The LogFileEventConsumer
class writes customized strings to a text file each time an event is delivered to it. Significant properties:
Name
: Gives a unique name to the instance. Text
: Contains the text to be written to the log file when an event arrives. You can use standard string templates to compose it. Filename
: Path to the text file where the value of the Text
property is to be written.
A sample usage of LogFileEventConsumer
could be to log changes in a Windows service's state. To use LogFileEventConsumer
with permanent event subscription:
- Change the context to the
Root\Subscription
namespace:
#pragma namespace("\\\\.\\root\\subscription")
- Create an instance of the
__EventFilter
class that monitors the Win32_Service
modification events:
instance of __EventFilter as $EventFilter
{
EventNamespace = "Root\\Cimv2";
Name = "Service State Event Filter";
Query = "Select * From __InstanceModificationEvent "
"Within 2 "
"Where TargetInstance Isa \"Win32_Service\" "
"And TargetInstance.State <> "
"PreviousInstance.State";
QueryLanguage = "WQL";
};
- Create an instance of
LogFileEventConsumer
:
instance of LogFileEventConsumer as $Consumer
{
Name = "Service State Log Consumer";
Filename = "c:\\scripts\\ServiceStateLog.csv";
IsUnicode = true;
Text = "\"%TargetInstance.Name%\","
"\"%PreviousInstance.State%\","
"\"%TargetInstance.State%\","
"\"%TIME_CREATED%\"";
};
- Bind the two instances using the
__FilterToConsumerBinding
class:
instance of __FilterToConsumerBinding
{
Consumer = $Consumer;
Filter = $EventFilter;
};
When assigning value to the LogFileEventConsumer.Text
property, use WMI standard string templates to access event-related data.
The CommandLineEventConsumer
class launches an arbitrary process when an event is delivered to it. Important properties are:
Name
: Gives a unique name to the instance. ExecutablePath
: Path to the executable. This property can be Null
, but even if you assign a value to it, you still need to include the executable path at the beginning of the CommandLineTemplate
property. CommandLineTemplate
: Standard string template that specifies the executable to be launched, followed by any command line arguments. You can use standard string templates when creating it.
Here is a sample of a CommandLineEventConsumer
MOF that monitors PNP device changes. To create a permanent event subscription that uses CommandLineEventConsumer
:
- Change the WMI context to the
Root\Subscription
namespace:
#pragma namespace("\\\\.\\root\\subscription")
- Create an instance of the
__EventFilter
class that detects the Win32_PNPEntity
instance creation:
instance of __EventFilter as $EventFilter
{
EventNamespace = "Root\\Cimv2";
Name = "Test Command Line Event Filter";
Query = "Select * From __InstanceCreationEvent "
"Within 2 "
"Where TargetInstance Isa \"Win32_PNPEntity\" ";
QueryLanguage = "WQL";
};
- Create an instance of the
CommandLineEventConsumer
class:
instance of CommandLineEventConsumer as $Consumer
{
Name = "Test CommandLine Event Consumer";
RunInteractively = false;
CommandLineTemplate = "cmd /c "
"WMIC /Output:"
"C:\\HWLogs\\PNPDeviceLog%TIME_CREATED%.html "
"Path Win32_PNPEntity "
"Get Caption, DeviceId, PNPDeviceId "
"/Format:HTable.xsl";
};
This CommandLineEventConsumer
instance uses the WMI command line utility (WMIC) to create a simple HTML file that contains a list of all Win32_PNPEntity
instances each time a new Win32_PNPEntity
instance is created.
- Bind the instances using
__FilterToConsumerBinding
:
instance of __FilterToConsumerBinding
{
Consumer = $Consumer;
Filter = $EventFilter;
};
The Win32_LocalTime
class is an exception: it is not derived from the __Event
class, but you can still use it in WQL event queries, which means that you can also use it to set up a permanent event subscription. An interesting use of the Win32_LocalTime
class can be to mimic the Windows Scheduler service. To create a permanent event subscription that subscribes to Win32_LocalTime
events:
- Change the context to the
Root\Subscription
namespace:
#pragma namespace("\\\\.\\root\\subscription")
- Create an instance of the
__EventFilter
class:
instance of __EventFilter as $EventFilter
{
EventNamespace = "Root\\Cimv2";
Name = "Sample Timer Event Filter";
Query = "Select * From __InstanceModificationEvent "
"Where TargetInstance Isa \"Win32_LocalTime\" "
"And TargetInstance.Hour = 18 "
"And TargetInstance.Minute = 10 "
"And TargetInstance.Second = 30";
QueryLanguage = "WQL";
};
Use this filter to subscribe to Win32_LocalTime
modification events. In the query, you can use any combination of Win32_LocalTime
properties: Day
, DayOfWeek
, Hour
, Milliseconds
, Minute
, Month
, Quarter
, Second
, WeekInMonth
, and Year
.
- Create an instance of a
__EventConsumer
derived class:
instance of CommandLineEventConsumer as $Consumer
{
Name = "Test CommandLine Event Consumer";
RunInteractively = false;
CommandLineTemplate = "cmd /c "
"C:\\Backup\\LocalBackup.bat";
};
In this case, it is an instance of the CommandLineEventConsumer
class, but it can be any of the standard consumer classes.
- Bind
__EventFilter
and CommandLineEventConsumer
instances by creating an instance of the __FilterToConsumerBinding
class:
instance of __FilterToConsumerBinding
{
Consumer = $Consumer;
Filter = $EventFilter;
};
Permanent event subscription has several advantages over temporary event subscription, but it also has one disadvantage: temporary event subscription is easier to debug. If you are using the System.Management
namespace to create an application that subscribes to WMI events, you have all Visual Studio debugging tools at your disposal. If you are using VBScript, you can test WQL event queries separately from the rest of the code, and you receive meaningful (at least sometimes) error messages from WMI. While you are testing permanent event subscription, the only source of debugging information is the WMI event subsystem log file, named Wbemess.log (it is typically located in the C:\Windows\System32\Wbem\Logs\ directory) - all errors detected both in the event filter and the event consumer instances are logged there, and the messages are not always easy to decipher. So, it is probably better to test WQL queries you want to use for permanent event subscription using System.Management
or VBScript first.
Permanent event subscription can be useful, but if you don't use it carefully, it can consume too much system resources and become inefficient. There are two ways to deal with this:
- Increase the polling interval using the
Within
clause. In the samples presented here, I used very short polling intervals, but, depending on your requirements, you can increase it at your will. - Make WQL queries more selective using the
Where
clause. For example, this query:
Select * From __InstanceCreationEvent
Where TargetInstance Isa "CIM_DataFile"
And TargetInstance.Path = "\\Logs\\"
will be less efficient than this one:
Select * From __instanceCreationEvent
Where TargetInstance Isa "CIM_DataFile"
And TargetInstance.Drive = "C:"
And TargetInstance.Path = "\\Logs\\"
And TargetInstance.Extension = "Log"
Queries that include file system classes like CIM_DataFile
or Win32_Directory
can be very resource consuming, in general: a query that monitors a couple of hundreds of files can slow down your system noticeably.
WQL is a version of SQL, and for SQL queries, it is often recommended not to select all fields in a table (using '*') unless you really need all of them. I haven't tested this recommendation with WQL queries, but I don't think this advice applies to WQL.
There is not much documentation concerning permanent event subscription, but you can find some in MSDN: