5.6 WMI and the .NET Framework


5.6 WMI and the .NET Framework

The .NET Framework programming environment is an extensive subject. Even if we only consider the relationship that exists between WMI and the .NET Framework, this topic represents enough information to at least dedicate an entire chapter in this book. However, in this book, we have considered the WMI scripting aspect without considering the programming aspects from languages such as C, C++, or Visual BASIC. Looking at the .NET Framework implies that we leave the scripting world to enter the world of programming with languages such as C# or VB.NET. Therefore, we will just give a brief overview of the .NET Framework WMI capabilities, since this topic is beyond the scope of this book. Some basic knowledge about the .NET Framework architecture, its concepts, and its usage from Visual Studio.NET are required.

5.6.1 The .NET Framework WMI information access

In this book, we have exclusively used the SWbemScripting COM objects to access WMI information from scripts. However, from C or C++ applications, it is also possible to access the same WMI information by using the native WMI COM interfaces, known as the IWbem interfaces. Once the .NET Framework is part of the system, another technology is available to applications accessing WMI information. The .NET WMI object model exposed by the System.Management and the System.Management.Instrumentation .NET namespaces are designed to support the utilization and the providing of WMI information:

  • The System.Management namespace: This .NET namespace contains .NET classes that provide access to a rich set of management information and management events about the system, devices, and applications instrumented to the WMI. Applications and services can query for interesting management information, using .NET classes derived from ManagementObjectSearcher and ManagementQuery classes, or subscribe to a variety of management events using the ManagementEventWatcher class. Simply said, the System.Management namespace offers the required functionality for the WMI clients to access management data.

  • The System.Management.Instrumentation namespace: This .NET namespace provides the .NET classes necessary to instrument applications for management and expose their management information and events through WMI to potential consumers. Consumers can then easily manage, monitor, and configure these applications acting as WMI information providers. The management data is available for WMI scripts or any other applications. Simply said, the System.Management.Instrumentation namespace offers the required functionality for WMI applications to provide management information (managed code) to potential WMI clients. It offers an alternate route for developing WMI providers to the C and C++ COM interfaces.

These two .NET namespaces are totally independent from each other. For instance, if an application provides information with the System.Management.Instrumentation namespace, this does not limit the visibility of the instrumentation to the System.Management classes, which means that the information provided can be utilized by any other WMI information types (i.e., WMI COM API). On the other hand, if you use the System.Management classes, this will allow you to access any WMI information, not just the instrumentation written using the System.Management.Instrumentation namespace.

Using the .NET Framework technology to access WMI information implies that we use a programming object model different from the one used for scripting. Does it mean that all material we learned before is not applicable anymore? Well, from a coding technique point of view, it is clear that a completely new object model must be relearned, since it imposes a new coding technique besides the new .NET languages adaptations. However, since the .NET Framework acts as a WMI consumer relying on the existing WMI infrastructure (i.e., WMI COM objects, CIMOM, CIM repository), all WMI classes and information provided under Windows NT, Windows 2000, Windows Server 2003, and Windows XP remain valid. This is where we clearly see the advantage of the abstraction layer that the CIM repository and the CIM Object Manager provide!

Besides the new WMI .NET object model, version 1.00 of the .NET Framework acted as a WMI provider as well. It brought a new WMI provider, called NetFrameworkv1Provider provider. Known as the Configuration provider, it supported a collection of WMI classes to manage the .NET Framework and its components. However, subsequent versions of the .NET Framework do not include this provider anymore.

Basically, the .NET Framework is an addition to the existing WMI infrastructure. It is an add-on from a WMI application programming point of view (to develop WMI consumers and providers) and from a .NET Framework manageability point of view (since it brings a WMI provider to manage the .NET application configuration settings).

Figure 5.34 shows the three WMI tiers and identifies how the System.Management namespace is layered on WMI: Microsoft Windows Forms (Windows Forms), Web Forms (ASP.NET), and management applications can act as clients that access the WMI instrumentation. At the same time, management providers can be either existing codes, which wrap system or application instrumentation, or Windows Forms and Web Forms/ASP.NET applications, which expose management instrumentation about themselves to other clients by using the System.Management.Instrumentation namespace.

