ONDotNet.com    
 Published on ONDotNet.com (http://www.ondotnet.com/)
 See this if you're having trouble printing code examples


A Quick Introduction to WMI from .NET

by Jim Murphy
04/08/2003

Windows Management Instrumentation (WMI) is Microsoft's implementation of the Web Based Enterprise Management Initiative as defined by the Distributed Management Task Force. Or, if you like, the DMTF WBEM--and let me tell you it's a monster.

There is a lot to the WMI architecture including performance counters, event notifications, configuration management and storage, all built for a distributed environment making WMI the preferred technology for monitoring applications on the Windows platform. Getting at the treasure trove of WMI data and events has always been relatively painless even from scripting environments, but providing that data from your own-instrumented applications has been much more complex. Luckily, the System.Diagnostics, System.Management and System.Management.Instrumentation namespaces contain dozens of classes and interfaces that make consuming and providing WMI data a much easier.

Reading Performance Counters

Management and monitoring are abstract words so what if you just wanted to see the current CPU load of your computer. The System.Diagnostics.PerformanceCounter class makes this straight forward:

PerformanceCounter cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
int value = cpuCounter.NextValue();

That's pretty painless but you're probably wondering where those magic strings came from. Performance counters are organized by category, counter name and instance name as reflected in the signature of the PerformanceCounter constructor. For a list of the counters available, run perfmon (Start-Run-perfmon.exe) and choose "Add Counter". The Add Counter dialog presents the Category/Counter/Instance names for dozens of interesting tidbits of system state. In addition to monitoring hardware and the Win32 subsystem, there are a number of CLR and ASP.NET specific counters that you can use to monitor your managed applications including garbage collection performance, COM Interop details, HTTP processing and even interesting SQLClient events. As we'll see later you can even add your own.

Screen shot.

Visual Studio .NET also includes support for displaying Performance Counters with Server Explorer. Incidentally, the PerformanceCounter class includes overloaded constructors to get at counters on other machines provided your application has the required permissions.

More Advanced Monitoring--WMI Events

Related Reading

Programming .NET Components
By Juval Löwy

Reading system performance counters is a good start on knowing the state of your application platform but that's just the beginning. WMI provides applications a way to subscribe to events, read and write configuration data and enumerate classes and instances of WMI providers.

Applications acting as WMI event consumers subscribe to a plethora of system and custom events using temporary or permanent event subscriptions. Temporary event subscriptions are created by the consuming application and end when the application ends. In this way the subscriptions are only active when the consuming application is running. Compare this to permanent event subscriptions where an application serving as a subscription builder invokes WMI APIs to store subscription information in the machine's CIM repository. When an event occurs the WMI runtime will invoke the script or configured application, starting it if required.

Event subscriptions, made using a WMI Query Language (WQL), allow you to specify exactly the events you are interested in. Its important to realize that events aren't necessarily pre-canned but are parameterized based on the classes and data provided in the WQL query. Navigating the rich and extensible namespace of WMI classes is a little daunting but a little experimenting with the WQL syntax and reviewing the WMI reference documentation will get you started.

Writing Temporary Event Consumers

Writing a temporary event consumer in .NET involves creating a System.Management.WQLEventQuery object, specifying the appropriate WQL query string and associating that query with a System.Management.ManagementEventWatcher. In this example we register a temporary subscription with WMI to fire when our Win32_Process instance's WorkingSetSize exceeds some value.

using System;
using System.Management;
using System.Collections;

class WSMonitor
{
  bool pleaseContinue = true;

  public void doit()
  {
     try
     {
          
        int processId = 
            System.Diagnostics.Process.GetCurrentProcess().Id;
        int workingSet = 30000000; 
        string wqlQuery = String.Format(
           @"SELECT * FROM __InstanceModificationEvent WITHIN 1 
             WHERE TargetInstance ISA 'Win32_Process' AND 
                   TargetInstance.ProcessId = {0} AND 
                   TargetInstance.WorkingSetSize >= {1} AND 
                   PreviousInstance.WorkingSetSize < {2} ", 
           processId, workingSet, workingSet);
          
        WqlEventQuery query = new WqlEventQuery(wqlQuery);
        ManagementEventWatcher watcher = 
           new ManagementEventWatcher(query);
        watcher.EventArrived += 
           new EventArrivedEventHandler(onEvent);
  
        watcher.Start();
        ArrayList array = new ArrayList();
        for (int i = 0; pleaseContinue; ++i)
        {
           array.Add(1);
  
           if (i % 1000 == 0)
              System.Threading.Thread.Sleep(1);
        }
        
        watcher.Stop();
     }
     catch (ManagementException e)
     {
        Console.WriteLine("Management exception: " + e);
     }
  }
  
  public void onEvent(object sender, EventArrivedEventArgs e)
  {
        pleaseContinue = false;
        Console.WriteLine("You're a big boy now!");
  }
}

In the above sample, the WQLEventQuery class is created using a query that fires when the Win32_Process instance's WorkingSetSize property exceeds the value provided. These can look a little strange if you're not familiar with WMI's CIM (Common Information Model), so letís take a closer look:

SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE
TargetInstance ISA 'Win32_Process' AND 
TargetInstance.ProcessId = 1367 AND
TargetInstance.WorkingSetSize >= 30000000 AND 
PreviousInstance.WorkingSetSize < 30000000

__InstanceModificationEvent is an intrinsic WMI event that fires when any value in the namespace changes. You scope that namespace with the WHERE clause to restrict the event to changes in "Win32_Process" objects that have a ProcessId property equal to the value specified and a WorkingSetSize that just exceeded our setpoint. The WITHIN 1 clause specifies that events should be dispatched within 1 second of the actual event occurring. Increasing this number will increase your notification latency window but will reduce the monitoring overhead associated with polling. TargetInstance and PreviousInstance are fields in the __InstanceModificationEvent class and represent the current and previous value for our Win32_Process class. WorkingSetSize is one of 45 properties on the CIM's Win32_Process class and with literally hundreds of built-in and custom classes there are plenty of events you can monitor. The Win32_Process is a WMI class not to be confused with a managed type written in .NET. WMI classes are types defined using an abstract language called MOF (Managed Object Format) that predates the CLR.

Creating Custom Performance Counters

The examples so far have consumed existing WMI performance counters and events, which is only half the story. You can extend WMI with your own performance counters and event classes and well as instrument your applications configuration data so it's available to an external management tool.

Creating a custom performance counter involves the use of the same System.Diagnostics.PerformanceCounter class we used to read performance data. You can use the static PerformanceCounter.Exists and PerformanceCounter.Create methods to conditionally install the new performance counter. Here is an (admittedly lame) example of a custom performance counter that indicates the number of 7s encountered in an integer parameter to a web service.

// Check if the category already exists or not.
if(!PerformanceCounterCategory.Exists(categoryName))
{
  CounterCreationDataCollection creationData =  
                         new CounterCreationDataCollection();

  // Create two custom counter objects.
  creationData.Add(new CounterCreationData("Number of 7s - Last", 
      "Number of occurences of the number 7 in the last WS call", 
      PerformanceCounterType.NumberOfItems32));


  // Bind the counters to a PerformanceCounterCategory.
  PerformanceCounterCategory myCategory = 
       PerformanceCounterCategory.Create("TestWS Monitor", 
                                         helpInfo, 
                                         creationData);

}

If the counter has not been installed yet, we create a CounterCreationDataCollection that represents the counters in our custom counter category. Deciding how to populate the CounterCreationDataCollection with CounterCreationData instances is the most difficult part. You need to define a PerformanceCounterType for each counter from the predefined list of types that determine how PerformanceCounter.NextValue algorithm works. There are several variations on Average, Difference, Instantaneous, Percentage and Rate types. Here we choose a simple NumberOfItems32 instantaneous type, which not surprisingly holds a simple 32bit value.

PerformanceCounterCategory.Create adds the counter category to the WMI CIM repository. Notice that we didn't register any WMI provider code that supplies the data just the metadata about this counter. Servicing the counter comes later when we create an instance of the PerformanceCounter class with our category and name.

Here is our contrived web service that takes a string parameter and attempts to parse it as an integer. Along the way we test the value provided for the number of 7s and update our custom counter.

[WebMethod]
public int parseInteger(String x)
{
   int val = Int32.Parse(x);
   int count = 0;
   foreach (char c in x.ToCharArray())
   {
      if (c == '7')
         count++;
   }

   PerformanceCounter counter =
      new PerformanceCounter("TestWS Monitor",
	                         "String Length - Last", 
                             false);
   counter.RawValue = count;

   return val;
}

Here we create a writable instance of our PerformanceCounter by passing a false value for the last constructor parameter. If our application has adequate permissions to write performance counters we will successfully construct the PerformanceCounter and can invoke any of the update properties including PerformanceCounter.RawValue, PerformanceCounter.Increment and PerformanceCounter.IncrementBy. The nature of the interaction between the PerformanceCounter.NextValue method and the various update properties depends on the type of performance counter you created.

Exposing Custom Events to WMI

Naturally, the same extensibility is available to the other parts of WMI including events. Exposing interesting application events to WMI lets your users create more sophisticated interactions with your application. Augmenting your exception handling code to fire custom WMI events enables administrators to react to expected and unexpected situations in new and interesting ways. You could print a stack trace to a log file but why not add the ability to notify an interested party with relevant state information as well. That interested party can do just about anything with that notification including emailing you about it!

There are a couple different implementation paths to publish WMI events from your applications. The one I describe below involves creating a class that extends System.Management.Instrumentation.BaseEvent. You can design this event class to consist of whatever fields, properties and methods you feel are appropriate. Any public fields and properties will be published in the CIM at installation time and will be available to event consumers.

using System;
using System.Management;
using System.Configuration.Install;
using System.Management.Instrumentation;

// Specify which namespace the Management 
// Event class is created in
[assembly:Instrumented("Root/Default")]

// Let the system know you will run InstallUtil.exe 
// utility against this assembly -- add reference to 
// System.Configuration.Install.dll
[System.ComponentModel.RunInstaller(true)]
public class CustomEventInstaller : 
                 DefaultManagementProjectInstaller{}

namespace WorkingSetMonitor
{
  // the event class: renamed in the CIM 
  // using ManagedNameAttribute
  [ManagedName("SuperEvent")]
  public class CustomEvent : BaseEvent
  {
    string data; // not exposed to the CIM - its private
    int state;   // exposed via public property
  
    public CustomEvent(string data)
    {
       this.data = data;
    }
  
    public int State
    {
       get { return state; }
       set { state = value; }
    }
  }
}

The InstrumentedAttribute decorates the assembly indicating the WMI namespace to install our metadata into. We include a ManagementProjectInstaller derivative that together with the System.ComponentModel.RunInstaller attribute will help install any custom WMI events in our assembly. The installer code wont run itself however and must be invoked directly or more simply by running the Installutil.exe utility from the .NET framework SDK. Installutil.exe inspects an assemply for the existence of System.ComponentModel.RunInstaller attributes and installs or uninstalls as directed. The System.Management.Instrumentation.ManagedNameAttribute attribute decorates the custom event class to provides a more appropriate event name to WMI than the managed class name.

Our custom event type holds the relevant event state but inherits a Fire method from BaseEvent. Invoking the event is as simple as creating a CustomEvent instance and calling its Fire Method.

CustomEvent customEvent = new CustomEvent("hello");
customEvent.State = 42;
customEvent.Fire();

Hopefully this article surfaced some useful WMI features you can use today with the help of the .NET framework. There's a lot more functionality included in WMI and plenty of .NET framework classes to help with the heavy lifting.

Jim Murphy works for Mindreef, Inc. building web services diagnostic tools.


Return to ONDotnet.com.

Copyright © 2009 O'Reilly Media, Inc.