IT Training Courses
Microsoft Gold IT Training Partner
800-326-1044
IT Training Newsletters WMI for .NET Programmers II

WMI for .NET Programmers II

Fun with events!

by Mike Bailey
SQLSoft+ Senior Instructor, MCT, MCSD

Due to the enormous response to my first article on .NET Programming with WMI, I decided to continue with one important thing that didn't make it into the first article. That one thing is the magical world of events.

So what is event handling? Simple, we want our code to run when there is a change to a computer. Usually we handle events to either document a change or problem, or to actually fix something automatically.

Anyway I must assume that you understand the information in the first article before continuing to this one. So if you are cheating, please read the first article now.

Display Code in

I. What is an Event?

In Windows programming, we get very used to the idea of having our programs wait for an event. Maybe we are waiting for a button to be clicked or a key to be pressed. Maybe we are looking for a form to load or a form to close. WMI has its set of events also. Basically something is either created, modified, or deleted.

Some common examples are:

  • A service stops.
  • A file is added to a directory.
  • An entry is made into the application log.
  • Some amount of print jobs are added to a queue.

How do you know if an event happened?

Somehow we have to make a consumer. A consumer is just something that is notified when the event occurs. For this to happen, somehow the event must be registered to the consumer. We are going to do this with a temporary event consumer.

There are basically two types of temporary event consumer. Either we are waiting for the bus (synchronous) or doing other things while we listen for the phone (asynchronous).

II. The Basics of Event Handler

Let's get started.

Create a Windows application in any language you like so long as it is either VB.NET or C#. Next add a reference to System.Management (as we did in the first article, which you were supposed to read). Finally import the System.Management namespace.

C#

VB.NET

Now we are ready to use WMI. We will use the same base object regardless of which method we will use.

We start with our scope. In the last article, we used the ManagementScope object to use other namespaces or even other computers. That is certainly still true here, but we have another problem; we need to set EnablePrivileges on the scope or we are not allowed to run queries. This is a really odd setup because we do not have to do this for anything we did in the first article. (I’m just complaining.)

C#

VB.NET

That odd SQL statement

The first real problem is making that complicated looking query. Here is an example:

SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process'

I know it looks pretty complicated, but it's really just odd. First of all, notice the __InstanceCreationEvent (with two underlines). We can see events on an instance (thing), a class (class changed in WMI), or a namespace (namespace changed in WMI). For the purpose of this article, we will only talk about instances because we normally don't care about changes to the WMI database (classes and namespaces), just changes to the things.

__InstanceCreationEvent When a new one is created like someone starts a new Win32_Process for Solitaire
__InstanceDeletionEvent When one goes away like someone removes a share(Win32_Share)
__InstanceModificationEvent When the properties of an instance change like when a service (Win32_Service) starts or stops since the instance is not deleted or added, just the status changes

So what's with the underscores? WMI's internal classes start with underscores to keep them from conflicting with the classes others may come up with. Note that there are two underscores, not one! Most fonts will blend the two together making reading about events confusing.

WITHIN 1

What's WITHIN 1? It's the polling interval. Let's say we are trying to be told when a process stops on a server in your datacenter. The above statement will have your computer ask the server every one second if the event has occurred. Anybody remember junior high science? Heisenberg uncertainty blah, blah... (I think I tuned out at that point.) The process of monitoring something has an effect on that thing. (I bet I get at least 100 e-mails about how I misquoted this.) Anyway, setting a really tight polling interval can slow down the computers involved and also the network in between. We will keep the interval short just so we don't get bored waiting for the event to be recognized, but in the real world, 30 seconds is usually good enough.

WHERE TargetInstance ISA 'Win32_Process'

TargetInstance is the pesky little instance that causes the event to fire. There must be tens of thousands of instances of hundreds of classes in WMI. We better be pretty specific about what we want. At first, we will look for anything that is a Win32_Process. We can be even more specific if we want:

SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process' and TargetInstance.Name='sol.exe'

This is my "You better not play Solitaire" query. We are looking for something new (__InstanceCreationEvent), we will look to see if it happened once a second, the new thing must be a Win32_Process and the name of the new thing (TargetInstance) must be sol.exe. MineSweeper and pinball now go undetected, but solitaire and only solitaire is now detected. Don't ever tell your company's HR departments you have this kind of power.