click to expand
Figure 5.34: The .NET Framework and the WMI architecture.

In this section, we will only cover the WMI information utilization aspect supported by the System.Management namespace.

5.6.2 Accessing WMI information from Visual Studio.NET

Before doing a brief overview of the .NET coding technique regarding the WMI information access, it is interesting to note that Microsoft provides a WMI-based management extension to the Visual Studio.NET Server Explorer tool. This component adds two new nodes to the Visual Studio.NET Server Explorer tool:

  • The Management Data node: Management Data allows the application developer to browse and modify WMI data as well as invoke methods.

  • The Management Events node: Management Events enable the application developer to register for WMI events.

This add-on can be downloaded from http://www.microsoft.com/down-loads/release.asp?ReleaseID=31155. Once installed, you can start the Visual Studio.NET Server Explorer tool by pressing Ctrl+Alt+S. Then you can browse the "Management Classes" node and, for instance, select the "Disk Volumes" node and expand the tree until you get the drive list shown in Figure 5.35.

click to expand
Figure 5.35: The WMI-based management extension to the Visual Studio.NET Server Explorer tool.

By selecting the C: drive, you will recognize the information exposed by the Win32_LogicalDisk class (display name "Disk Volumes" for the C: instance). Note that the Server Explorer tool doesn't show the real class name; rather, it shows the class display name. The class display name corresponds to the value stored in the displayname Qualifier available from the WMI class definition. This could be confusing the first time, since you likely worked with real class names rather than class display names (especially in the WMI COM environment). As shown in Figure 5.35, the Win32_DiskVolume class has a "Disk Volumes" display name, where "Disk Volumes" is the value assigned to the displayname Qualifier of the Win32_DiskVolume class.

By expanding the "Disk Volumes," the Server Explorer shows all existing instances of the class (i.e., A:, C:, D:, and E:). Next, if you expand one instance of the Win32_LogicalDisk class (i.e., C:), the Server Explorer will show all class display names of instances associated with the expanded instance (i.e., directories, partitions, volume quota, etc.). Behind the scenes, Server Explorer uses the association classes defined in the CIM repository to establish the link. Moreover, Server Explorer allows you to see the properties of a class or instance and execute methods from classes (static methods) or instances (nonstatic methods). If you plan to execute a method, the Visual Studio.NET Server Explorer tool will prompt you for the method input parameters. Once the method executes, it will show you the method execution result with the eventual output parameters. However, by default, not all classes from the CIM repository are visible from the Visual Studio.NET Server Explorer tool. By right-clicking on the Management Data node and selecting Add Class from the context menu, you get the dialog shown in Figure 5.36.

click to expand
Figure 5.36: Browsing WMI namespaces for classes.

This window allows you to search and browse in the CIM repository across all namespaces and class names. Again, this window shows the class display names, but note that the real class name is visible in the description at the bottom of the window.

With this extension, it is also possible to subscribe to WMI events. For this, you need to right-click on the "Management Events" node and select the "Add Event Query" selection. At that time, the window shown in Figure 5.37 appears.


Figure 5.37: Event subscription from Visual Studio.NET.

First, you must select a class from the "Available Classes" window. In Figure 5.37, the "Services" display name corresponds to the Win32_Service class. Once the class display name is selected and the condition of event delivery selected with an eventual polling interval, the event query configured in Figure 5.37 will correspond to the following WQL event query:

 Select * From __InstanceModificationEvent Within 5 Where TargetInstance ISA 'Win32_Service' 

Once a modification occurs to one of the Windows services, Visual Studio.NET will show the instance modification, as shown in Figure 5.38 in the "Output" window (bottom of the screen).

click to expand
Figure 5.38: Viewing events from Visual Studio.NET.

To summarize, we can say that the WMI-based management extension to the Visual Studio.NET Server Explorer tool is an updated, integrated, and combined version of WMI CIM Studio, WMI Object Browser, and WBEMTEST.Exe tools.

5.6.3 Accessing WMI information from a .NET language

