An in-depth look at WMI and Instrumentation: Part II


Introduction

The first part of this article explained how to instrument your application. We looked at a simple WMI provider to demonstrate how to define WMI classes, how the .NET framework registers these classes in the WMI store, how to publish class instances and how to raise WMI events. On the other hand we looked at a simple WMI consumer to demonstrate how to query for WMI class instances and how to subscribe to and receive asynchronously WMI events. Finally we looked at Microsoft Operations Manager 2005, how to set it up to monitor WMI events raised by instrumentation applications and how to create Management Packs to distribute to customers. This gives a comprehensive view about how to instrument applications and how to tie this in with management applications like MOM 2005.

This second part looks in more detail at how to query the WMI store, how to work with WMI classes and class instances, and then demonstrates the wealth of information available through the Win32 and IIS WMI providers. This article has two sample applications attached. The "WMI System Browser" allows you to browse a wealth of information about your machine. The "WMI Power Browser" allows you to browse the WMI schema and look in detail at classes and class instances. All .NET types for working with WMI reside in the System.Management DLL, so you need to add the System.Management namespace to your code.

Working with WMI namespaces

WMI supports namespaces, allowing users to logically group WMI classes together. Each WMI provider normally registers its own WMI namespace and then all its classes within that namespace. For example, all Win32 WMI classes can be found in the namespace "root\cimv2", all IIS WMI classes can be found at "root\microsoftiisv2", and all LDAP WMI classes can be found at "root\directory\ldap". The root namespace is called "Root" and namespaces can have child namespaces and WMI classes. Each namespace contains a class called __namespace and its class instance list gives you a list of all child namespaces. Here is a code snippet for that:

// the root namespace we are connecting to
ManagementScope Scope = new ManagementScope(@\\.\Root);
// the __namespace class to connect to - which provides a list of
// all child namespaces in the namespace we are connecting to
ManagementPath ClassPath = new ManagementPath("__NAMESPACE");
// connect to the namespace and class
ManagementClass WMIParentObject = new ManagementClass(Scope, ClassPath, null);
try
{
// now loop through all the object instances we find for that namespace class
foreach (ManagementObject WMIChild in WMIParentObject.GetInstances())
{
// a child-namespace found
string ChildNamespace = Convert.ToString(WMIChild.Properties["Name"].Value);
// add the namespace name to the tree view
TreeNodeCol.Add(WMIChild.Path.Path, ChildNamespace.ToLower());
// release the underlying COM object
WMIChild.Dispose();
}
}
// show any exception happening
catch (Exception e)
{
MessageBox.Show(e.Message);
// release the underlying COM object
finally
{
WMIParentObject.Dispose();
}

It first creates a ManagementScope object and sets it to the root namespace on the local machine. This defines where (which machine and namespace) the class we are binding to is residing. Next we create a ManagementPath setting it to the WMI class we are binding to (the __namespace class). Finally we create a ManagementClass object using the scope and path object we created. The same could be achieved by only creating a ManagementClass object and passing along the path "\\.\root:__namespace". When you have bound to a WMI class, you can call GetInstances() to get a list of all class instances for this class. In the case of the __namespace class this is the list of child namespaces. The path and the name of each namespace is then used to add it to a tree view. The ManagementClass and ManagementObject classes use underlying unmanaged COM objects, so call Dispose to release them.

The WMI infrastructure allows you to apply security to each namespace. Open up the "Computer Management" console in Windows (right click on "My Computer" and choose Manage from the popup menu) and navigate to the item "Service and Applications" and then "WMI Control". Open up the properties of the "WMI Control" item and navigate to the "Security" tab. It allows you to navigate the namespace hierarchy (shown in a tree view). Select the one you want to set the security for and click on the "Security" button. This brings up the standard Windows ACL dialog and allows you to set the rights for each Windows user or Windows group. The available rights are:

  • Execute Methods - Allows user to execute methods defined on the WMI classes.

  • Full Write - Allows full read, write and delete access to WMI classes, class instances.

  • Partial Write - Permits write access to static WMI class instances

  • Provider Write - Permits write access to dynamic WMI class instances

  • Enable Account - Permits read access to WMI class instances

  • Remote Enable - Permits remote access to the namespace

  • Read Security - Permits read-only access to security information

  • Edit Security - Permits write access to security information

This allows you to set granular access rights for the classes and class instances in this namespace. The list of security rights shown is on Windows 2003. On Windows XP, "Enable Account" is named "Enable" and "Full Write" is named "Full Control". All other security rights are named the same.

Working with WMI classes

The class ManagementClass allows you to bind to and work with WMI classes. The path of a WMI class consists of the machine, followed by the namespace hierarchy ending with a colon and the class name itself, for example "\\.\Root\cimv2:Win32_Volume". The ManagementClass provides access to the following information:

  • SystemProperties - A collection of system properties, which are properties defined by WMI and always start with two underscores. For a complete list click here. Knowing the meaning of some of those system properties helps a lot. Let's take as an example the class __Win32Provider in the "root\cimv2" namespace on the local machine which inherits from __Provider, which itself inherits from __SystemClass.

    • __Class - The WMI class name; in our example it is __Win32Provider.

    • __Superclass - The class name this class inherits from; in our example __Provider.

    • __Derivation - The full class hierarchy for multiple inheritances; in our example "__SystemProvider, __SystemClass". Note this is an array of strings.

    • __Dynasty - The top level class this class inherits from; in our example __SystemClass. This value is the same as __Derivation if the class inherited from has no parent class, such as the class __Provider (inherits from __SystemClass which has no parent class).

    • __Path - The full path for this WMI class or class instance; in our example "\\.\root\cimv2:__Win32Provider".

    • __Relpath - The relative path of the WMI class or class instance which excludes the machine and namespace (this is everything after the colon of the full path). In our example this is __Win32Provider.

    • __Namespace - The namespace this class or class instance is residing in; in our example "root\cimv2".

    • __Server - The server we query; in our case the local machine.

    • __Property_Count - The number of properties (excluding system properties) defined on this class. This includes inherited properties.

This collection is of the type PropertyDataCollection which is a collection of PropertyData types. The type PropertyData exposes Name and Value properties. The property IsArray can be used to determine whether Value is an array of values or a single value.

  • Properties - A collection of properties. This allows reading and writing property values on a class instance. This is also of the type PropertyDataCollection, which is again a collection of PropertyData types.

  • Qualifiers - A collection of qualifiers assigned to this class. You can find a complete list of qualifiers here. Some examples are:

    • Abstract - This is an abstract class which cannot be instantiated

    • Dynamic - Class instances are not stored in the WMI store but are created dynamically by the provider. All WMI classes created through .NET instrumentation (see part I of this article) are marked dynamic.

    • Provider - The provider for dynamic WMI class instances (see part I of this article).

    • Singleton - Only one instance of this class can exist.

This is a collection of the type QualifierDataCollection which is a collection of QualifierData types which exposes Name and Value properties to read the name and value of the qualifier.

  • Methods - A collection of methods defined by this WMI class which can be invoked by class instances. This collection is of the type MethodDataCollection which is a collection of MethodData types. The MethodData type exposes a Name property for the method name and InParameters and OutParameters properties which provide access to all in and out parameters of the method. InParameters and OutParameters are of the type ManagementBaseObject. They have a "Properties" property, of type PropertyDataCollection which is a collection of the type PropertyData, providing access to each individual in or out parameter. You can read the name, the value, check if it is an array of values or a single value, etc. The return value of the method is an out parameter with the name ReturnValue.

These four collections provide you with a wealth of information about the WMI class. They can be used to read information about a WMI class and also to edit a WMI class (shown by the attached "WMI Power Browser" application). Here is a code snippet illustrating how to read class information.
 
ManagementClass WMIClass = null;
try
{
WMIClass =
new ManagementClass(@\\.\root\cimv2:__Win32Provider);
// add all the properties to the list
foreach (PropertyData Property in WMIClass.Properties)
ListCollection.Add(
new ListViewItem(new string[] { "Property",
Property.Name, GetPropertyValue(Property) }));
// add all the system properties to the list
foreach (PropertyData Property in WMIClass.SystemProperties)
ListCollection.Add(
new ListViewItem(new string[] { "SystemProperty",
Property.Name, GetPropertyValue(Property) }));
// add all the qualifiers to the list
foreach (QualifierData Qualifier in WMIClass.Qualifiers)
ListCollection.Add(
new ListViewItem(new string[] { "Qualifier",
Qualifier.Name, Convert.ToString(Qualifier.Value) }));
// add all the methods to the list
foreach (MethodData Method in WMIClass.Methods)
ListCollection.Add(
new ListViewItem(new string[] { "Method",
Method.Name, String.Empty }));
}
// show any exception happening
catch (Exception e)
{
MessageBox.Show(e.Message);
}
// release the underlying COM object
finally
{
if (WMIClass != null)
WMIClass.Dispose();
}

This code snippet binds to the __Win32Provider class in the root\cimv2 namespace and then reads all its properties, system properties, qualifiers and methods and adds for each the name and value to a list view (note that methods have no values). At the end it calls Dispose to release the underlying COM object. The value of properties or system properties can be arrays so it calls GetPropertyValue() which does the following:

public string GetPropertyValue(PropertyData Property)
{
// a single value property so we return its value as string value
if (!Property.IsArray)
return Convert.ToString(Property.Value);
// return a comma separated list of array values
else
{
string PropertyValue = String.Empty;
// loop through all items in the array; only if we have items (!= null)
if (Property.Value != null)
{
foreach (object Value in ((Array)Property.Value))
PropertyValue += Convert.ToString(Value) + ", ";
}
// return the comma separated list of values for this array
if (PropertyValue.Length > 0)
return PropertyValue.Substring(0, PropertyValue.Length - 2);
else
return String.Empty;
}
}

It checks through IsArray if this is an array or single value. If a single value it converts the value to a string and returns it. For an array of values it loops through each item and adds it to a comma-separated list which is then returned.

Working with WQL queries

You can query WMI using the WQL query language. WQL is based on the standard SQL syntax with some differences, the biggest one being that it allows only queries and not updates, inserts or deletes. It allows you to query for classes, class instances, class associations, inherited classes, etc. Queries are always executed against a namespace. Here are a few examples:

  • SELECT * FROM meta_class
    Queries for all classes in the namespace.
  • SELECT * FROM meta_class WHERE __CLASS = "Win32_Volume"
    Queries for a class with the name Win32_Volume.
  • SELECT * FROM meta_class WHERE __this ISA "Win32_Volume"
    Queries for a class with the name Win32_Volume and all its child classes. So any class which directly or indirectly is inherited from this class is returned.
  • SELECT * FROM Win32_Volume WHERE __CLASS = "Win32_Volume"
    Queries for any class instances of the class Win32_Volume.
  • SELECT * FROM Win32_Volume
    Queries for any class instances of the class Win32_Volume and any other class directly or indirectly inherited from it.

To execute a WQL query create first a ManagementScope object which defines against which namespace you want to perform this query, for example "\\.\root\cimv2". Next create an instance of WqlObjectQuery and pass along the query WQL string. Finally create an instance of ManagementObjectSearcher and pass along the scope and query objects. Call ManagementObjectSearcher.Get() which performs the search and returns a collection of ManagementObject objects. Here is a sample code snippet:

// the namespace we are searching in
ManagementScope Scope = new ManagementScope(@\\.\root\cimv2);
// the query we are performing
WqlObjectQuery Query = new WqlObjectQuery("SELECT * FROM meta_class");
// enumeration options to use for the search; set a time-out value
EnumerationOptions Options = new EnumerationOptions();
Options.Timeout =
new TimeSpan(0, 0, 10);
// instantiate the searcher object using the scope and query
ManagementObjectSearcher Searcher = new ManagementObjectSearcher(Scope, Query, Options);
try
{
// now get the search results and loop through all found object instances
foreach (ManagementObject Object in Searcher.Get())
{
// add the object to the list view
Collection.Add(new ListViewItem(new string[] { Object.Path.Path,
Object.Path.RelativePath } ));
// dispose the object instance
Object.Dispose();
}
}
// show any exception happening
catch (Exception e)
{
MessageBox.Show(e.Message);
}
// dispose the underlying search COM object
finally
{
Searcher.Dispose();
}

This code snippet sets the search scope to the namespace "root\cimv2" and searches for all classes in that namespace. It also sets a timeout of 10 seconds for enumerating the result-set, not for how long the actual search is allowed to take. It adds for each found object the full and relative path to a list view. Don't forget to call Dispose() on each object in the enumeration and on the ManagementObjectSearcher object to release the underlying COM objects.

You can also build associations between different classes using association classes. Association classes are classes which have references to two other classes and through that associate these two classes. The diagram below shows the associations between the Win32_DiskPartition class with the Win32_ComputerSystem, Win32_DiskDrive and Win32_LogicalDisk class. For each there is an association class:


  • Win32_SystemPartitions - Associates the Win32_DiskPartition class with the Win32_ComputerSystem class, meaning a partition belongs to a computer system. The GroupComponent property on this class contains the reference to the Win32_ComputerSystem class while the PartComponent property contains the reference to the Win32_DiskPartition class.

  • Win32_DiskDriveToDiskPartition - Associates the Win32_DiskDrive class with the Win32_DiskPartition class, meaning a disk drive belongs to a partition. The Antecedent property on this class contains the reference to the Win32_DiskDrive class while the Dependent property the reference to the Win32_DiskPartition class.

  • Win32_LogicalDiskToPartition - Associates the Win32_LogicalDisk class with the Win32_DiskPartition class, meaning a logical disk belongs to a partition. The Dependent property on this class has the reference to the Win32_LogicalDisk class and the Antecedent property the reference to the Win32_DiskPartition class.

WMI allows you to query for those associated classes and the association classes which reference the two classes together. This makes it easy to find all associations and association classes without having to go through the documentation of all classes, which in the case of "root\cimv2" number in the hundreds. Let's assume your computer has an instance of the Win32_DiskPartition class with the value "Disk #0, Partition #0" and you want to find all associated classes and class instances and association classes and class instances. Here is how it works:

  • ASSOCIATORS OF {Win32_DiskPartition.DeviceID="Disk #0, Partition #0"}
    Returns all class instances which are associated with this Win32_DiskPartition class instance. The result may vary based on your hardware and software configuration. My machine returns an instance of the Win32_ComputerSystem class (value Enterprise-Minds), an instance of the Win32_LogicalDisk class (value "C:") as well as an instance of the Win32_DiskDrive class (value "
    \\.\PHYSICALDRIVE0"). Note that Win32_DiskDrive is your physical disk drive while Win32_LogicalDiskDrive is the logical disk created through your OS.

  • ASSOCIATORS OF {Win32_DiskPartition.DeviceID="Disk #0, Partition #0"} WHERE ClassDefsOnly
    This query returns the WMI class names of all the classes associated with the Win32_DiskPartition class. My machine returns Win32_ComputerSystem, Win32_DiskDrive and Win32_LogicalDiskDrive.

  • REFERENCES OF {Win32_DiskPartition.DeviceID="Disk #0, Partition #0"}
    Returns all the association class instances which have a reference to this Win32_DiskPartition class instance, meaning one of the properties contains a reference to this class instance. My machine returns an instance of the Win32_DiskToDiskPartition class, an instance of the Win32_SystemPartitions class and an instance of the Win32_LogicalDiskToPartition class.

  • REFERENCES OF {Win32_DiskPartition.DeviceID="Disk #0, Partition #0"} WHERE ClassDefsOnly
    This query returns the class names of all the associations classes referencing the Win32_Partitions class. My machine returns Win32_DiskDriveToDiskPartition, Win32_LogicalDiskToPartition and Win32_ComputerSystem.

WMI provides a wealth of information, so it is important to have strong query/search capabilities and to be able to get a good view of the associations between the data. WQL is build on top of SQL which makes the query language instantly familiar to developers but is still enhanced to support WMI specific capabilities. You can find out more about WQL from here as well as here .

Working with WMI class instances

The ManagementObject class can be used to work with WMI class instances. The ManagementClass type is inherited from the ManagementObject type. This means nearly all the functionality explained in the section "Working with WMI classes" applies also to WMI class instances. The two exceptions are that the property values for a class are default values (if specified) while they return the actual value for a class instance, and there is no Methods collection, meaning there is no access to the method information. You can obtain the class information of a class instance through the ClassPath property. This returns a ManagementPath type which provides access to the class name (ClassName property), the server queried (Server property), the namespace (property NamespacePath), the full path (Path property) and the relative path (RelativePath property).

Through ManagementObject.InvokeMethod() you can invoke a method on the class instance. Managed WMI classes (i.e., those provided by managed code) cannot provide methods. But many of the unmanaged WMI classes provide methods which you can invoke to manipulate the class instance. For example the class Win32_Volume has a Chkdsk() method to perform a check disk, a DefragAnalysis() method to perform defrag analysis, a Defrag() method to perform a defrag and many more. Query the WMI class to obtain a list of methods. When you know the method name use the method ManagementObject.GetMethodParameters() to obtain a ManagementBaseObject with the list of in parameters for the method. Next set the values for all in parameters and then call ManagementObject.InvokeMethod() to invoke the method. It returns another ManagementBaseObject type which contains the values of all the out parameters plus the method return value. The method return value is stored in the out parameter list as an out parameter with the name ReturnValue. Here is a code snippet:
 
ManagementObject WMIObject = null;
string ObjectPath = @"\\.\root\cimv2:Win32_Proxy.ServerName=""Enterprise-Minds""";
string MethodName = "SetProxySetting";
try
{
// bind to the WMI object instance
WMIObject = new ManagementObject(ObjectPath);
// get a WMI object representing all in-parameters for this method
ManagementBaseObject InParams = WMIObject.GetMethodParameters(MethodName);
// set all the in-param values according to the in-param list view
foreach (ListViewItem Item in InParamItems)
InParams[Item.SubItems[0].Text] = Item.SubItems[1].Text;
// set the method invoke options so that the method has 10 sec to execute
InvokeMethodOptions Opt = new InvokeMethodOptions();
Opt.Timeout =
new TimeSpan(0, 0, 10);
// execute the method on the WMI object and get the out-parameters
ManagementBaseObject OutParams = WMIObject.InvokeMethod(MethodName, InParams, Opt);
// read all the return values and show them in the out-param list view
foreach (PropertyData Property in OutParams.Properties)
OutParamItems.Add(
new ListViewItem(new string[]
{
Property.Name,
GetPropertyValue(Property) }));
// dispose the COM object of the in param object
if (InParams != null)
InParams.Dispose();
// dispose the COM object of the out param object
if (OutParams != null)
OutParams.Dispose();
}
// show any error message
catch (Exception e)
{
MessageBox.Show(e.Message);
}
// free up the underlying COM object
finally
{
if (WMIObject != null)
WMIObject.Dispose();
}

The code snippet first binds to the Win32_Proxy class instance (note that Enterprise-Minds is the machine name, which will of course be different for your machine) and then gets the method in parameters from the GetMethodParameters() method. It then reads all the in parameter values from the InParamItems list view. Then it invokes the InvokeMethod() which returns all the out parameters and the method return value. These are then added to an OutParamItems list view. At the end it calls Dispose on the in parameter and out parameter types and the class instance itself to release the underlying COM objects. Note that some out parameter values might be another WMI class instance. The "WMI Power Browser" sample application attached shows how to check for the type of the return value and then read the property values of such a returned WMI class instance.

You can also delete class instances, unless they are dynamic. First bind to the class instance and then call the method ManagementObject.Delete(). Here is a code snippet for that:

ManagementObject WMIObject = null;
try
{
WMIObject =
new ManagementObject(ObjectPath);
// delete the object
WMIObject.Delete();
}
// show any exception happening
catch (Exception e)
{
MessageBox.Show(e.Message);
}
// release the underlying COM object
finally
{
if (WMIObject != null)
WMIObject.Dispose();
}

Property values of a class instance can be updated. These changes will not be committed till you call ManagementObject.Put(). The provider for dynamic class instances needs to support write access to the property. No managed class instances support write access. Here is a code snippet showing how to update property values:

ManagementObject WMIObject = null;
try
{
WMIObject =
new ManagementObject(ObjectPath);
// loop through all the properties in the list view and get the values
foreach (ListViewItem Item in ItemCollection)
WMIObject.Properties[Item.SubItems[0].Text].Value = Item.SubItems[1].Text;
// write any changes back to the WMI store
WMIObject.Put();
}
// show any exception happening
catch (Exception e)
{
MessageBox.Show(e.Message);
}
// release the underlying COM object
finally
{
if (WMIObject != null)
WMIObject.Dispose();
}

This sample takes all the new property values from an ItemCollection list view. It first binds to the class instance and then loops through the list view, gets the latest values and updates the object property values with it. It then calls ManagementObject.Put() to commit the changes back to the WMI store (or the provider for dynamic classes). At the end it again calls Dispose() to free up the underlying COM object.

The "WMI Power Browser" sample application

This sample application demonstrates how to enumerate the list of WMI namespaces and all classes for each. It also demonstrates how to perform queries, whether querying for all the classes of a namespace, all class instances of a class, all associated classes and class instances, all association classes or class instances, etc. It also allows you to enter a WQL query string and execute the query, showing its result set. This makes it easy to experiment with WQL and see right away the result set. The sample also demonstrates how to create new classes, edit or delete new classes, clone class instances and edit or delete class instances. It also allows you to view all the methods available on a class instance and then to invoke them. The rich Windows form interface (using tree views, list views, etc.) makes it very easy to discover all namespaces, classes and class instances. At the same time you can step through the code and understand in all details how to work with WMI namespaces, classes and class instances.

The "WMI System Browser" sample application

This sample application shows how you can use WMI to find out a wealth of information about any machine on your network. The "WMI System Browser" shows only a subset of information available through WMI. In the list on the left side you can select a category, for example "processor information", "BIOS information", "IIS web sites", etc. The list in the middle then shows you all found instances, for example it shows each found processor, each found IIS web site, etc. Selecting a specific instance shows the details in the list on the right side, for example the maximum and current clock speed of a processor, the serial number and release date of the BIOS, etc. Click on the "Show class description" button to launch the MSDN help page of the underlying WMI class. This helps a lot to understand all the properties available on a class. If the class exposes methods then you can click on the "Invoke a method" button. It lists for you on the left side all available methods. Selecting a method shows you in the list in the middle all in parameters. You can set the values for each by clicking on it. Click on the "Invoke method" button to invoke the method and show the out parameters in the list on the right side. Click on the "Show method description" to launch the MSDN help page for the selected method. This helps to understand the in and out parameters of each method.

It is very simple to extend the sample application and really understand what information WMI can provide. This could even be a simple troubleshooting tool, helping the user understand the hardware and software configuration of a remote machine. Much of the information shown here can be useful when resolving issues. Here is a list of information you can view, and which WMI class is used for it:

  • IIsWebServiceSetting - Returns configuration information about the IIS itself. This includes ASP settings, logging settings, authentication settings, etc.

  • IIsApplicationPoolSetting - Returns information about all IIS application pools (IIS 6.x specific). For each you can see the user credentials used, application pool recycling policy information, etc.

  • IIsWebServerSetting - Lists all the web sites configured on your machine and for each the ASP settings, log settings, authentication settings, etc.

  • IIsWebVirtualDirSetting - Returns all the created virtual web directories and for each the path, ASP settings, authentication settings, etc.

  • Win32_Battery - Lists information about each battery in your computer: its device ID, power management capabilities, etc.

  • Win32_BIOS - Lists information about the computer BIOS: its name, serial number, release date, etc.

  • Win32_ComputerSystem - Returns information about the computer system including the physical memory size, the boot options, the primary owner, the hardware model and manufacturer, etc.

  • Win32_ComputerSystemProduct - Lists information about your hardware system including the serial number, name, vendor, version, etc.

  • Win32_DiskPartition - Returns the created disk partitions (a disk partition may span multiple physical disks and contain multiple logical disks). For each you find the size, block size, number of blocks, etc.

  • Win32_Environment - Lists all defined environment variables. For each you can see the name, value, if it is a system or user defined variable, etc.

  • Win32_Group - Lists all the Windows groups of your local machine as well as of the domain, if connected to a domain. For each group you can see the name, domain, SID, etc.

  • Win32_LogicalDisk - Returns all logical disks defined on your system and for each the file system, the driver letter, the size and free space, etc.

  • Win32_NetworkAdapterConfiguration - Lists all the network configurations (not the physical network adapters). For each network adapter configuration you see the IP address, subnet mask, is it DHCP enabled, etc.

  • Win32_NTDomain - Lists all the domains the machine belongs to (your local computer is returned as a domain too) and information like the name, domain controller address, etc.

  • Win32_OperatingSystem - Returns information about the Windows OS like build number, serial number, name, OS type, etc.

  • Win32_OSRecoveryConfiguration - Shows the OS recovery configuration including where to create the dump file, whether to auto reboot, etc.

  • Win32_PageFile - Returns information about the Windows page file including the file path, the file size, etc.

  • Win32_PhysicalMemory - Lists all physical memory banks in your system and for each the capacity, speed, data width, etc.

  • Win32_PnPDevice - Returns a list of plug-and-play devices and for each the name, the device ID, its status, etc.

  • Win32_Printer - Lists all the printers defined on your OS. It shows a wealth of configuration information including the port used, paper sizes supported, etc.

  • Win32_Process - Returns a list of all running processes of your OS. For each process it shows the executable path, environment variables, min and max working set size, etc.

  • Win32_Processor - Returns all processors in your system and for each the current and maximum processor speed, its current load percentage, etc.

  • Win32_ProgramGroup - Lists all Windows program groups defined on this Windows system. For each it returns the name, the user it belongs to, etc.

  • Win32_Proxy - Returns the proxy server information which includes the proxy server and port, etc.

  • Win32_QuickFixEngineering - Lists all the Windows hot fixes installed on this Windows system. For each it shows the hotfix ID, which Service Pack it will be part of, who installed it, etc.

  • Win32_Service - Lists all Windows services of your OS. For each service it shows the name, the executable path, the start mode, the user credentials used, etc.

  • Win32_Share - Returns a list of file shares defined on your OS. For each it returns the share name, path, status, etc.

  • Win32_Thread - Returns a list of all running threads of your OS. For each thread it shows the total execution time and kernel time in milliseconds, its priority and the process it belongs to, etc.

  • Win32_TimeZone - Returns the currently selected time zone for your OS. Returns its name, daylight savings information, etc.

  • Win32_UserAccount - Lists all the Windows users of your local machine as well as of the domain, if connected to a domain. For each user you can see the name, domain, SID, etc.

  • Win32_Volume - Returns the list of volumes and for each the capacity, the free size, the file system, etc.

This list already provides a wealth of information about a machine. There are many more classes available and you can use the "WMI Power Browser" to discover them and each class instance.

Summary

WMI provides a powerful way to instrument your application and to allow management tools like Microsoft Operations Manager 2005 to tie into your application and proactively notify IT operators. But WMI also provides a wealth of information about systems which you can leverage in your enterprise applications. It is also very easy to collect that information for remote machines or to invoke actions on class instances remotely. The .NET framework provides a comprehensive and easy approach to working with WMI. It makes it very easy to create WMI classes, expose dynamic WMI class instances, raise WMI events as well as discover and interact with existing WMI classes and class instances.

Hopefully future versions of the .NET framework will remove the existing limitations, the biggest being that you cannot implement methods, cannot provide write access to properties and only provide dynamic classes. WMI is still vastly underutilized in today's enterprise applications. Hopefully these two articles provide a better insight into how to use WMI and the power of WMI.