devxlogo

Exploring WMI: Integrate WMI into the .NET Framework

Exploring WMI: Integrate WMI into the .NET Framework

Windows Management Instrumentation (WMI) is one of the most fascinating technologies for managing distributed corporate computer systems. As the Windows implementation for WBEM (Web Based Enterprise Management), a standard developed by the DMTF (Distributed Management Task Force), WMI allows a completely new approach for managing system resources on the Windows operating system?including not only PCs and servers, but also many network devices such as switches and routers.

You can now access BIOS data, memory, software components, and network devices through one simple, unified object model, referred to as the Common Information Model (CIM). One of the most common uses for WMI is the creation of VBScript or JScript scripts in the Windows Scripting Host environment to automate repetitive administrative tasks.

This article discusses the WMI and its integration with the .NET Framework Namespace System.Management.

System Requirements
To get the most benefit from this article, you need a good understanding of C# and object-oriented programming. A good knowledge of the Windows environment, including Services, Event Logs, and Active Directory, will be very helpful too. Although a deep understanding of the system internals isn’t necessary, understanding its basics will be advantageous.

WMI is available for WinME, Windows 2000, or newer operating systems. The WMI SDK includes a WMI Provider for Windows NT 4.0 SP 3. Versions for Windows 9x are also available, although with some limitations.

The examples in this article should be used with the final release of Visual Studio.NET (2002) on Windows 2000 or XP machines. For older versions of the Windows operating system, use the WMI Providers in the WMI SDK, which you’ll find in the download section of http://msdn.microsoft.com.

What Is WBEM and What’s It Got to Do with WMI?
When you start using WMI you will come across the acronym WBEM and likely ask yourself: What’s this all about? It stands for Web Based Enterprise Management, the technological standard behind WMI. Still unclear? Let me explain the ideas behind WBEM.

In the past few years, the management of large computer systems has grown more and more complex, establishing the need for a new, easy-to-adapt/implement management technology. Since Active Directory focuses on the management of user accounts and groups in an organization, a component that aggregates information about hardware was still missing. Typically, you find many different types of hardware in one organization: routers, switches, media converters, data storage systems, desktops, servers, etc.?all from different vendors running different operation systems. Maybe you have already experienced the difficulty of trying to find all available information about your local workstation. Now imagine how complex it would be to find all attributes of a network that contains several hundred devices. You would have to use many different tools from various vendors to access the needed information. This way of collecting hardware data is very cost intensive and, of course, quite error prone.

The total cost of ownership for large corporate networks clearly needed to come down. A good starting point was a new protocol/object model that unified the ways IT professionals ask different computer systems about their vital hardware information. Enter WBEM (see Figure 1). In 1998, the DMTF took ownership of the independent WBEM project and combined it with their CIM (currently in its second version, CIMV2).

CIM is a relatively simple model for managing a computer system’s objects. It contains management objects for software and hardware elements. You can use CIM to represent relations between objects (e.g., a group that contains several user accounts). CIM itself is an open standard that the DMTF developed and maintains, so you can enhance it with your own management classes. The important CIM attribute is that it standardizes the way system components are accessed when information is needed.

Figure 1: WBEM Architecture

As stated previously, WMI is the windows implementation of the WBEM standard. So WMI offers a management infrastructure that represents the objects in a Windows operation system environment in a CIM conform way. Since WMI is also based on COM (Component Object Model) technology, you can also use it from ActiveX-capable scripting environments such as Windows Scripting Host (WSH) or Active Server Pages. In fact, this integration into the WSH makes WMI the best invention since sliced bread. Administrators can use it to check the attributes of all hardware devices in a network and start appropriate script actions (e.g., restarting a system service on any machine where it has stopped, or adding registry keys to each machine without having to log on locally to every workstation in the corporation). For further information on this topic, take a look at the Related Resources section in the left column.

CIM uses the Managed Object Format (MOF) description language (which is derived from IDL, the Interface Description Language) to model the CIM schemas. The schema describes the components of a system and represents information about their relationships. The schema data contained in the .mof files are in a machine- and human-readable format. You can take a look at them in the Windows subdirectory %windir%System32WBEM. Just open them with your favorite text editor if you need more in-depth information about an object.

How’s CIM Fit In?
Now that you know how information about individual objects is stored, you can examine the repository that stores the information for all objects of a specific system: the CIM Object Manager. The CIM Object Manager defines a structure, the CIM Repository Schema, which describes the enterprise-wide layout and context of management class objects. It is also the connection point for the various technologies that can access WMI.

Figure 2: CIM Repository Architecture