Even if the object model is different from the one we used in the scripting environment, the type of functionality provided by the .NET System.Management classes is similar to the one provided by the WMI COM interfaces. All properties and methods exposed by the WMI COM interfaces have their equivalents in the System.Management classes. As before, the event subscriptions and method executions are available as well. Of course, because the .NET Framework abstracts WMI COM interfaces with the System.Management and the System.Management.Instrumentation classes, it is clear that an adapted coding technique from a language such as C# or VB.NET is required. We won't cover all aspects and details of .NET programming in C# or VB.NET here. Since .NET is an entirely new implementation in several strategic areas, I encourage you to gather more information about this subject, which is mainly available from the Microsoft .NET Framework SDK. Today, there are loads of publications available on this matter as well. However, through some small examples, we will quickly see that the functionality level you get from the .NET System.Management classes is similar to the one you have from the WMI COM API. If you master the capabilities of the WMI COM API (and you should, as you arrive at the end of this book), it will be quite easy to reproduce logics developed in VBScript or Jscript from WSH with a .NET language and the System.Management classes.

Because the startup is always the most painful step, let's examine a very basic WMI sample in C#, performing a WMI connection and listing all properties of all instances of the Win32_Service class (see Sample 5.23).

Sample 5.23: Connecting and retrieving all Windows Services instances with their properties with the System.Management classes from C#

start example

  1:    using System;  2:    using System.Management;  3:  4:    class Constants  5:               {  6:               public const string UserID = "LISSWARENET\\Administrator";  7:               public const string Password = "password";  8:               public const string ComputerName = "net-dpen6400a.LissWare.NET";  9:               public const string NameSpace = "Root\\CIMv2"; 10:               public const string WMIClass = "Win32_Service"; 11:               } 12: 13:    class WMISample 14:          { 15:          public static int Main(string[] args) 16:                 { 17:                 ConnectionOptions options = new ConnectionOptions(); 18:                 options.Username = Constants.UserID; 19:                 options.Password = Constants.Password; 20:                 options.Authentication = AuthenticationLevel.Default; 21:                 options.Impersonation = ImpersonationLevel.Impersonate; 22: 23:                 ManagementScope scope = new ManagementScope(); 24:                 scope.Path = new ManagementPath("\\\\" + Constants.ComputerName + 25:                                                 "\\" + Constants.NameSpace); 26:                 scope.Options = options; 27: 28:                 try { 29:                     scope.Connect(); 30:                     } 31:                 catch (Exception Err) 32:                     { 33:                     Console.WriteLine("Failed to connect: " + Err.Message); 34:                     return 1; 35:                     } 36: 37:                 ManagementClass WMIClass = new ManagementClass(Constants.WMIClass); 38:                 WMIClass.Scope = scope; 39: 40:                 ManagementObjectCollection WMIInstanceCollection = WMIClass.GetInstances(); 41: 42:                 foreach (ManagementObject WMIInstance in WMIInstanceCollection) 43:                         { 44:                         Console.WriteLine(); 45:                         PropertyDataCollection.PropertyDataEnumerator propertyEnumerator = 46:                                                WMIInstance.Properties.GetEnumerator(); 47:                         while (propertyEnumerator.MoveNext()) 48:                               { 49:                               PropertyData property = (PropertyData)propertyEnumerator.Current; 50:                               Console.WriteLine(property.Name + " = " + property.Value); 51:                               } 52:                         } 53:                 return 0; 54:                 } 55:      } 

end example

