Skip to main content
Email Password   helpLost your password?

Contents

  1. Introduction
  2. WQL Event Queries
  3. Event Helper Classes
  4. WITHIN and GROUP Clauses
  5. Temporary Event Consumers
  6. Permanent Event Subscription
  7. Using WMI Tools
  8. ActiveScriptEventConsumer Class
  9. Note about Strings
  10. TargetEvent and TargetInstance
  11. SMTPEventConsumer Class
  12. LogFileEventConsumer Class
  13. CommandLineEventConsumer Class
  14. Win32_LocalTime Class
  15. Final Note
  16. Useful Links

1. Introduction

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.

2. WQL Event Queries

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.

3. Event Helper Classes

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:

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.

4. WITHIN and GROUP Clauses

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:

5. Temporary Event Consumers

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:

' VBScript source code

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:

Here is a sample of the third case:

' VBScript source code

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.

6. Permanent Event Subscription

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's Query property to store
//    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:

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.

7. Using WMI Tools

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

8. ActiveScriptEventConsumer Class

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:

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:

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:

9. Note about Strings

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

10. TargetEvent and TargetInstance

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

11. SMTPEventConsumer Class

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:

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:

12. LogFileEventConsumer Class

The LogFileEventConsumer class writes customized strings to a text file each time an event is delivered to it. Significant properties:

A sample usage of LogFileEventConsumer could be to log changes in a Windows service's state. To use LogFileEventConsumer with permanent event subscription:

When assigning value to the LogFileEventConsumer.Text property, use WMI standard string templates to access event-related data.

13. CommandLineEventConsumer Class

The CommandLineEventConsumer class launches an arbitrary process when an event is delivered to it. Important properties are:

Here is a sample of a CommandLineEventConsumer MOF that monitors PNP device changes. To create a permanent event subscription that uses CommandLineEventConsumer:

14. Win32_LocalTime Class

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:

15. Final Note

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:

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.

16. Useful Links

There is not much documentation concerning permanent event subscription, but you can find some in MSDN:

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Generalcreating my own wmi event consumer Pin
lwiji-tn
2:09 18 Mar '09  
GeneralRe: creating my own wmi event consumer Pin
Uros Calakovic
11:40 18 Mar '09  


Last Updated 29 Jul 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2009