Monday, October 31, 2005

Process Information And Notifications using WMI

I have often needed information about a process running or to see if it is running. Fortunately . NET framework comes up with System.Diagnostics name space, which is providing very useful classes like Process to access all kind of information about running process. But what if the process is not running and you need to wait idle until it completes the startup? In this case you will need an event raised to you as soon as the process has been started. That is where WMI comes very handy. You can do everything what System.Diagnostics provides to you and a little bit more by using WMI.

Background (Windows.Management Namespace)

WMI is the Microsoft implementation of Web-Based Enterprise Management (WBEM), an industry initiative to develop a standard technology for accessing management information in an enterprise environment. This initiative helps companies lower their total cost of ownership by enabling powerful management of systems, applications and devices.

This namespace provides several classes. Some of the are used to query information about a system resource like hard disk, Network Adaptor, Windows Service, Process, etc. I will use some of these classes to get the list of running processes. Query the system can take some unnecessary time of your running thread, that's when the other set of classes come handy. You can subscribe to some system resources to get notification when your requested action takes place. I will use these classes to subscribe for process instantiation and termination.

Using the code

This sample code is just starting point to put you to the right direction and opens a hole powerful technology. You can use the same technics to query all about the Windows system. If anything is runing on your machine, like memory card, you can query it.

WQL = WMI Query Language

The WMI Query Language (WQL) is a subset of standard American National Standards Institute Structured Query Language (ANSI SQL) with minor semantic changes to support WMI.

An example of wql which will result to our ptocess list would look like this:

string queryString = "SELECT Name, ProcessId, Caption, ExecutablePath" +           " FROM Win32_Process";  


A SelectQuery can be instantiated using that string or also like the following
SelectQuery query = new SelectQuery(className, condition, selectedProperties); 

Scope Object

The scope is like the database you are sending the query to. It mentions the machine name and then the schema and then the path to get to the resource. A local machine in Microsoft platform is usually referred to by a dot. Our example to get the list of processes on the local machine will look like the following:
ManagementScope scope = new System.Management.ManagementScope(@"file://root/CIMV2"); 

Searcher Object

Now that we have our two base classes, we can create the query using the searcher class which and execute it by calling Get() method which returns us a collection of management objects.
    ManagementObjectSearcher searcher =          new ManagementObjectSearcher(scope, query);          ManagementObjectCollection processes = searcher.Get(); 

Retrieving the detail
From here we can just iterate through the processes' properties and get our information.
foreach(ManagementObject mo in processes)  {      DataRow row = result.NewRow();      row["Name"] = mo["Name"].ToString();      row["ProcessId"] = Convert.ToInt32(mo["ProcessId"]);      if (mo["Caption"]!= null)          row["Caption"] = mo["Caption"].ToString();      if (mo["ExecutablePath"]!= null)          row["Path"] = mo["ExecutablePath"].ToString();      result.Rows.Add( row );  }  

Subscribing to an Event

So far we have just got a query to the system repository. Now the second part is even more intresting. Assume you are depending on a service running on a machine. Or you want to do an action when a service goes down. Or in this example find out when an application has been created (added to the process list).

All you need is a ManagementEventWatcher which has a delegate where you can subscribe. It has methods like Start() and Stop() which launch a different thread. And similar to the searcher object it works with a scope and a query.
string pol = "2";  string queryString =      "SELECT *" +      " FROM __InstanceOperationEvent " +      "WITHIN " + pol +      " WHERE TargetInstance ISA 'Win32_Process' " +      "   AND TargetInstance.Name = '" + appName + "'";      // You could replace the dot by a machine name to watch to that machine  string scope = @"//./root/CIMV2";  // create the watcher and start to listen  watcher = new ManagementEventWatcher(scope, queryString);  watcher.EventArrived +=  new EventArrivedEventHandler(this.OnEventArrived);  watcher.Start();  

Al of this makes it possible for us to use this class easily to figure out what happens for a process like notepad.exe
notePad = new ProcessInfo("notepad.exe");  notePad.Started +=      new Win32Process.ProcessInfo.StartedEventHandler(this.NotepadStarted);  notePad.Terminated +=      new Win32Process.ProcessInfo.TerminatedEventHandler(          this.NotepadTerminated);  



Note

It is possible to call Set the property values on the query and submit it. That is slightly more work but still very powerful.

Points of Interest

After I had installed WMI Server Explorer on my machine, I've got my Server Explorer on the Visual Studio extended to provide very nice tool which tels me what is correct name for different resources. And I was actually surprised how many resources I can access now.

See Also MSDN