Sample 5.23 executes the following WMI operations:

  • Performs a WMI remote connection (lines 17 through 35): The remote connection parameters are stored in an object called options made from the ConnectionOptions .NET class (line 17). Each parameter of this object holds one parameter for the connection. For instance, the username and password properties contain the credentials (lines 18 and 19), while the authentication and impersonation properties determine the WMI authentication and impersonation mode, respectively (lines 20 and 21). Next, the system name and the WMI namespace to access are specified in a ManagementScope object created from the ManagementScope class (lines 23 through 25). The ManagementScope object information is completed with the connection parameters previously created (line 26). Once done, the WMI connection is executed (line 29). In case of error, the exception is handled with a "Try ... catch" C# statement (lines 28 through 35). All connection parameters are defined in constants. These values are available from the Constants .NET class defined from line 4 through 11.

  • Retrieves the list of Windows Services available (lines 37 and 40): Before retrieving all Windows Services available, the code sample creates a ManagementClass object from the ManagementClass .NET class (line 37). This object represents the WMI class given in reference during the object creation, which is the Win32_Service WMI class defined as a constant at line 10. With the ManagementClass object, the C# code requests all instances of the Win32_Service WMI class by invoking the GetInstance() .NET method (line 40). Note that the ManagementClass object knows about the connection details, since it reuses the ManagementScope object created for the connection (line 38). Because the connection is previously established (line 29) and because the ManagementClass object reuses the same ManagementScope object, the connection is not recreated and the connection channel is reused. The GetInstance() .NET method invocation creates a new ManagementObjectCollection object (line 40), which contains a collection of all Windows Services instances available from the connected system.

  • For each Windows service in the collection, the code retrieves all properties with their values (lines 42 through 52): To display the Windows services available, the ManagementObjectCollection object must be enumerated. Therefore, a loop performs this enumeration (lines 42 through 52). For each Windows service instance we have in the collection, the C# code retrieves the property list available from the instance by creating a PropertyDataCollection.PropertyDataEnumerator object (lines 45 and 46). Next, for each property we have in this new collection, the code enumerates each property one by one (lines 47 through 51) and retrieves the property in a Property-Data object (line 49) to display its name and its corresponding value (line 50).