As Figure 2 illustrates, you can access the data in the CIM Repository in many ways. One of the most common is to use the WMI ActiveX controls with scripting languages to easily create administration scripts for your enterprise. You can also use WMI from within ADO or ODBC providers. However, the most interesting way to connect to the CIM Repository definitely is the .NET Framework with the namespace System.Management.

As you may have noticed, Figure 2 also shows a block with providers. These are the connectors from the CIM Object Manager to the represented software or hardware. Many different providers are installed on standard Windows machines. Some of the typical providers are:

  • Win32 Provider
  • WDM Provider
  • Event Log Provider
  • Registry Provider
  • Performance Counter Provider
  • Active Directory Provider
  • Windows Installer Provider

Many other providers are available from third-party vendors. You can even write custom WMI providers for your application needs. You will need the WMI SDK to create any custom providers. You can download the SDK from the Microsoft website.

For more information visit the DMTF Web site or the Microsoft Web site.

Important Classes from the System.Management Namespace
ManagementBaseObject
ManagementBaseObject is the base class for most of the management object classes. It contains basic elements of a management object like the ClassPath, Properties Collection, and SystemProperties Collection (e.g., the class name, server, and namespace; WMI system property names always begin with “__”.). It also contains Get- and Set- methods for accessing the Properties and Qualifiers of a managed object.

The ManagementBaseObject class is derived from the class System.ComponentModel.Component, which provides the base implementation for the IComponent interface and enables object sharing between applications. Component is the base class for all components in the common language runtime (CLR), which marshal by reference. Component also is remotable and derives from MarshalByRefObject. It provides an implementation of IComponent. You can host a Component in any object that implements the IContainer interface, and can query and get services from its container.

ManagementObject
The ManagementObject class represents a data management object, which is an instance of a management class from the Common Information Model. (Note the difference between a managed class, which represents an entity in the WMI Repository, and a .NET class, which represents the code for a .NET object.) ManagementObject is derived from ManagementBaseObject, therefore it allows access to the Properties and Qualifiers Collections. Additional information contained in this class are the Scope (where you are connected and which credentials are you using), the connection Options, and a few others. Some very interesting parts of the ManagementObject are the public methods Get, Put, and InvokeMethod, which you use to bind to the management object, to save changes, and to invoke methods of the object. You can also access associated management objects (e.g., users are associated with groups) with the two public methods GetRelated and GetRealtionship.

ManagementObjectCollection
The ManagementObjectCollection represents different collections of WMI instances like management objects, namespaces, scopes, and query watcher. You use this class to enumerate instances of management classes. For example, one management class represents a Windows service but many instances of the class can exist. For each service installed on your machine, there is one management object of type Win32_Service. You will obtain this collection of running instances from your management class to obtain the details of all services installed on your machine.

ManagementClass
The ManagementClass is derived from ManagementObject, and it represents a management class from the Common Information Model. You can use this class to obtain all instances of ManagementObjects in a ManagementObjectCollection by calling the GetInstances method or to create new instances by calling the CreateInstance Method. It also contains a property Methods, which gets or sets a collection of MethodData objects that represent the methods defined in the WMI class.

The System.Management namespace contains many more classes, but they are far beyond the scope of this article. For further information take a look at the MSDN Library.

Manage Windows Services with WMI
Now that you know about WMI and its implementation in the .NET Framework, you can write your first sample application. For this purpose, this article shows you how to write a simple Windows Service manager, similar to the one you’ll find in the Management Console. The final application should look similar to the Figure 3 screenshot (just ignore the titlebar, it’s not one of the default Windows XP schemes).

Figure 3: Screenshot of the Sample Application?A Service Browser

Application Layout
The base of your sample application will be a standard Windows Application project. The application will contain two classes: the main form class, which contains the form, all controls, and all event handlers, and a ServiceNode class, which is derived from the ListViewItem class. Each object of the class ServiceNode will represent one Service in the ListView, and it will also contain data that belong to the management object of the CIMV2 class Win32_Service. You can find a detailed description of the Win32_Service class in the help file from the Platform SDK.

Building the Windows Service Manager
UI Design
Start with the main form. Before adding new controls, change the following properties:

(Name) ServiceForm
Text Local Services

Add a MainMenu to the Form. Then add a menu item ‘File’ and the submenu items ‘Refresh’, Seperator, and ‘Exit’. Then modify the following settings:

(Name) mMain
? ?
(Name) miFile
Text &File
? ?
(Name) miRefresh
Text &Refresh
Shortcut F5
? ?
(Name) miExit
Text E&xit
Shortcut AltF4

Now add the MainMenu mMain to the ServiceForm by setting the ‘Menu’ property.