Remember to always put in the ISA part even if your specific about another property like the name. You will get an unparsable query error because WMI certainly would not want to look at the name of every object added to WMI to see if it is what you are talking about.

Let's try a few more:

When the Alterer service stops:

SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Service' and TargetInstance.Name='Alerter' and TargetInstance.State='Stopped'

When a file is added to the C:\temp directory:

SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'CIM_DataFile' and TargetInstance.Drive='C:' and TargetInstance.Path='\\temp\\'
(note the funny path)

When a new event is added to the Application Event Log with a Source of WSH:

SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_NTLogEvent' and TargetInstance.LogFile='Application' and TargetInstance.SourceName='WSH'

III. Making it Work Synchronously

Looks like fun! Let's actually make them work.

The next object is something called an EventQuery. Sounds familiar, we worked with the ObjectQuery in the first article. The EventQuery object has stuff added to handle events. Let's now make an object out of the SQL.

C#

VB.NET

Next let's build the ManagementEventWatcher object. As its name implies, it is there to watch for events.

C#

VB.NET

Now we are ready to wait for the bus (synchronous). We will make are program just sit and wait until the event finally occurs.

C#

VB.NET

This will sit and wait for the bus. Unfortunately the bus may never come. We could be a little smarter and make it time out. First we make a timespan.

C#

VB.NET

The timespan is a neat little object that is used in a couple of different places (like the ASP.NET cache object). In this case, 0 days, 0 hours, 0 minutes, 30 seconds. So the code will sit on the WaitForNextEvent call and if the event doesn’t happen in 30 seconds, an exception will be raised.

C#

VB.NET

To quote a line from every TV infomercial, "But wait. There's more."

We can get the instance that causes the event to occur. Let continue with my "You better not play Solitaire" idea but this time we are going to close solitaire as soon as it starts. To do this we need to find out a little about the instance that created the event (TargetInstance).

WaitForNextEvent actually returns an object we can use to get a little more information.

C#

VB.NET

Let's see, oResult is what we get from WaitForNextEvent. It has a TargetInstance property that we can use to get the properties of the instance. The key property of a Win32_Process is the handle. After this code, we have the handle(iHandle) and can use it to grab the object . Now we just call the terminate method (information about calling methods is in the first article that you said you read). Here is my close a process subroutine. It's a little rough (no error handling or way set a computer or namespace) but I am trying to keep thing simple.

C#

VB.NET

One final note on our synchronous code; we haven't looped anything. We are only going to see the first time the event happens. This code could easily be modified to loop many or even infinite times.

IV Asynchronous Events

I think we are done waiting for the bus; let's listen for the phone instead. When we waited for the bus, we waited and did nothing else while we waited. What if we wanted to wait for more than one thing? What if we have other things to do?

Asynchronous event handling solves this. We go along with our lives until the phone rings. We then stop what we are doing and answer the phone. Once we are done with the phone call we continue on with what we where doing.

To make this work we use the same scope and query we made above. We even use the same watcher object. The difference is that we pass a delegate that will get called when the event occurs. We go on with our lives and when the phone rings, the delegate is invoked.

C# Version

We make a delegate:

And point it to some wonderful new method:

Then create the watcher, set the delegate, and start the watching:

VB.NET Version

We make a delegate:

And point it to some wonderful new method:

Then create the watcher, set the delegate, and start the watching:

As long as the program stays running, anytime the event occurs, the subroutine is run.

Now let's do something productive with the handler. Our second parameter is of the type EventArrivedEventArgs. This class has a NewEvent Property we can use to get our old friend, the targetInstance. Now we are back to getting information about the object that caused our event to occur just like above in the synchronous section. Here is an example where I write out information to a listbox.

C#

VB.NET

Conclusion

WMI events have been an often overlooked, but very powerful tool. WMI has been looked at solely as a way to get information and not as the dynamic tool it really is. I hope you will now feel comfortable with WMI event handling.

The entire code can be downloaded for free if you have an account on My SQLSoft. (Accounts can be created free.)

If you have questions, comments, and most of all, if anyone finds any errors, please e-mail me: Mike.Bailey@sqlsoft.com.