As we can see in Sample 5.23, we perform a task we already did many times from WSH. From a functional point of view, there is no difference. However, the coding and the object references are totally different. This makes sense, since we use another language (i.e., C#) and programming object model.

We can also submit WQL queries as we did from the WMI COM interfaces. Sample 5.24 uses the same structure to retrieve the same set of information as Sample 5.23. However, instead of invoking the GetInstances() .NET method, it performs a WQL data query.

Sample 5.24: Connecting and retrieving all Windows Services instances with their properties with a WQL data query with the System.Management classes from C#

start example

  1:    using System;  2:    using System.Management;  3:  4:    class Constants  5:          {  6:          public const string UserID = "LISSWARENET\\Administrator";  7:          public const string Password = "password";  8:          public const string ComputerName = "net-dpen6400a.LissWare.NET";  9:          public const string NameSpace = "Root\\CIMv2"; 10:          public const string WQLQuery = "Select * From Win32_Service"; 11:          } 12: 13:    class WMISample 14:          { 15:          public static int Main(string[] args) 16:                 { ..: 37:                 ManagementObjectSearcher WMIInstanceSearcher = new ManagementObjectSearcher(); 38:                 WMIInstanceSearcher.Query = new ObjectQuery(Constants.WQLQuery); 39:                 WMIInstanceSearcher.Scope = scope; 40: 41:                 ManagementObjectCollection WMIInstances = WMIInstanceSearcher.Get(); 42: 43:                 foreach (ManagementObject WMIInstance in WMIInstances) 44:                         { ..: 53:                         } 54:                 return 0; 55:                 } 56:            } 

end example

The first modification concerns line 10. This line defines the WQL data query in a new constant. On line 38, this constant is referenced to create an ObjectQuery object made from the ObjectQuery .NET class. Next, this object is stored in a new ManagementObjectSearcher object (line 38) created previously (line 37) from the ManagementObjectSearcher .NET class. As in Sample 5.23, the ManagementObjectSearcher object reuses the ManagementScope object created previously (skipped lines 16 through 37) to establish the WMI connection (line 39). Next, a ManagementObjectCollection object is created (line 41). Instead of creating this object from the GetInstance() .NET method, it is created by invoking the WMI search with the Get() .NET method exposed by the ManagementObjectSearcher object. The search is based on the WQL data query assigned to the ManagementObjectSearcher object (line 38). Next, as before, the C# code enumerates all instances available with their properties and values (lines 43 through 53).

If we talk about WQL data queries, we should talk about WQL event queries as well. This is the purpose of Sample 5.25, which performs an asynchronous WQL event query.

Sample 5.25: Connecting and performing a WQL event query to display Windows Services instances subject to a modification with the System.Management classes from C#

start example

  1:    using System;  2:    using System.Management;  3:  4:    class Constants  5:               {  6:               public const string UserID = "LISSWARENET\\Administrator";  7:               public const string Password = "password";  8:               public const string ComputerName = "net-dpen6400a.LissWare.Net";  9:               public const string NameSpace = "Root\\CIMv2"; 10:               public const string WQLQuery = "SELECT * FROM __InstanceModificationEvent" + 11:                                              "WITHIN 5 " + 12:                                              "WHERE TargetInstance ISA \"Win32_Service\""; 13:               } 14: 15:    class WMISample 16:          { 17:          public static int Main(string[] args) 18:                 { ..: 30:                 WQLEventQuery WQLEventQuery = new WqlEventQuery(); 31:                 WQLEventQuery.QueryString = Constants.WQLQuery; 32: 33:                 ManagementEventWatcher WMIEventWatcher = new ManagementEventWatcher(); 34:                 WMIEventWatcher.Scope = scope; 35:                 WMIEventWatcher.Query = WQLEventQuery; 36: 37:                 WMIEventHandler WMIHandler = new WMIEventHandler(); 38:                 WMIEventWatcher.EventArrived += 39:                                 new EventArrivedEventHandler(WMIHandler.Arrived); 40: 41:                 try { 42:                     WMIEventWatcher.Start(); 43:                     } 44:                 catch (Exception Err) 45:                     { 46:                     Console.WriteLine("Failed to connect: " + Err.Message); 47:                     return 1; 48:                     } 49: 50:                 Console.WriteLine("Waiting for events ..."); 51: 52:                 while (!WMIHandler.IsArrived) 53:                       { 54:                       System.Threading.Thread.Sleep(1000); 55:                       } 56: 57:                 WMIEventWatcher.Stop(); 58: 59:                 return 0; 60:                 } 61:          } 62: 63:          public class WMIEventHandler 64:                 { 65:                 private bool isArrived = false; 66: 67:                 public void Arrived(object sender, EventArrivedEventArgs EventArgs) 68:                        { 69:                        ManagementBaseObject WMIInstance = 70:                                  (ManagementBaseObject)(EventArgs.NewEvent["TargetInstance"]); 71: 72:                        Console.WriteLine("Event triggered ...\n" ); 73:                        PropertyDataCollection.PropertyDataEnumerator propertyEnumerator = 74:                                               WMIInstance.Properties.GetEnumerator(); 75:                        while (propertyEnumerator.MoveNext()) 76:                              { 77:                        PropertyData property = (PropertyData)propertyEnumerator.Current; 78:                        Console.WriteLine(property.Name + " = " + property.Value); 79:                        } 80: 81:                        isArrived = true; 82:                  } 83: 84:           public bool IsArrived 85:                  { 86:                  get 87:                    { 88:                    return isArrived; 89:                    } 90:                   } 91:              } 

end example

This last .NET C# example is slightly more complex. As with previous examples, Sample 5.25 starts by creating the ConnectionOptions and ManagementScope objects (lines 19 through 28). Next, it creates the WQLEventQuery object holding the WQL event query (lines 30 and 31) defined as a constant in the Constants class (lines 10 through 12). To briefly demonstrate the versatility of the System.Management classes, the WQL query can be assigned to the WQLEventQuery object in many different ways. In Sample 5.24, we used one coding technique (line 38). In Sample 5.25, we use the following assignment, where we split the object creation from the WQL query assignment:

 WQLEventQuery WQLEventQuery = new WqlEventQuery(); WQLEventQuery.QueryString = Constants.WQLQuery; 

However, in the System.Management namespace object model, the WQL query can also be specified parameter by parameter:

 WQLEventQuery WQLEventQuery = new WqlEventQuery("__InstanceModificationEvent", new TimeSpan(0,0,5),                                                 "TargetInstance ISA 'Win32_Service'"); 

Or, eventually, the WQL query parameters can be assigned to their respective properties one by one:

 WQLEventQuery WQLEventQuery = new WqlEventQuery();WQLEventQuery.EventClassName = "__InstanceModificationEvent"; WQLEventQuery.Condition = "TargetInstance ISA 'Win32_Service'"; WQLEventQuery.WithinInterval = new TimeSpan(0,0,5); 

All these WQL query assignments produce the exact same result: They create a WQLEventQuery object made from the WQLEventQuery .NET class containing the WQL query defined in the Contants .NET class. This small example shows how versatile the object model can be and how it can be confusing for beginners as well!

Next, the portion of code preparing the ground for the asynchronous event monitoring starts with the creation of a ManagementEventWatcher object (lines 33 through 39). This object contains the connection settings (line 34) and the WQLEventQuery object (line 35). Since the monitoring is asynchronous, a WMIEventHandler object is created (line 37) from the WMIEventHandler.NET class defined further in the code (lines 63 through 91). This class implements the event sink routine handling WMI event notifications. We will come back to this class creation later in this section.

Once the ManagementEventWatcher object is created (line 33), in addition to the ManagementScope and WQLEventQuery objects information, the ManagementEventWatcher object needs to know which routine is used as the event handler. Therefore, based on the event type, the ManagementEventWatcher object is initialized with a pointer to the event routine. The event types can be:

  • EventArrived: Occurs when a new event arrives.

  • Stopped: Occurs when a subscription to an event is canceled.

 38:                  WMIEventWatcher.EventArrived += 39:                                 new EventArrivedEventHandler(WMIHandler.Arrived); 

For people not used to the C# notation, the VB.NET notation may make more sense:

 AddHandler WMIEventWatcher.EventArrived, AddressOf WMIHandler.Arrived 

Next, the event watching is started (line 42) by invoking the Start() method of the ManagementEventWatcher object. Since the WMI connection is performed during the event watching startup process, the statement is enclosed in a "Try ... catch" C# statement to manage exceptions (lines 41 through 48). Next, the C# code will wait until an event arrives (lines 52 through 55). The IsArrived property is exposed by the WMIEventHandler .NET class created within the C# code (lines 84 through 90).

The last piece of code to examine concerns the event handler itself (lines 63 through 91). The event handler is triggered for the EventArrived event. Therefore, a subfunction, which must be named Arrived(), is created inside the .NET class (lines 67 to 82). Two .NET objects are passed as parameters along the event notification:

  • Sender object: This object contains information about the event sender. For instance, with this object it is possible to retrieve information contained in the WMIEventWatcher object created in the first part of the code. This could be done as follows inside the event handler routine:

     ManagementEventWatcher WMIEventWatcher = (ManagementEventWatcher) sender; Console.WriteLine(WMIEventWatcher.Query.QueryString); 

  • EventArgs object: This object contains information about the event itself and is made from the EventArrivedEventArgs .NET class. This object exposes two properties: the Context property and the NewEvent property. The NewEvent property gets information about the WMI event notification. Therefore, to retrieve information about the WMI instance subject to the __InstanceModificationEvent, the C# code retrieves, via the WMI TargetInstance property exposed by the WMI instance event notification, the Win32_Service instance subject to the changes (line 69 and 70). This instance is stored in a ManagementBaseObject object made from the ManagementBaseObject .NET class.

 68:          ManagementBaseObject WMIInstance = 69:                  (ManagementBaseObject) (EventArgs.NewEvent["TargetInstance"] ); 

Once the ManagementBaseObject object is available, Sample 5.25 proceeds as previous samples: It enumerates all properties with their values of the WMI instance (lines 72 through 82). Once the enumeration is completed, the IsArrived property exposed by the WMIEventHandler .NET class is updated (line 81), which makes the main routine of Sample 5.25 exiting from its loop at lines 52 through 55. Then the event watcher is stopped (line 57) before the C# code terminates (line 59).




Leveraging WMI Scripting
Leveraging WMI Scripting: Using Windows Management Instrumentation to Solve Windows Management Problems (HP Technologies)
ISBN: 1555582990
EAN: 2147483647
Year: 2003
Pages: 82
Authors: Alain Lissoir

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net