Next, add a Toolbar to the application. You will use it instead of menu items to access the service commands. In a real world application, you’d probably add a menu, a toolbar, and a context menu for this purpose. The following table shows the properties of the toolbar and the toolbar buttons.

(Name) ServiceToolbar
Appearance Flat
ShowToolTips True
Buttons (Collection) (click […] to access the buttons collection)
? ?
(Name) tbbResume
Text Resume
ToolTipText Resume Service
Tag Resume
? ?
(Name) tbbStart
Text Start
ToolTipText Start Service
Tag Start
? ?
(Name) tbbStop
Text Stop
ToolTipText Stop Service
Tag Stop
? ?
(Name) tbbPause
Text Pause
ToolTipText Pause Service
Tag Pause
? ?
(Name) tbbRestart
Text Restart
ToolTipText Restart Service
Tag Restart

Now you can add the last control to your application, a ListView. It will be designed as a detailed list containing the columns ‘Name’, ‘Description’, ‘Status’, ‘Startup Type’, and ‘Log On As’. The following table shows the properties of the ListView.

(Name) lvServices
FullRowSelect True
GridLines True
View Details
HideSelection False
MultiSelect False
Dock Fill
Columns (Collection) (click […] to access the columns collection)
? ?
(Name) colName
Text Name
Width 120
? ?
(Name) colDescription
Text Description
Width 120
? ?
(Name) colStatus
Text Status
Width 100
? ?
(Name) colStartup
Text Startup Type
Width 100
? ?
(Name) colLogOnAs
Text Log On As
Width 100

Congratulations! You have finished the UI design of your sample application. Now, build and start the program. Check if the interface looks similar to the screenshot. (See figure 3). (Without the services listed, you still need to code that part.)

The ServiceNode Class
Create a new class called ‘ServiceNode‘ and add it to your project. Before you start typing code, you need to add the reference for the assembly System.Management.dll. It contains the namespace System.Management, which you will use in your class. Right click on the references node of the project in the solution explorer and select Add Reference… (see Figure 4).

Figure 4: Microsoft Visual Studio?Solution Explorer

In the resulting dialog (shown in Figure 5), select the .NET tab and find System.Management in the list. Add it to the list of selected assemblies by double clicking it. Click OK to close the dialog, and take a look at the References node in solution explorer to make sure that the reference has been added.

Figure 5: Microsoft Visual Studio?Add Reference Dialog

Now that you have added the necessary reference, you can start implementing the ServiceNode class.

First, you need to import all used namespaces: System for all the base classes, System.Management for WMI, and System.Windows.Forms for the ListViewItem class from which you want to derive your ServiceNode class:

using System;using System.Management;using System.Windows.Forms;

Next, add the derivation to your class definition and define an enumeration that will represent the possible service states. This enumeration is not very useful for your own ServiceNode class, but it can really help other coders who need to use your class. So for the sake of good code, add it:

namespace ServiceManager{		public class ServiceNode : ListViewItem	{		public enum ServiceState		{			Start,			Stop,			Pause,			Resume		}	}}

Now, you need a ManagementObject field in your class to store the reference to an instance of a Win32_Service management class. Just add it right at the beginning of your class:

protected ManagementObject mo = null;

With that finished, you can address the constructor for your class. You need a constructor that receives a management object and fills all the necessary parts of the derived ListViewItem. To achieve the highest possible code reuse, perform a little trick: create a property that represents your ManagementObject, and then, in the set part of the property code, transfer the values from the CIM to the ListViewItem. Having such a property in your class, you can create a very short constructor. Just assign the object you get in the constructor toy your property and you’re done:

public ServiceNode(ManagementObject moService){	ServiceObject = moService;}public ManagementObject ServiceObject{	get	{		return mo;	}	set	{		mo = value;		if(mo!=null)		{		SubItems.Clear();		Text = (string)mo.Properties["Name"].Value;		SubItems.Add((string)mo.Properties["Description"].Value);		SubItems.Add((string)mo.Properties["State"].Value);		SubItems.Add((string)mo.Properties["StartMode"].Value);		SubItems.Add((string)mo.Properties["StartName"].Value);		}	}}

As you can see in this code, you use the Properties Collection from the ManagementObject class to access the various properties of the Win32_Service CIM class. Since the Value property of the Collection item is of type object, you need to cast it to a string to add it to the SubItems collection of your ListViewItem. You will add the ListViewItem itself to the ListView lvServices later.

To make your class more useable, add a few public properties that enable easy access to the object properties stored in the ListViewItem:

