The majority of event monitoring scenarios can easily be covered using the WMI permanent event consumer framework. This approach is clearly
Nevertheless, the
System.Management
namespace comes well equipped for handling management events. The functionality afforded by
System.Management
event-handling types, however, is intended to support temporary, rather than permanent event consumers. Although it is theoretically possible to implement an event consumer provider using FCL and the .NET languages,
System.Management
does not include any facilities that are
The entire event-handling mechanism is packaged as a single
System.Management
type called
ManagementEventWatcher
. This type is solely responsible for handling all types of WMI events in both synchronous and asynchronous fashion. Just like most of the FCL types,
ManagementEventWatcher
is
Perhaps, the simplest thing that can be achieved with
ManagementEventWatcher
is handling management events in synchronous mode. For example, the following code snippet initiates the subscription for all process creation events and then
ManagementEventWatcher ew = new ManagementEventWatcher( @" SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_Process'"); while( true ) { ManagementBaseObject mo = ew.WaitForNextEvent(); Console.WriteLine("Event arrived: {0}", mo["__CLASS"]); mo = (ManagementBaseObject)mo["TargetInstance"]; Console.WriteLine("Process handle: {0}. Executable
path
: {1}", mo["Handle"], mo["ExecutablePath"]); }
Here, the instance of
ManagementEventWatcher
is created using a constructor that takes a single query string parameter. The code than enters an endless loop and starts polling for events using the
WaitForNextEvent
method. This method is built around the
IWbemServices::ExecNotificationQuery
method, which is what WMI uses to initiate synchronous event subscriptions. If you remember the discussion of synchronous query processing, you may assume that
WaitForNextEvent
Be careful when you are examining the delivered event. For the code above, the returned ManagementBaseObject embodies an instance of __InstanceCreationEvent . As you may remember, the TargetInstance property of the event object refers to an embedded object that triggers the event—in this case, an instance of the Win32_Process class. This instance can be retrieved by accessing the TargetInstance property through the ManagementBaseObject indexer or its Properties collection and casting the result back to ManagementBaseObject .
If you bother to compile and run the code above, it may produce an output similar to the following,
Event arrived: __InstanceCreationEvent Process handle: 160. Executable path: C:\WINNT\System32\notepad.exe
The
ManagementEventWatcher ew = new ManagementEventWatcher( @"\\BCK_OFFICE\root\CIMV2", @" SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_Process'"); while( true ) { ManagementBaseObject mo = ew.WaitForNextEvent(); Console.WriteLine("Event arrived: {0}", mo["__CLASS"]); mo = (ManagementBaseObject)mo["TargetInstance"]; Console.WriteLine("Process handle: {0}. Executable path: {1}", mo["Handle"], mo["ExecutablePath"]); Console.WriteLine("Originating Machine: {0}", mo["__SERVER"]); }
The code above registers for receiving the events that take place on a remote machine BCK_OFFICE . Note that the origins of an event can be traced by interrogating the __SERVER system property of the received event object.
As I mentioned earlier, there are certain security implications involved when you subscribe for specific categories of management events. For instance, in order to receive Windows log events,
SeSecurityPrivilege
must be granted and enabled. Granting the privilege is a task that should be carried out using an appropriate
Assuming that all the right privileges are granted, clearing WMI security is remarkably easy. Thus, the following code snippet successfully sets up a subscription for Windows log events, assuming that a user is granted SeSecurityPrivilege :
ManagementEventWatcher ew = new ManagementEventWatcher( @" SELECT * FROM __InstanceCreationEvent WHERE TargetInstance ISA 'Win32_NTLogEvent'"); ew.Scope.Options.Impersonation = ImpersonationLevel.Impersonate; ew.Scope.Options.EnablePrivileges = true; while(true) { ManagementBaseObject mo = ew.WaitForNextEvent(); Console.WriteLine("Event arrived: {0}", mo["__CLASS"]); }
The only difference here is the two lines of code that follow the construction of the
ManagementEventWatcher
object. It turns out that all security-
Although synchronous mode is definitely the simplest event-processing option available to the developers of management applications, it is not very flexible and not all that efficient. Its main drawback is that it needs to continuously poll WMI using the
WaitForNextEvent
method. A much better approach would be to register for events once and then handle the notifications as they
The following code snippet duplicates the functionality of the previous example, but this time in asynchronous mode:
{% if main.adsdop %}{% include 'adsenceinline.tpl' %}{% endif %}
class Monitor { bool stopped = true; public bool IsStopped { get { return stopped; } set { stopped = value; } } public void OnEventArrived(object sender, EventArrivedEventArgs e) { ManagementBaseObject mo = e.NewEvent; Console.WriteLine("Event arrived: {0}", mo["__CLASS"]); mo = (ManagementBaseObject)mo["TargetInstance"]; Console.WriteLine("Process handle: {0}. Executable path: {1}", mo["Handle"], mo["ExecutablePath"]); } public void OnStopped(object sender, StoppedEventArgs e) { stopped = true; } public static void Main(string[] args) { Monitor mon = new Monitor(); ManagementEventWatcher ew = new ManagementEventWatcher( @" SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_Process'"); ew.EventArrived += new EventArrivedEventHandler(mon.OnEventArrived); ew.
Stopped
+= new StoppedEventHandler(mon.OnStopped); ew.Start(); mon.IsStopped = false; while( true ) { // do something useful.. System.Threading.Thread.Sleep(10000); } } }
This code is fairly straightforward and should
An asynchronous event subscription is initiated by calling the
Start
method of the
ManagementEventWatcher
type. This is the method that internally invokes
IWbemServices::ExecNotificationQueryAsync
, which registers a consumer for asynchronous event delivery. Once started,
ManagementEventWatcher
continues listening for management events until stopped, either explicitly or implicitly. To explicitly terminate an event registration, consumers may call the
Stop
method, which internally invokes the
IWbemServices::CancelAsyncCall
method. Implicit termination may occur for a variety of reasons. Perhaps, the most obvious one is that the
ManagementEventWatcher
variable goes out of scope. This may happen as a result of a premature program termination or a function return, or simply because a programmer
For obvious reasons
Finalize
invokes the same old
Stop
method, which in
public void OnStopped(object sender, StoppedEventArgs e) { Console.WriteLine("Stopped with status {0}", e.Status.ToString()); stopped = true; }
Assuming that the ManagementEventWatcher object is created with an event query that references a nonexisting event class in its FROM clause, the code will produce the following output:
Stopped with status NotFound
The string " NotFound " is a textual description associated with the ManagementStatus.NotFound enumeration member, which in turn, corresponds to the WBEM_E_NOT_FOUND ( 0x80041002 ) WMI error code. In this case a ManagementException will be thrown as soon as the Start method is invoked, but the Stopped event is still triggered.
Just as it is the case with synchronous event processing, asynchronous events are always delivered one-by-one. This is a bit less efficient than the native
IWbemServices::ExecNotificationQueryAsync
model, which allows several events to be received at once. Curiously, there is a separate type, called
EventWatcherOptions
, which, like all other options types, is designed to control various aspects of event processing. Besides the
Context
and
Timeout
properties inherited from its superclass
ManagementOptions
,
EventWatcherOptions
has the
BlockSize
property, which seems to be designed for
An event query does not have to be represented by a plain string. There is a special type, called
EventQuery
, that is dedicated to handling event queries. However, unlike the other query classes described in Chapter 3,
EventQuery
is
In addition to a default parameterless constructor, the
EventQuery
type has two parameterized constructor
EventQuery q = new EventQuery( @" SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_Process'");
While the first constructor automatically assumes that the language of the query is WQL, the second one allows the language to be set explicitly:
EventQuery q = new EventQuery("WQL", @" SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_Process'");
The problem is that WQL is the only language supported at this time. So, if you attempt to create a query with a language string of, say, " XYZ ", and then you feed it into ManagementEventWatcher , an exception will be thrown.
Using the EventQuery type with ManagementEventWatcher is also very straightforward. The latter offers a constructor method that takes an object of EventQuery type, rather than a query string:
EventQuery q = new EventQuery( @" SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_Process'"); ManagementEventWatcher ew = new ManagementEventWatcher(q);
A query can also be explicitly associated with an instance of ManagementEventWatcher by setting its Query property. Thus, the following code is equivalent to the previous example:
EventQuery q = new EventQuery( @" SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_Process'"); ManagementEventWatcher ew = new ManagementEventWatcher(); ew.Query = q;
Besides the properties inherited from its base type ManagementQuery such as QueryString and QueryLanguage , the EventQuery type does not offer any additional functionality, and therefore, it is not very useful.