WMI Programming with Visual Basic.NET: Breaking the Ice
The goal of this series is to introduce Microsoft Windows Management Instrumentation (WMI) and get you up and running as quickly as possible, using it to manage your system through the WMI programming using .NET. In this article, we will cover the basics of WMI and how to access WMI information from Visual Basic.NET (both locally and remotely). Before diving into further details, we first need a little background in what WMI is, how it came about, and why it is valuable.
You can download a zip file of the entire Visual Studio.NET solution (developed for this article) here.
Introduction to WMI
Windows Management Instrumentation (WMI) is the Microsoft implementation of Web-based Enterprise Management (WBEM), an industry initiative to establish standards for accessing and sharing management information over an enterprise network. WMI is WBEM-compliant and provides integrated support for the Common Information Model (CIM), the data model that describes the objects that exist in a management environment.
WMI includes a CIM-compliant object repository, which is the database of object definitions, and the CIM Object Manager, which handles the collection and manipulation of objects in the repository and gathers information from WMI providers. WMI providers act as intermediaries between WMI and components of the operating system, applications, and other systems. For example, the registry provider draws information from the registry, while the SNMP provider provides data and events from SNMP devices. Providers give information about their components, and might provide methods to manipulate the components, properties that can be set, or events that can alert you to changes in the components.
The two paragraphs above are the definitions of WMI from Microsoft WMI team. But to put it simply, you can almost manage (or administrate) a single computer, an entire network, or even several domains with just WMI. The information you get from WMI can be further integrated with databases or the Web in an enterprise to work very efficiently, or remotely. To get the information from WMI, we need to use scripting (such as VBScript or Jscript) together with a huge repository of CIM. CIM contains all classes, objects or namespaces to the related resources (such as network, printer, and so forth).
Experts even suggest diverting to WMI rather than working on core WIN32 API. It is more difficult for a beginner to understand and work with WIN32 API than WMI. You can use WMI with programming (including .NET) or scripting systems (such as Windows Script Host) to retrieve configuration details about most aspects of your computer systems, including server applications, to make changes to your systems or even to schedule the scripts. We can also remotely administer the systems available in the network using WMI. And another wonder is that several tools from Microsoft are developed using WMI as the main technology. These tools include Microsoft Systems Management Server, Microsoft Operations Manager, and others.
What all of this means is that WMI makes Windows 2000/2003 extremely manageable by using a single consistent, standards-based, extensible and object-oriented interface. Also, any application or script accessing WMI data can do so on the local machine or remotely in a seamless way. And, it's not only for Windows 2000/2003; WMI is available for Windows 95, Windows 98, and Windows NT 4.0 as well (with a separate free download).
The above paragraphs just gave only a very basic introduction to WMI. Since this article (and series) mainly focuses on accessing WMI by using .NET, I request the readers to go through MSDN and WMI SDK for a complete architecture and explanation of how to use WMI.
I just gave you a huge amount of theory without any practical examples. Now we shall create a simple windows application using Visual Basic.NET to get some information from WMI. Let's start by opening Visual Studio.NET 2003 Enterprise Architect.
After opening Visual Studio.NET 2003 IDE, go to File menu -> new -> Project.
Within the Visual Basic projects, select the "Windows Application" template (as shown in Fig. 1 below) and provide the name as "WMISample" at your own location and finally click OK.
Figure 1
Drag a button and a listbox from the toolbox onto the form.
Name them as "btnShowDrives" and "lstDrives" respectively. The form should look like the one in Fig. 2 below.
Figure 2
Go to Project menu -> Add Reference. Choose System.Management in the list of components (of .NET tab) and pressthe "select" button. Finally click OK. You should be able to see System.Management as part of "references" in the solution explorer.
Modify your code so that it looks like the following:
Imports System Imports System.Management
Public Class Form1 Inherits System.Windows.Forms.Form . . .
Private Sub btnShowDrives_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnShowDrives.Click 'connect to "Win32_LogicalDisk" class of WMI Dim diskClass As New ManagementClass("Win32_LogicalDisk") 'get the information Dim disks As ManagementObjectCollection = diskClass.GetInstances() 'loop the information to store in an array Dim disk As ManagementObject Dim arDisks As New ArrayList For Each disk In disks arDisks.Add(disk("deviceid").ToString()) Next disk 'populate the listbox Me.lstDrives.Items.AddRange(arDisks.ToArray) End Sub End Class
Now, press F5 to execute the program. Click on the "Show Drives" button. And you should be able to see the listbox filled with all the drives attached (logically) to your computer (as in Fig.3).
Figure 3
Don't ignore the next section. It not only explains the above program, but also gives you a few tips on some interesting aspects.
In order to get information (or data) from WMI, either on the local computer or from a remote computer, you must connect to the WMI service by connecting to a specific namespace. For example, the "root\cimv2" namespace (which is also the default) includes most of the classes that represent resources commonly associated with a computer and operating system. "root" is the main namespace to start with WMI. "cimv2" is a sub namespace available within the "root". There exist literally hundreds (or even thousands) of classes in the "root\cmv2" namespace. Similarly, there exist several other namespaces as well, logically grouped based on specific tasks.
The above paragraph provides you with just a procedure for working with WMI. But how do you implement it? The version of WMI that shipped with Windows 2000/2003 displayed classic COM interfaces to client applications. Therefore, you can access WMI-instrumented management data and events via the .NET COM Interop layer.
But there is a much better way to work with WMI from .NET. The System.Management namespace, defined as part of the .NET Framework, provides an easy-to-use, intuitive API for consuming WMI data and events. A lot of the complexities and idiosyncrasies of WMI APIs are taken care of by System.Management, and its class definitions follow the standard design paradigm of the .NET Framework.
Now, I would like to take a moment to explain the program in the previous section. Let us consider the following statement.
Imports System.Management
The above statement straight away imports all the necessary ingredients to work with WMI in our application. It must be added as a reference to our application before importing it.
Dim diskClass As New ManagementClass("Win32_LogicalDisk")
The above statement connects to a WMI class called "Win32_LogicalDisk". WMI creates a separate instance of this class for each and every logical drive present in the system. The class exists in "root\cimv2" namespace, which is the default namespace. You need not specify "root\cimv2" namespace, if you are working with any class within that namespace. If you really would like to do so, you need to change the above statement to something like the following:
Dim diskClass As New ManagementClass ("\\.\root\cimv2:Win32_LogicalDisk")
It is something like the UNC (Universal Naming Convention). The following gives you syntax for using the convention:
\\<ServerName>\root\<subnamespace>:<class>
Here is the next statement in the program:
Dim disks As ManagementObjectCollection = diskClass.GetInstances()
The above statement returns all the "instances" (or drives) of drives logically present in your system. Each drive returned (as part of the collection) is an "instance" of the class "Win32_LogicalDisk".
For Each disk In disks arDisks.Add(disk("deviceid").ToString()) Next disk
The above loop iterates through each instance of drives (present in the collection) and adds the "deviceid" to the arraylist. "deviceid" is a public member of the class "Win32_LogicalDisk". As we received the instances of the class "Win32_LogicalDisk" (from WMI), we are retrieving the value available in the "deviceid" member of each of those instances using a loop.
Me.lstDrives.Items.AddRange(arDisks.ToArray)
Finally, we add all the elements available in the arraylist to the listbox.
The previous sections introduced you to connecting only to a local computer, i.e. your own system. But what if I wanted to get the list of drives information for another computer (within the same network)? Is it as simple as changing the "ServerName" in the WMI path? Absolutely not.
When we try to get the information from another computer, the concept of security comes in. To connect to another computer within the network, we need to provide the authentication information along with the WMI path! The following shows you the necessary modifications for the above program to connect to another computer:
Dim options As New ConnectionOptions options.Username = "administrator" options.Password = "" Dim scope As New ManagementScope("\\hostname\root\cimv2", options) Dim diskClass As New ManagementClass(scope, New ManagementPath("Win32_LogicalDisk"), Nothing)
Make sure you replace the "hostname" of the scope to the host name of the other computer within your network. So, the scope is now being provided with the necessary path, along with some connection options for username and password.