public string Name{	get	{		return Text;	}}public string Description{	get	{		return SubItems[1].Text;	}}public string State{	get	{		return SubItems[2].Text;	}}public string StartupType{	get	{		return SubItems[3].Text;	}}public string LogOnAs{	get	{		return SubItems[4].Text;	}}public bool CanPause{	get	{		return (bool)mo.Properties["AcceptPause"].Value;	}}

Although you could always use the management object, I decided to use the SubItems collection to access the desired information. However, nothing’s stopping you from using the Properties collection of your management object.

The last parts of your ServiceNode class are two Methods: ChangeState and Refresh. You use the ChangeState method to call the InvokeMethod method on your management object to start, stop, pause, and resume the service. This call takes two parameters: the first is the name of the method to be called, and the second is an object array with the arguments for the call. Since your methods don’t need any arguments, you can use null instead of the array.

The Refresh method is used to refresh the current ListViewItem. You could also use the Refresh method in your ServiceObject property. But I decided to implement this behavior separately so as not to unnecessarily obfuscate your code:

public void ChangeState(ServiceState newState){	switch(newState)	{		case ServiceState.Start:			mo.InvokeMethod("StartService", null);			break;		case ServiceState.Stop:			mo.InvokeMethod("StopService", null);			break;		case ServiceState.Pause:			mo.InvokeMethod("PauseService", null);			break;		case ServiceState.Resume:			mo.InvokeMethod("ResumeService", null);			break;	}			}public void Refresh(){	mo.Get();	SubItems.Clear();	Text = (string)mo.Properties["Name"].Value;						SubItems.Add((string)mo.Properties["Description"].Value);	SubItems.Add((string)mo.Properties["State"].Value);	SubItems.Add((string)mo.Properties["StartMode"].Value);	SubItems.Add((string)mo.Properties["StartName"].Value);}

Coding the Application Logic
After finishing your ServiceNode class, continue with the code for the application logic. Because your sample is simple, you can add this code directly to the form class.

First off, add the necessary namespace declaration to the header of your class file:

using System.Management;

Now, create a method RefreshServiceList, which will be called in the constructor every time a user selects the Refresh menu item:

void RefreshServiceList(){	lvServices.Items.Clear();	ManagementClass mc = new ManagementClass("Win32_Service");	foreach(ManagementObject mo in mc.GetInstances())		lvServices.Items.Add(new ServiceNode(mo));	foreach(ToolBarButton b in ServiceToolBar.Buttons)		b.Enabled = false;}

This method clears the Items collection of your ListView lvServices. Then it creates a new object of the type ManagementClass, which takes the name of the CIMV2 class as a constructor parameter. Since you want windows services, take 'Win32_Service'.

To access the individual services that are installed on your machine, you need to call the method GetInstances() on your ManagementClass object. This call returns an object of type ManagementObjectCollection, which you can iterate with a foreach loop to access the ManagementObject instances that are contained with the collection. Each instance of a service is now used with the constructor of your ServiceNode class to create a new instance of a ServiceNode, which is then added to the Items collection of your ListView lvServices.

The second foreach loop in this method is used to disable all buttons on the toolbar because the clearing of the Items collection in the ListView also cleared the SelectedItem, and without any item selected, no action could be performed.

Now you can add the call to RefreshServiceList to the constructor of your Form:

public ServiceForm(){	InitializeComponent();	RefreshServiceList();}

To create the event handler for the SelectedItemChanged event of your ListView, simply double click on the ListView in the form designer. You will add application logic to enable and disable the toolbar buttons according to the features of your services:

private void lvServices_SelectedIndexChanged(object sender, System.EventArgs e){	if(lvServices.SelectedItems.Count!=1)	{		foreach(ToolBarButton tbb in ServiceToolBar.Controls)			tbb.Enabled = false;	}	else	{		ServiceNode sn = (ServiceNode)lvServices.SelectedItems[0];		switch(sn.State)		{			case "Running":				tbbStart.Enabled = false;				tbbStop.Enabled = true;				tbbRestart.Enabled = true;				tbbResume.Enabled = false;				tbbPause.Enabled = true;				break;			case "Stopped":				tbbStart.Enabled = true;				tbbStop.Enabled = false;				tbbRestart.Enabled = false;				tbbResume.Enabled = false;				tbbPause.Enabled = false;				break;			case "Paused":				tbbStart.Enabled = false;				tbbStop.Enabled = true;				tbbRestart.Enabled = true;				tbbResume.Enabled = true;				tbbPause.Enabled = false;				break;			default:				foreach(ToolBarButton tbb in ServiceToolBar.Controls)					tbb.Enabled = false;				break;		}		if(!sn.CanPause)			tbbResume.Enabled = tbbPause.Enabled = false;	}}

