WMI and .NET: System.Management Lets You Take Advantage of WMI APIs within Managed Code

MSDN Magazine

WMI and .NET

System.Management Lets You Take Advantage of WMI APIs within Managed Code

Zina Pozen
This article assumes you're familiar with WMI and C#
Level of Difficulty     1   2   3 
SUMMARY Visual Studio .NET and the Microsoft .NET Framework SDK provide a new set of APIs and tools that let you consume Windows Management Instrumentation (WMI) data and events from managed .NET applications. After presenting an overview of what's new for WMI in the .NET Framework and the Visual Studio .NET environment, the author provides an in-depth exploration of the Management Extensions in Visual Studio .NET for Server Explorer. These extensions help you develop management-aware software and come in handy in a variety of distributed application development scenarios.

You may have used Windows® Management Instrumentation (WMI) in the past to browse, modify, or surface management-related data and events. In thinking about moving to .NET, you're probably concerned about easy access to technologies such as WMI. Have they gotten any easier to use?
      Maybe you considered using WMI in the past, but were daunted by its COM-based, late-bound APIs, as well as the need to define or discover Common Information Model (CIM) schema as a separate development step. If that is the case, you should be pleased to find how much more approachable WMI has become since the advent of the .NET Framework.
      WMI is an object-oriented, standards-based framework for accessing management-related information. It hides the interface quirkiness of the underlying objects and exposes a uniform API, which can easily be used by a generic management application to access data and events, whether these are coming from the system or from an instrumented application. In fact, all major management solutions for the Windows platform rely on WMI, including Microsoft® Operations Manager (https://www.microsoft.com/mom) and Systems Management Server (https://www.microsoft.com/smserver/default.asp), IBM's Tivoli solution portfolio (https://www.tivoli.com/products), Hewlett-Packard's OpenView (https://www.openview.hp.com), Computer Associates' Unicenter (https://www.cai.com/products), and others.
      Starting with Windows 2000, WMI has been shipping as part of the operating system, and it is also available for download at the Microsoft Web site for Windows 9x or Windows NT® 4.0.

Why WMI?

      There are many good reasons to use WMI. First, WMI is extensible. It defines a model for plugging in provider modules that supply data and events surfaced in WMI. Instrumentation of the system devices and of applications such as Microsoft Internet Information Server (IIS) or SQL Server™ has been implemented by Microsoft and is included in WMI. Many ISVs are instrumenting their devices and applications to make them visible to (and configurable by) any WMI-aware management, monitoring, and profiling applications.
      WMI is also remotable. It relies on DCOM for remoting, using custom marshaling for improved performance.
      Another great feature of WMI is its high-performance, extensible event mechanism. A WMI event consumer can be notified when a WMI event is raised or when any WMI data object is created, deleted, or modified. For example, you can ask WMI to notify you when CPU usage on a system exceeds 90 percent. It is also possible to request an event notification at a certain time or repeatedly at regular intervals. In addition, WMI allows you to define a permanent consumer: a component that is invoked whenever a specified event has occurred. WMI ships with a set of standard consumers that perform such common actions as running a script, creating an event or file log entry, or sending e-mail upon receiving a WMI event.
      WMI allows the execution of data and event queries. The Windows Management Instrumentation Query Language (WQL), which is a subset of SQL with a few extensions, can be used to issue queries against WMI. A WQL query for all processes that consume more than 10MB of memory would look like this:

  SELECT * FROM Win32_Process WHERE WorkingSetSize >= 10485760
  

 

I'll talk more about this later in the article.
      WMI is scriptable, and starting with Windows XP, ships with a command-line interface tool making this framework easily accessible to administrators.
      What are some of the things your application can do with WMI? Using WMI, an application can detect and discover management information such as what operating system (including the service pack number) is installed on a server. An application can also determine how many processors the computer system has, which services are running in a process, which network connections are currently open, and which domains have a trust relationship with the local domain of the host computer.
      Your application can also perform actions against management objects such as stopping or starting a process or a service, deleting all disk drive shares, recursively unloading all running Web applications from a specific IIS virtual directory, and other nasty things! Of course, WMI performs these actions on behalf of the client, so you will be able to do only as much the client context permissions allow. Using WMI, an application can also receive management event notifications when a specified service stops, when the amount of free space on a system disk drops below a certain limit, when a process uses more than a specified amount of virtual memory, when an application has encountered a significant error condition and raised a WMI event, and so on.

Writing WMI Client Code with .NET

      The version of WMI that shipped with Windows 2000 exposed classic COM interfaces to client applications. Therefore, you can access WMI-instrumented management data and events via the .NET COM Interop layer. But there is a much better way to work with WMI from .NET. The System.Management namespace, defined as part of the .NET Framework, provides an easy-to-use, intuitive API for consuming WMI data and events. A lot of the complexities and idiosyncrasies of WMI APIs are taken care of by System.Management, and its class definitions follow the standard design paradigm of the .NET Framework.
      Several useful classes are defined in this namespace. ManagementObject represents a data object (a data instance, using WMI terminology). ManagementObjectSearcher allows you to retrieve an object collection based on a WQL query. For example, here is how a C# client would enumerate all started services:

  ManagementObjectSearcher searcher = new ManagementObjectSearcher
  
("SELECT * FROM Win32_Service WHERE Started = TRUE");
foreach (ManagementObject service in searcher.Get())
Console.WriteLine("Service = " + service["Caption"]);

 

The ManagementScope class represents a WMI namespace. Keep in mind that root\cimv2 is the default scope for System.Management API (this is where most of the system instrumentation resides). If you need to work with objects defined in any of the other WMI namespaces, you have to explicitly set the scope to indicate that when querying for data or events. All of the relevant APIs are overloaded to accept a ManagementScope parameter.
      The code in Figure 1 enumerates all running processes on the local machine using System.Management, and then listens for process creation events and prints out the name of a new process created. It demonstrates the use of the ManagementClass and ManagementEventWatcher classes.

ATL Wizards

      Visual Studio® .NET provides several new tools and APIs for writing WMI providers and instrumenting applications to WMI. To help with writing COM-based WMI providers, Visual Studio .NET offers two new ATL wizards that generate most of the code needed to get a simple instance, method, or event provider up and running in minutes (see Figure 2). Describing the wizards in detail is outside the scope of this article, but here are a few helpful tips to keep in mind as you begin to work with them.

Figure 2 New ATL Wizards
Figure 2 New ATL Wizards

      Define your instrumentation schema before you run the provider wizard. You can do this by creating a Managed Object Format (MOF) file and compiling it using the MOF compiler (mofcomp.exe), or by using a schema authoring tool such as CIM Studio. (CIM Studio is available in the WMI SDK, downloadable from https://msdn.microsoft.com/msdn-files/027/001/566/Search.asp.) Once the schema class you intend to populate from your provider is defined and committed to the WMI repository on your development machine, you will be able to select it in the ATL wizard. The provider code generated will then be tailored for the selected schema class, leaving you very little to do to build a skeletal provider. Essentially, you only need to supply the property values.
      When authoring class schema, pay attention to qualifiers. For example, do not forget to mark any methods you want to surface from your provider as implemented. This way, the method will become visible to the wizard and, again, the bulk of the code will be generated for you.
      My next tip is that the wizards assume that one provider COM object will correspond to one schema class. Therefore, if you want to populate multiple schema classes from the same provider DLL (and most providers do), you need to run the wizard once for each class that is provided. This results in much cleaner code, and the performance overhead is quite minimal.
      If you are planning to instrument an application, take a look at the MSDN® documentation on building a decoupled provider from ATL wizard-generated provider code ("Completing the WMI Event Provider Wizard"). A decoupled provider controls its own lifetime (as opposed to being loaded by WMI when a client request for its services comes in), so the decoupled model is a natural fit for application instrumentation scenarios. The decoupled provider subsystem ships with WMI in Windows XP and is also available as a redistributable Windows Installer merge module, so it can be deployed with your decoupled provider on downlevel Windows platforms.
      Another tip is that the wizard registers your provider with WMI as part of the project build step, so you can try out your new provider on the development machine as soon as the project has been successfully built.
      Keep in mind that deploying a provider involves more than just installing and registering a DLL. You also need to add your instrumentation and provider registration schema to the WMI repository on the deployment target machine. The provider registration schema is generated for you by the wizard and placed into the ProjectName.mof file in your ATL project. It is generally a good idea to paste the MOF definition of your instrumentation schema into the same MOF file and then add a deployment step to run mofcomp.exe against this .mof file.
      ATL wizards provide a useful tool for writing COM-based WMI providers in C++. And, as I mentioned, you can also add WMI instrumentation to your existing ATL-enabled project by harnessing the decoupled provider infrastructure.

System.Management.Instrumentation

      Instrumenting your application will enable application monitoring and profiling by standard tools such as Visual Studio Analyzer or Microsoft Operations Manager. And if you are developing your new project using managed code, there is a low-overhead way of adding instrumentation to your application using the System.Management.Instrumentation namespace, which is defined as part of the .NET Framework.
      With the help of classes defined in this namespace, you can fire WMI events or surface your code objects as WMI classes and instances. You can tag your class definition with the InstrumentationClass(InstrumentationType.Instance) attribute to surface its instances to WMI as data, or attribute them with InstrumentationClass(InstrumentationType.Event) to be able to fire them as events. Adding this attribute causes .NET to automatically generate the schema of the WMI event or data object to match your managed code object's class definition and also to generate the necessary WMI provider registration schema. You will need to call static methods on the Instrumentation helper class for firing events and publishing instance data (such as Instrumentation.Fire and Instrumentation.Publish).
      Since the solution offered by the System.Management.Instrumentation namespace is geared toward application instrumentation scenarios, it is based entirely on the decoupled provider model. This means that your instrumented .NET application will be in control of its lifespan, independent of any consumer request for its data or events. And if there are no outstanding requests for the data or events provided, the performance overhead of invoking the Instrumentation APIs is minimal.
      In the example in Figure 3, a BabyBorn class is defined and attributed so that its instantiation can be fired as a WMI event. In fact, the class itself handles the firing right in the constructor. I added a special .NET installer for the application, DefaultManagementProjectInstaller, which takes care of registering all generated CIM schema with WMI on the target machine when your instrumented application is deployed. You need to ensure that this install step is invoked, though, by creating an MSI package, running installutil.exe, or invoking it programmatically.

WMI Extensions for Visual Studio .NET Server Explorer

      Server Explorer is a new tool window in Visual Studio .NET. It acts as a distributed management console integrated into the development environment that allows you to view and manipulate database and system resources on multiple network servers. One of the goals of Server Explorer is to facilitate the monitoring and troubleshooting of systems in a distributed application environment. Using the Server Explorer window, you can stop and start services, view message queues and event logs, or add data connections to create and maintain databases on multiple machines—all without leaving the development environment.
      Just as important, Server Explorer acts as a design tool and interacts with other Visual Studio designers. Most of the Server Explorer nodes can be dragged and dropped to a designer surface, thus generating the code that would reference a corresponding component. For example, you can drop an event log node into your form in design mode; this action will generate the code to instantiate a System.Diagnostics.EventLog object, tied to the specific log you added to your form (Application, System, Security, or a custom log).
      There is a wealth of management information beyond what's available in Server Explorer, most of it surfaced by WMI. To be able to access it in your development environment, you need to download the WMI Extensions for Visual Studio .NET Server Explorer.
      The extension adds Management Classes and Management Events entries under each server node in your Server Explorer window (see Figure 4). The Management Classes node acts as a gateway for WMI data browsing and manipulation, while the Management Events node provides access to WMI eventing. I will describe both of them next.

Figure 4 Server Explorer
Figure 4 Server Explorer

      The Management Classes node initially expands to reveal a set of useful management objects (see Figure 4). This is just the tip of the iceberg, though. This tool allows you to view all management data surfaced by WMI, including your own classes if you instrumented your application to WMI or created a specialized WMI provider. The Add Classes context menu item that is available from the Management Classes node opens a dialog that allows you to search and browse all of the WMI namespaces for management data.
      The cross-namespace search feature is especially useful if you are not familiar with WMI namespace structure and class names (and few people are, considering how much information there is available through WMI). Let's say you are interested in retrieving BIOS information. Just type "bios" in the search textbox and this will find the Win32_BIOS class in root\cimv2 namespace (see Figure 5).

Figure 5 Searching WMI Namespaces
Figure 5 Searching WMI Namespaces

      Note that class names appearing in this dialog and elsewhere in the UI are not the real WMI class names (which can be pretty clunky), but rather their display names. So, for instance, you will encounter "All Files" instead of "CIM_DataFile." The real WMI class name can always be found in the Property Browser or in the description field, as you can see in Figure 5.
      Once you have added a class to the view, you will be able to see its properties in the Property Browser window. You can expand a class node to see the instances of the management objects (see Figure 4). So, for example, under the Processes class node you will find all processes running on the selected server.
      You can further expand an object node to reveal other management objects semantically related to this one. This feature relies on the WMI association mechanism, which defines the relationships between objects that cannot be expressed through inheritance or containment. For example, by expanding a process object node, you can discover which DLLs are loaded by this process. Or, if you expand a service object node, you will be able to see which other services are dependent on it, and which services it depends on.
      Many WMI classes define and implement methods. These can be invoked on a class (using static methods) or on a particular object. The methods can be accessed in Server Explorer through the context menu of a class node (for static methods) or of an object node (for nonstatic ones). Server Explorer only displays the methods that are actually backed by an implementation in the class or in a parent class (WMI supports implementation inheritance). When you select the name of the method you want to invoke from the context menu, Server Explorer opens a dialog box that lets you enter input parameter values, invoke the method, and view the output parameters.
      You can also create new management objects using the new extensions to Server Explorer. Select the Create New Object context menu item available on the class nodes. Very few dynamic WMI classes (that is, those surfaced by WMI providers) actually support creating new objects through this mechanism; most management classes that support create or bind operations do so through a static method call. However, the option is enabled and quite useful in the case of static (backed by the WMI repository engine) classes. For example, you can create a new WMI namespace by creating a new object under the WMI Namespaces class.
      You can also delete objects using the Delete Object context menu option, but this option is also not supported by many classes. Most classes define a Delete method instead of relying on this parameterless mechanism.

Strongly Typed Wrappers for WMI Classes

      Management nodes in Server Explorer also make use of strongly typed management objects, a new feature of WMI. Normally, all objects surfaced by WMI, whether they are accessed through COM, scripting, or System.Management, are late bound. This means that an object's properties are accessed through a Properties collection, its methods are accessed through a Methods collection, and all values are returned as System.Object. To retrieve the amount of free space available on a disk through a late-bound object (of type ManagementObject) from a C# client, the syntax you use should look like the code shown here:

  UInt64 freeSpace = 
  
(UInt64)(diskObject.Properties["FreeSpace"].Value);

 

      Such late-bound APIs present a uniform interface for all management objects, which makes them easily consumable by generic management applications. WMI namespaces, classes, and objects can all be programmatically enumerated, and the structure and content of each object discovered. (As an aside, the System.Reflection namespace in the .NET platform takes an alternative approach to class discovery, which works with managed code assemblies.)
      Unfortunately, the code manipulating late-bound objects is awkward to write and even more awkward to read, does not take advantage of the convenient IntelliSense® technology in Visual Studio .NET, robs the developer of compiler-time type safety, and requires constant use of supporting documentation. I know you would love to be able to retrieve the free space on a disk like this:

  UInt64 freeSpace = diskObject.FreeSpace;
  

 

And I'm sure you would like to have IntelliSense present you with a dropdown list of properties and methods and display their types and parameters in a tooltip.
      The problem is that the strongly typed class definition would need to be available at design time. To address this scenario, the .NET SDK and Visual Studio .NET provide a way to construct a strongly typed wrapper class for a WMI class, which can delegate the calls to the corresponding late-bound API. Once such a wrapper has been added to the client project, you can code to a WMI object as you would to any regular strongly typed .NET object.
      In the Visual Studio .NET environment, an easy method for creating a strongly typed wrapper for a WMI class is to drag an object node from the Server Explorer onto a form or a component in design mode. This action will generate a wrapper and add it to your project. It will then add the strongly typed object as a private data field to your form and bind it to the specific WMI object that you had selected for the drag and drop operation.
      For example, if you selected a process object from Server Explorer and dropped it into your form, a file named Win32_Process.cs (or Win32_Process.vb), which contains the definition of the wrapper class, would be added to your project. In addition to this, the process1 field will be added to your component and then instantiated and bound to a specific process inside the InitializeComponent method:

  private WindowsApplication1.ROOT.CIMV2.Process process1;
  
•••

this.process1 = new WindowsApplication1.ROOT.CIMV2.Process();

this.process1.Path = new
System.Management.ManagementPath("\\\\DEVBOX1\\root
\\cimv2:Win32_Process.Handle=\"4864\"");

 

      However, it rarely makes sense to code against a specific object. You might want to just generate the wrapper and code against it to enumerate or query WMI objects, and then perform actions against the objects that meet certain criteria (such as processes that use the most memory, rather than those with a specific handle). For that purpose, the strongly bound class definition of each object includes several overloaded static GetInstances methods, which let you perform object enumerations. The code in Figure 6 enumerates all running processes on the local box, the dropdown list shows their names, and the tooltip shows the syntax.
      Other overloads of GetInstances allow you to connect to remote machines (by specifying System.Management.ManagementScope pointing to a remote path), and filter out the resultset by specifying a query string. You can also specify which properties you are interested in; this is useful when retrieving potentially large or numerous objects remotely.
      To generate a strongly bound class definition for your project using Server Explorer (without instantiating an object of that class), select the Generate Managed Class context menu option on the class node. This feature can also be used instead of drag and drop when working with .NET projects that do not contain any designable components such as console applications.
      You can also generate a strongly bound class outside the Visual Studio .NET environment by running the MgmtClassGen.exe utility (in the .NET SDK) or even programmatically by calling one of the overloaded System.Management.ManagementClass.GetStronglyTypedClassCode methods.

Management Events

      Now let's take a look at the Management Events node installed by the WMI extensions for Visual Studio .NET Server Explorer. It allows you to build a WMI event query and subscribe for event notifications. This can be quite useful in a variety of development scenarios: if you want to monitor specific system changes in your distributed application test environment, be notified when specified thresholds are crossed, view the events your instrumented application fires, or test the WMI event provider you are developing.
      Initially, the Management Events node does not have any children. Select Add Event Query from its context menu to open the Event Query Builder dialog shown in Figure 7. Decide whether you want to receive event notifications when WMI objects are created, deleted, or modified (the Data Operation event type), or when custom WMI events are fired (the Custom event type). For those of you more familiar with WMI, these correspond to intrinsic and extrinsic events, respectively, in classic WMI terminology.

Figure 7 EventQuery Builder
Figure 7 EventQuery Builder

      Once you have selected the event type, you can search or browse WMI namespaces for relevant classes. If you are interested in data operation events, the classes available to you in this dialog will be all the classes that are nonabstract or have nonabstract children. These are the valid targets for building data operation queries. Once you've selected the class you are interested in, you will be able to pick the data operation events you want to receive: creation, deletion, modification of objects, or any of the above.
      Finally, you can specify the event polling interval—meaning how often the WMI engine should query the underlying data provider to detect the changes in its instances. As you probably realize, such polling may be expensive, especially if the interval is short and a lot of data is involved. It can also be error-prone if the rate of the change is greater than the polling interval. This is not an issue for certain areas of system instrumentation, as well as other classes backed by instance providers which also notify WMI of changes to their data (therefore acting as intrinsic event providers). When the provider is notifying the WMI engine of the data operations, it doesn't need to do the polling, so there will be no performance overhead or missed events with your query.

Building Event Queries

      To build a query that will always notify you of a custom event, just select the event class. If you want to further refine your query, you can click the Advanced button to open the dialog that lets you specify any conditions that the event objects returned should meet (essentially, build the where clause of the query). When dealing with custom events, you can also indicate any of the properties of the event object that you are interested in receiving.
      The event query you have created using this UI will appear as a child node under Management Events and will start listening for the appropriate events. It is important to note that the query builder UI is somewhat limited and cannot produce some of the valid WQL event queries (for example, it assumes that the query conditions are always simply conjuncted using the logical AND). So if you need to create a more sophisticated query expression using logical OR or parentheses, you can always modify the query string by changing the Query property of the event query node in the Property Browser window. This will stop the current event subscription and start a new one.
      Once an event matching your query criteria arrives, it will be printed to the Output window in Visual.Studio .NET. You can then select Refresh from the query node's context menu to add the event as a child to the query node. The reason event nodes are not being added automatically to the Server Explorer window is that these could be arriving at a fast pace, making it difficult to work with this UI. You can stop and start event subscriptions from the context menu of the event query node. You can also clear out the old event nodes this way. In Figure 8, you can see the event query and event nodes corresponding to the BabyBorn events you saw earlier.

Figure 8 CustomEvent Query
Figure 8 CustomEvent Query

      If you want to receive WMI event notifications in your managed application code, you can simply construct the event query in Server Explorer and drag the query node into your component in design mode. This adds an instance of the System.Management.ManagementEventWatcher class to your component and sets it to the query that you built. Then, to generate an EventArrived handler, switch to the Events tab of the Property Browser and type in the name of your event handler.
      In order for your component to start receiving events, set the scope of your ManagementEventWatcher object and call Start on this object, as is illustrated here:

  managementEventWatcher1.Scope = new 
  
System.Management.ManagementScope("root\\MyNamespace");

managementEventWatcher1.Start();

 

Conclusion

      The WMI Extensions for Visual Studio .NET Server Explorer is a useful tool for management-conscious development. The extensions present a simple and intuitive user interface that follows the same philosophy as the rest of the Server Explorer nodes. All the rich system and application information instrumented to WMI can be easily viewed and searched.
      Working together with the features of WMI and .NET, the extensions knock down the traditional barriers that stood in the way of wider adoption of WMI: difficult discoverability of manageable data and events, the need for a separate schema design step, awkward ways to use late-bound APIs, and clunky class names, to name a few. The new APIs and tools succeed in combining the power and flexibility of the original WMI architecture with the simpler, clearer programming paradigm of the Microsoft .NET Framework. In my opinion, the balance has been achieved. But don't just take my word for it; test-drive all these new features yourself and realize a whole new level of system management.

For related articles see:
Monitoring in .NET Distributed Application Design
Diagnostic Hooks
Introduction to Server Explorer
For background information see:
Windows Management Instrumentation: Administering Windows and Applications across Your Enterprise
About WMI

Zina Pozen is a software design engineer at Microsoft, working on some of the components described in this article. Over the last 10 years, she has made a transition from Moscow to New York to the emerald city of Redmond, WA, where different stages of the product cycle compensate for the lack of seasonal changes.

From the May 2002 issue of MSDN Magazine