Search Forum
(57415 Postings)
Search Site/Articles

Archived Articles
712 Articles

C# Books
C# Consultants
What Is C#?
Download Compiler
Code Archive
Archived Articles
Advertise
Contribute
C# Jobs
Beginners Tutorial
C# Contractors
C# Consulting
Links
C# Manual
Contact Us
Legal

GoDiagram for .NET from Northwoods Software www.nwoods.com


              
Printable Version

An in-depth look at WMI and instrumentation, Part II
By Klaus Salchner

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
  • Edit 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 an array of strings.
  • __Dynasty 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
  • __Relpath which excludes the machine and namespace (this is everything after the colon of the full path). In our example this is __Win32Provider.
  • __Namespace
  • __Server
  • __Property_Count

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 writing property values on a class instance. This is also of the type PropertyDataCollection, which is again a collection of PropertyData types.

  • Qualifiers href="http://msdn.microsoft.com/library/en-us/wmisdk/wmi/wmi_qualifiers.asp"> here. Some examples are:

    • Abstract
    • Dynamic .NET instrumentation (see part I of this article) are marked dynamic.
    • Provider
    • Singleton

      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 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 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 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 the IIS itself. This includes ASP settings, logging settings, authentication settings, etc.
    • IIsApplicationPoolSetting application pools (IIS 6.x specific). For each you can see the user credentials used, application pool recycling policy information, etc.
    • IIsWebServerSetting
    • IIsWebVirtualDirSetting
    • Win32_Battery
    • Win32_BIOS
    • Win32_ComputerSystem owner, the hardware model and manufacturer, etc.
    • Win32_ComputerSystemProduct 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 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 system and for each the file system, the driver letter, the size and free space, etc.
    • Win32_NetworkAdapterConfiguration 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 (your local computer is returned as a domain too) and information like the name, domain controller address, etc.
    • Win32_OperatingSystem like build number, serial number, name, OS type, etc.
    • Win32_OSRecoveryConfiguration configuration including where to create the dump file, whether to auto reboot, etc.
    • Win32_PageFile including the file path, the file size, etc.
    • Win32_PhysicalMemory system and for each the capacity, speed, data width, etc.
    • Win32_PnPDevice
    • Win32_Printer sizes supported, etc.
    • Win32_Process
    • Win32_Thread 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 your OS. Returns its name, daylight savings information, etc.
    • Win32_UserAccount 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 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. If you have comments on this article, please contact me @ klaus_salchner@hotmail.com. I want to hear if you learned something new. Contact me if you have questions about this topic or article.

    Download Code.zip

    About the author

    Klaus Salchner has worked for 14 years in the industry, nine years in Europe and another five years in North America. As a Senior Enterprise Architect with solid experience in enterprise software development, Klaus spends considerable time on performance, scalability, availability, maintainability, globalization/localization and security. The projects he has been involved in are used by more than a million users in 50 countries on three continents.

    Klaus calls Vancouver, British Columbia his home at the moment. His next big goal is doing the New York marathon in 2005. Klaus is interested in guest speaking opportunities or as an author for .NET magazines or Web sites. He can be contacted at klaus_salchner@hotmail.com or http://www.enterprise-minds.com .

    Enterprise application architecture and design consulting services are available. If you want to hear more about it contact me! Involve me in your projects and I will make a difference for you. Contact me if you have an idea for an article or research project. Also conact me if you want to co-author an article or join future research projects!