If no service is selected in your ListView, you simply disable all your toolbar buttons. If a service is selected, you need to differentiate the state of the Service. Create a switch statement on the property ServiceNode.State. Depending on the state of the server, you either enable or disable the toolbar buttons. In the case of no valid state or an unknown state, simply disable all buttons. Last but not least, check whether the service cannot pause and disable the Resume and Pause buttons.

Next, implement the event handlers for your menu items. Just double click on the menu item Refresh and then Exit. The implementation of these two event handlers is straightforward:

private void miExit_Click(object sender, System.EventArgs e){	this.Close();}private void miRefresh_Click(object sender, System.EventArgs e){	RefreshServiceList();}

When the user clicks on the Exit menu item, you simply shut your application down by calling the Close() method of your form. When the user clicks on Refresh, you rebuild your ListView with a call on the RefreshServiceList() method.

Testing Time
You can test the application now. It should display a list of all installed services, and the toolbar buttons should be enabled accordingly to the state and features of the selected service.

What’s still missing is the action that should take place when the user presses one of your toolbar buttons. Luckily, you have everything prepared: your ServiceNode class is able to call the InvokeMethod method on your management object. But how do you know when a service finishes an invoked operation? Starting and stopping services takes some time. You can’t permanently refresh your ListViewItem until you get a different state from the object. The solution would be to use WMI Events, but these are far beyond the scope of this article. So stick with a simple solution, and wait a few seconds before you update the status of your item:

private void ServiceToolBar_ButtonClick(object sender, 
System.Windows.Forms.ToolBarButtonClickEventArgs e){ Cursor = Cursors.WaitCursor; ServiceNode sn = (ServiceNode)lvServices.SelectedItems[0]; switch((string)e.Button.Tag) { case "Resume": sn.ChangeState(ServiceNode.ServiceState.Resume); break; case "Pause": sn.ChangeState(ServiceNode.ServiceState.Pause); break; case "Start": sn.ChangeState(ServiceNode.ServiceState.Start); break; case "Stop": sn.ChangeState(ServiceNode.ServiceState.Stop); break; case "Restart": sn.ChangeState(ServiceNode.ServiceState.Stop); sn.ChangeState(ServiceNode.ServiceState.Start); break; } System.Threading.Thread.Sleep(5000); sn.Refresh(); lvServices_SelectedIndexChanged(this, new System.EventArgs()); Cursor = Cursors.Default;}

To show the user that your program is doing some important, time-consuming work, change the Cursor on your form to WaitCursor. You get the reference to the selected service into the nice, short named variable sn. Since you stored the state string for the services in the tag of your toolbar buttons, you can use it to decide which action should be performed on the selected service. After calling the ChangeState method, which in turn calls InvokeMethod on the management object, you need to wait a few seconds for the service to finish its work. You do this with a call on System.Threading.Thread.Sleep. Yes, this is a quick and dirty hack, but a better solution is beyond the scope of this article.

When the sleeping time of your thread is over, refresh your service node. To get the toolbar buttons updated, you simply call the event handler for SelectedIndexChanged. You have already implemented the logic for your toolbar buttons there, so no need to type that code sequence twice. Now that your work unit is finished, you can put the cursor back to its default state.

Running the Windows Service Manager
Now you can build and run the application. You should get a list of installed services just like in the first screenshot of your application. Try to start and stop a service, and use the service applet from the management console to control the success of your actions.

Author Note: Be careful when stopping or pausing services. Some of them may be vital to your system. If you shut a vital service down, you can cause damage to your system. Try the program with some unimportant services like the index service or the alerter service.

What Have You Learned?
This article introduced the concepts of WMI and addressed the following questions:

  • Why do you need something like WMI?
  • How does it represent the WBEM Standard?
  • Who is responsible for this standard?

After the theoretical introduction to WBEM and the concepts developed by the DMTF, it examined the System.Management Namespace and the classes it contains. Special attention was put on the ManagementBaseObject, ManagementObject, ManagementClass, and the ManagementObjectCollection, since these are the classes needed for the first sample program.

The sample program is a Windows Service Manager, similar to the management console applet for Windows services. Included features are listing all services (including name, description, status, and startup type) and the manipulation of services (start, stop, restart, pause, and continue).

Limitations and Further Work
As stated previously, simply waiting five seconds and then updating the service status isn’t a clean solution. You should use WMI events to make this happen cleanly and asynchronously. A nice feature that you could add would be a remote connection to another machine or the ability to specify the user account that should be used to impersonate on WMI.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist