| ne of the greatest challenges facing an enterprise developer is the management of computer networks as they become larger and more complex. WindowsÂ® Management Instrumentation (WMI) is a powerful technology that makes it possible to manage a single machine, or ten thousand all at once. Unfortunately, WMI is probably one of the best-kept secrets to come out of Microsoft in recent years. It hasnï¿½t received much coverage compared to technologies such as Active Directoryâ¢ or Windows DNA. This article will take you on a tour of WMI, so youï¿½ll have the background you need to write your own applications using WMI. Before long youï¿½ll be convinced that WMI should be as much a part of every developerï¿½s vocabulary as COM, Win32Â®, or Microsoft.
Overview WMI is one of several technologies introduced by Microsoft to support the management of systems in an enterprise. Active Directory provides location, policy, and organizational information on an enterprise-wide level. The MicrosoftÂ® Management Console (MMC) is the presentation framework for writing management apps. Windows Script Host (WSH) allows administrators to automate complex tasks. And finally, WMI is the technology that enables remote management of Windows-based systems and apps.
WMI allows developers to use a simple, consistent mechanism to query for information or configure settings on computers across an enterprise. The amount of information available through the WMI interface is staggeringâ"hardware settings, performance information, driver configuration, BIOS information, application settings, event log information, and much more. WMI gathers information from a diverse set of APIs, but it presents information following a simple, industry-standard management object model. This makes it unnecessary for application developers to learn the specifics of every API set provided by Windows.
To understand this power, consider a developer who wants to enumerate the descriptions for four different groups of objects on a machine: services, printers, processes, and CPUs. Without WMI, a developer has to find and learn different APIs to accomplish each type of enumeration. With WMI, itï¿½s remarkably simple because every object class is enumerated in the same way. Just take a look at how simple this task can be completed with the VBScript code for the WSH shown in Figure 1.
If you are running Windows 2000, you can copy this code to a file ENUM.VBS, and run the command cscript ENUM.VBS from a console window. If you encounter an error, itï¿½s most likely because Iï¿½ve omitted error checking for null to keep the script short. For example, if there is no comment listed for one of your installed printers, obj.Description will be returned as null.
If you are running Windows 9x or Windows NTÂ® 4.0, WMI is an optional component. The most recent version of WMI is available from the Microsoft Web site (http://msdn.microsoft.com/downloads/default.asp?URL=/code/topic.asp?URL=/MSDN-FILES/028/000/015/topic.xml).
Remarkably, the script in Figure 1 could be used to enumerate the same information for a remote machine. To show the information about MYSERVER, simply use WinMgmts://MYSERVER as the parameter to GetObject in the first line. In fact, any information that is exposed to WMI is automatically available using the same API from a local or remote machine. Although some Win32 APIs provide the ability to specify a remote machine, others only provide access to the local machine and will leave you stranded if you try to get the information about a remote system.
WMI also makes it simple to set information or invoke actions on managed objects. The same syntax can be used to stop a service, terminate a process, or log a user off a remote machine. The developer only needs to know the name of the object and the name of the action. Thereï¿½s no need to learn a new API.
To top everything off, an event infrastructure provides change notifications for all objects exposed through WMI. Later in the article Iï¿½ll show a sample that monitors processes being created or destroyed. Another sample generates a notification whenever a floppy disk is inserted or removed. The same techniques can be used on any type of object managed through WMI. Even when the underlying API doesnï¿½t provide this sort of notification for an object, the WMI architecture transparently simulates the behavior by monitoring the instances. Again, remote access to all this functionality is provided for free by the WMI service.
In addition to the information already exposed through WMI, applications can also expose their own custom objects and events. Once this is done, any WMI management application will be able to manage your application as well. This can include remote configuration, change notifications, or receiving of custom events.
The Genesis of WMI WMI is a Microsoft technology, but it is built on industry standards introduced in recent years. Understanding WMI requires understanding its evolution from an initiative called Web Based Enterprise Management (WBEM). Several years ago, a group of computer companies established the WBEM initiative to develop standards for managing enterprise systems and devices. The goal was to develop a single set of standards for managing any component of an enterprise network. This would ease the problems caused by the existence of many separate standards, such as SNMP for network devices and DMI for desktops. In the future, companies would develop WBEM-compliant hardware, software, and systems that could all be managed in the same way. Thus a single management application could easily manage all the diverse components in an enterprise environment. Eventually the responsibility for the WBEM initiative was assumed by the Desktop Management Task Force (DMTF) organization. The DMTF is responsible for maintaining the standards that will help meet the goals of the WBEM initiative.
CIM Classes The first standard from the WBEM initiative is a method for describing management information called the Common Information Model (CIM). CIM takes an object-oriented approach to modeling information. Management information is expressed using class definitions, inheritance, instances, properties, and methods. CIM classes are defined in text files using the Managed Object Format (MOF). A complete coverage of the MOF file format is well beyond the scope of this article, but its syntax is fairly intuitive if you are familiar with C++, the Java language, or Interface Definition Language (IDL). Hereï¿½s an example of how to define a class and two of its subclasses:
The previous MOF file defines an Automobile as a base class with two subclasses: Car and Truck. An Automobile has a Make property, a Model property, and a Recall method. A Car has an additional property, BlueBookValue. A Truck has an Axles property, with a default value of 2. (New instances of Truck will have a value of 2 for Axles unless otherwise specified.) The words in brackets (abstract and key) introduce the CIM concept of qualifiers. Qualifiers can be applied to the entire class, a property, a method, or even an individual method parameter. Qualifiers are similar to the concept of attributes in IDL files. They provide additional descriptive information about the use of a class, property, or method.
[abstract] class Automobile
[key] string Make;
[key] string Model;
class Car : Automobile
class Truck : Automobile
uint32 Axles = 2;
In this example, the abstract class qualifier tells you that you will never see instances of an Automobile. On the other hand, you can instantiate a Car or Truck since these are not abstract classes. The key property qualifier tells you that any Automobile (or any instance of a derived class) can be uniquely identified by its Make and Model. This is a concept borrowed from database technologies. All classes that support multiple instances must specify the key properties. Each instance must have a unique combination of values for its keys. In other words, the Automobile class definition allows for the existence of only one instance of a Ford Mustang, but you can create an instance of a Ford Taurus or even a GM Mustang. If a class has one and only one instance, it can be declared with the singleton class qualifier. In this case, the class does not have to specify any properties as keys.
The MOF file format is used to describe instances as well as class definitions. The following example describes an instance of a Truck:
The CIM standard alone is not enough to enable companies to create manageable objects and prevent chaos. For example, one company might call a router a NetworkRouter while another company calls the same thing a Router. Even worse, two companies could attempt to define a Router with the same class name, but different properties. To prevent this sort of confusion, the DMTF also defines the CIM schema. A schema is a well-defined collection of class definitions that all companies agree to follow. Classes that are a part of the CIM schema start with CIM_ by convention (such as CIM_Battery or CIM_Process). The CIM schema makes heavy use of inheritance to allow management applications to treat groups of similar objects in the same way. For example, CIM_Battery, CIM_Printer, and CIM_Processor are all derived from the base class CIM_LogicalDevice. This allows management applications that know how to work with a CIM_LogicalDevice to manage any type of device derived from that class. In addition, any derived class will support the properties and methods of the base class. The class CIM_LogicalDevice has a property that specifies if power management is supported (PowerManagementSupported), and a method that allows the power state to be set (SetPowerState). By exposing this functionality in the base class, applications can manage the power state for a printer, processor, or any device derived from CIM_LogicalDevice.
instance of Truck
Axles = 5;
Make = "Ford";
Model = "Big Rig";
Another benefit of inheritance is that vendors can extend the CIM schema if they are able to provide more information than the CIM classes define. For example, the Windows operating system may know the AveragePagesPerMinute of installed printers. The CIM_Printer class does not define a property for this information, so Microsoft defines a Win32_Printer, derived from CIM_Printer, that has the additional property AveragePagesPerMinute. A management application that knows about the Win32_Printer class can use this information, but an application that only knows how to manage a CIM_Printer will still function properly.
WBEM Support The CIM and MOF standards and the CIM schema are the core components of the WBEM initiative. These standards are platform- and implementation-neutral. To make universal management a reality, hardware and software vendors must create systems that support these standards (referred to as implementations of WBEM). This involves exposing management functionality through classes derived from the CIM schema. The Microsoft implementation of the WBEM initiative for the Windows operating systems is WMI.
Microsoft is not alone in supporting WBEM. Hardware companies have committed to providing network and storage devices with implementations of WBEM. Sun has promised a WBEM implementation for Solaris. Major players in management software, such as Tivoli Systems and Computer Associates, have committed to supporting WBEM in their applications. With all this support, enterprises that can be managed in a single consistent manner will certainly become a reality.
WMI Architecture To understand the components of the WMI architecture, shown in Figure 2, it helps to analyze the creation of an implementation of the WBEM standards. The first task is to decide which elements of the CIM schema apply to a computer running Windows. The next step is to extend the CIM schema by defining classes that expose any additional information available under Windows. The convention used by Microsoft is to derive from CIM classes and prefix the class name with Win32_ (for example, the Win32_NetworkAdapter is a class derived from CIM_NetworkAdapter). Eventually you need to write some software that actually provides the management capabilities. It will have to maintain the list of supported classes, and provide the actual data when instances of a class are requested. It will also have to respond to method calls on objects by implementing the requested actions. In generic terms, this is called a CIM Object Manager (CIMOM). On Windows NT, the CIMOM resides in a service called WinMgmt, and is implemented in WinMgmt.exe. Although Windows 9x does not support Windows NT services, the CIMOM is still implemented in an executable called WinMgmt.exe.
At some point, you need an API that allows you to talk to the CIMOM. You need to be able to query the schema, enumerate instances, and call methods. Although the CIM standard defines the content to be included in classes, there is no definition for its binary representation or the mechanism thatï¿½s used to access it. You are free to use DLLs, COM, sockets, named pipes, and so on. Microsoft chose to expose the CIMOM functionality through a set of DCOM interfaces. These interfaces all start with IWbem (such as IWbemServices or IWbemClassObject), and are documented in the Platform SDK. These interfaces make it possible to write management applications that work with classes and instances that are managed by WMI.
By choosing DCOM, Microsoft could essentially make any interface to the CIMOM accessible from remote machines without writing a single extra line of code. In reality, though, the overhead of sending every interface call to the remote machine would cripple the performance of clients. In addition, the information returned by the interfaces includes a large amount of redundant information and would waste bandwidth. For example, every WMI object includes a property that specifies the machine where the object exists. An interface method that returns an array of objects will include the same machine name for every element. Relying on DCOM to marshal the data would result in the machine name being sent over and over. To improve performance, Microsoft developed sophisticated custom proxies and stubs to compress information and minimize this overhead. Although developers wonï¿½t know that these proxies and stubs exist, they are responsible for the awesome performance of remote connections.
Once youï¿½ve defined the DCOM interfaces that allow access to the CIMOM, the next step is to create a database that holds the class definitions. In addition, you need to store a lot of static information such as the security settings for WMI itself. Static information is data that does not change except when manipulated by the CIMOM directly or indirectly through calls to the DCOM interfaces. This can include static instance data as well class definitions, allowing configuration information to be stored in much the same way that static information is stored in the registry today. The WinMgmt service maintains this static information in a database called the CIM Object Repository, which is a single file in the \winnt\system32\wbem\respository directory.
To provide information about dynamic objects, the WinMgmt service supports the registration of providers. WMI providers are actually COM servers that support a set of well-defined interfaces described in the Platform SDK. Providers can be queried on demand by the CIMOM for dynamic information, or they can explicitly call the CIMOM to inform it of changes to data. The most common use is to provide dynamic instance information, such as the list of active processes. It is also possible to write providers that dynamically describe classes in the schema, or providers that generate event information (the event architecture will be discussed later). WMI ships with several providers that supply information for an amazing number of classes in the schema (see Figure 3). Developers can extend the information provided through WMI by defining custom classes and writing providers to supply dynamic instance information.
The DCOM interfaces provided by the WinMgmt service are enough to start C++ developers writing management applications, but unfortunately not all development environments support access to arbitrary COM interfaces. To access WMI from environments that support Automation objects, Microsoft provides scripting objects that wrap the direct DCOM interfaces. There is a fairly straightforward mapping between the DCOM interfaces and the scriptable objects. For example, SWbemObject wraps IWbemClassObject, and SWbemServices wraps IWbemServices. The naming conventions are not identical, but anyone familiar with one interface can easily identify and understand the other. The scripting objects allow complete access to WMI through Visual BasicÂ®, Visual Basic for Applications (VBA), WSH, VBScript, JScriptÂ®, ASP, or any other environment that supports automation objects. Visual Basic or VBA projects can access these objects by adding Microsoft WMI Scripting V1.1 Library in the References dialog. Platforms that support ActiveXÂ® scripting can create these objects by their class ID or by their class name, which is prefixed with WbemScripting such as WbemScripting.SWbemLocator.
Another component provided by Microsoft wraps access to the DCOM interfaces. It is an ODBC adapter that makes classes, properties, and instances look like tables, columns, and rows for database applications. This is useful when incorporating WMI data into Microsoft Excel spreadsheets, ActiveX Data Objects (ADO) applications, or other database-driven apps.
The remainder of this article discusses the use of WMI through either the DCOM interfaces or the scripting objects. Both of these methods provide full access to the services of WMI, while the ODBC adapter currently only supports a subset of the WMI functionality. Although this article doesnï¿½t cover the use of XML, it is an extremely important feature to watch in the future. As mentioned earlier, the WBEM initiative does not specify the use of a particular technology to access CIM objects. Microsoft chose to use DCOM interfaces, but it is unlikely that other operating systems or hardware devices will do the same. Instead, XML provided by HTTP connections will most likely be adopted as a standard way of interfacing with WBEM implementations. In fact, the DMTF has taken on the task of creating an XML/HTTP standard for WBEM implementations. Microsoft is also expected to support an XML standard in the future. This will allow applications to query for WMI data using HTTP with the results returned in XML. Future hardware devices and OSs will also support management through XML and HTTP.
Namespaces WMI supports the grouping of a collection of classes into logical units called namespaces. Namespaces are arranged much like folders on a drive, and class definitions are like files in these folders. Figure 4 shows an example of how namespaces are presented with the WMI tools in the Platform SDK. Every machine has a well-defined namespace at the top of the hierarchy called the root namespace. The location of a namespace is described with a path, just like child directories (such as root\cimv2 or root\directory\LDAP). Each namespace contains its own collection of classes and instances. To uniquely specify a class, the namespace must also be specified (such as root\cimv2:Win32_Processor). Within a namespace, all the class names must be unique (just like files in a directory), but itï¿½s possible to have unrelated classes in different namespaces with the same name. Inheritance is a critical part of CIM, and one restriction is that all classes derived from a common base class must be defined in a single namespace. In other words, if you want to define a Car derived from an Automobile, the Automobile class must be defined in the same namespace.
|Figure 4 Exploring Namespaces |
As mentioned earlier, one of the core components to the WBEM initiative is to support the classes defined in the CIM schema. In WMI, these classes can be found in the root\cimv2 namespace. The CIM schema is continually updated, but each release is described with a version number. The v2 part of the root\cimv2 namespace means that the schema represented is the CIM Version 2 schema. Microsoft has also developed custom schemas for information not represented by the CIM V2 schema. These schemas are stored in other namespaces, such as root\WDM for information provided by WDM drivers, or root\directory\LDAP for information provided by the Active Directory provider. Developers who are providing their own schemas can create new namespaces. In fact, unless you are extending a schema by deriving from an existing class, it is best to create new classes in your own namespace.
System Classes and System Properties Every namespace contains a common set of classes called system classes. System classes all have names that start with a double underscore (such as __Provider). One interesting system class in each namespace is the __NAMESPACE class. This class has a single property, the Name property. Enumerating the instances of this class provides the mechanism for discovering child namespaces. For example, the root namespace will have a __NAMESPACE instance with the Name property set to cimv2. This means that a namespace called root\cimv2 exists. Other system classes are used for relatively sophisticated WMI programming that will not be covered in this article, such as custom provider registration.
In addition to system classes, thereï¿½s a standard set of system properties that can be found on every class. Again, they are easy to identify because they always start with a double underscore. The properties are read-only, and are generated automatically by WMI. They exist mostly to allow client applications to dynamically determine the identity of an object. When describing class or instance definitions in MOF files, these properties are omitted. Figure 5 shows a list of the system properties.
Object Paths The syntax shown in the previous section for describing namespaces and classes introduces the concept of object paths. An object path is a way of specifying a machine, namespace, class, or instance. The absolute form of an object path specifies a machine name as well as the namespace. For example, \\jeffc\root\cimv2: Win32_Process refers to the class definition of Win32_Process in the root\cimv2 namespace on the machine \\jeffc. Object paths are considered relative if they omit the machine name or namespace. The path root\cimv2:Win32_Process refers to the Win32_ Process in the root\cimv2 namespace on the current machine. The path Win32_Process refers to the class definition of Win32_Process in the current namespace on the current machine.
Object paths are used to specify instances as well as class definitions. To specify an instance, the key properties and their values are specified after the class name. The relative object path
refers to the instance of Win32_Environment where the Name property is TEMP and the UserName property is JEFFC\Administrator. Note that the \ was expressed as \\ when quoted in the object path (just like C++ or the Java language). In the case of singleton classes (which have no key properties, but one and only one instance), the object path consists of the class name appended with =@, such as \\jeffc\root\cimv2:SingletonTest=@.
Ideally, object paths are completely case-insensitive. This means that machine names, namespace names, class names, property names, and property values are all case-insensitive. In reality, some poorly written providers may accidentally require case sensitivity for property names or values. If this is ever encountered, it should be treated as a bug by the provider. In addition to being careful when writing providers, developers must be careful that any operations performed with object paths are done in a case-insensitive manner.
Programming WMI Itï¿½s now time to get down to the specifics of writing management applications with WMI. The first step for any application using WMI is to get an IWbemLocator pointer using CoCreateInstance with CLSID_WbemLocator. If you are using the scripting interfaces, you are creating a WbemScripting.SWbemLocator object. From this point on, Iï¿½ll present the samples using JScript and the scripting interface. This will make it easier to understand and read the samples. Iï¿½ll mention the equivalent DCOM interfaces where appropriate, so it should be easy to convert over to C++ at a later time.
The locator object allows you to request a connection to the WMI service through the ConnectServer method. ConnectServer lets you specify a machine name and namespace, or it will connect you to a default namespace on the local machine if left blank. ConnectServer returns an SWbemServices object (or IWbemServices interface). The SWbemServices object provides communication to the CIMOM, but it can only access objects in the namespace specified by ConnectServer. To get objects for a different namespace (even if it is on the same machine), youï¿½ll need to get a different SWbemServices object from another call to ConnectServer. SWbemServices provides many methods for getting WMI objects, but the simplest is Get (or IWbemServices:: GetObject). This allows you to use an object path to get a class definition or a specific instance. Figure 6 shows an example of what youï¿½ve seen so far.
In Figure 6, youï¿½ll notice that the Get method was used to retrieve the class definition of Win32_LogicalDisk, as well as an instance of Win32_LogicalDisk for the C: drive. In both cases, you are returned an SWbemObject (or IWbemClassObject interface when using IWbemServices::GetObject). The SWbemObject gives you access to the full definition of the object, including properties, values, methods, and qualifiers. The scripting object dynamically appends the CIM objectï¿½s properties to standard properties supported by SWbemObject. This makes it easy to access properties if you know what you are looking for in advance. The following shows how to access the FreeSpace property of the C: drive instance you obtained previously:
Property names and values can also be accessed through the Properties_ collection:
// Show free space for C: drive
Methods are accessed in a similar manner. Methods can be accessed through the Methods_ collection, and they are also appended directly onto the SWbemObject itself.
// Show all properties and values using an "Enumerator"
// In VBScript, this can be done with a "FOR EACH" statement
var f=new Enumerator(diskinstance.Properties_)
for (;!f.atEnd();f.moveNext ())
var prop = f.item();
WScript.Echo(prop.Name + " = " + prop.Value);
As mentioned earlier, every object has a set of system properties that allow the identity of an object to be determined dynamically. These system properties are accessed through the Path_ property of SWbemObject. The Path_ property is an SWbemObjectPath object that has properties corresponding to each system property. The naming convention is not identical to the names of the system properties, but the mapping is fairly obvious. The following function shows how to dynamically determine if an object is a class or an instance:
Until now, the examples have assumed that you know the object path of the class or instance you want to examine. The SWbemServices object also allows you to dynamically query for available classes or instances. The list of available classes can be determined through the SubclassesOf method. This method returns the subclasses of a given class or the list of base classes if no class is specified as an argument. To allow for instances to be retrieved dynamically, WMI again borrows a technique from the database world; it uses a variation of SQL called the WMI Query Language (WQL). Hereï¿½s an example of a simple WQL query that retrieves the list of all logical drives:
// Internally, IsClass uses the __Genus system property
// to determine if an object is a class or instance
Just as with SQL, WQL queries can be refined to return a specific set of properties, or a subset of instances that satisfy some criteria. The full syntax of WQL queries is documented in the Platform SDK, but some examples can help show the power of WQL. The following query returns the list of all services that are stopped:
SELECT * FROM Win32_LogicalDisk
This query returns only the Name and FreeSpace properties of all logical disks:
SELECT * from Win32_Service WHERE State="Stopped"
To execute a WQL query, you can use the ExecQuery method of SWbemServices. The ExecQuery method will return a collection of objects that meet the criteria specified in the query. The following sample will output the name, free space, and total size for every logical drive:
SELECT Name,FreeSpace FROM Win32_LogicalDisk
If you run the previous script from a console window (using the command cscript SAMPLE2.JS), the output will look something like this:
var locator = new ActiveXObject("WbemScripting.SWbemLocator");
var service = locator.ConnectServer();
var props = service.ExecQuery("SELECT Name,FreeSpace
var f = new Enumerator (props);
for (;!f.atEnd();f.moveNext ())
var p = f.item ();
WScript.Echo(p.Name + " - " + p.FreeSpace + " - " + p.Size);
Each line should show the drive name, free space, and drive size, but youï¿½ll notice that the drive size is missing. If you look carefully at the WQL query, youï¿½ll notice that I only asked for the Name and FreeSpace property. Since I did not request the Size property, Iï¿½m not guaranteed that it will be available in the returned objects. Limiting a WQL query to specific properties can provide a great performance boost since providers can skip properties that are costly to query, but it can leave you with SWbemObject objects that only have partial information. This can be an annoying source of bugs if you are not careful.
A: - 18432 - undefined
C: - 1079707648 - undefined
D: - 1006243840 - undefined
E: - 553623552 - undefined
Z: - 4573265920 - undefined
The SWbemServices object provides several other methods that return collections of objects (such as InstancesOf, ReferencesTo, and AssociatorsOf). If you are working with the IWbemServices interface, you may notice that these methods are missing. This is because the extra SWbemServices methods actually generate WQL queries behind the scenes. The extra methods exist on the scripting object as a convenience that can make scripts somewhat more legible. There is no performance difference between calling InstancesOf(SomeClass) or ExecQuery(SELECT * FROM SomeClass).
You may have noticed that the very first sample used GetObject to retrieve an SWbemServices object. Other samples have used a two-step process of creating an SWbemLocator followed by a call to ConnectServer. GetObject uses a concept familiar to COM developers called monikers. WMI supports a rich set of monikers to enable one-step access to SWbemServices and SWbemObject objects for classes or instances. The following JScript shows an example of using monikers to access the Alerter service:
The equivalent method without monikers requires three steps:
var alerter = GetObject("WinMgmts:Win32_Service=\"ALERTER\"");
The use of GetObject with monikers can be very convenient for many simple tasks. However, repeated use of monikers for objects from the same namespace may be less efficient than it would be to reuse a single SWbemServices object. A full list of all the options supported by WMI monikers is covered in the Platform SDK under the heading "Object Creation and Monikers."
var locator = new ActiveXObject("WbemScripting.SWbemLocator");
var service = locator.ConnectServer();
var alerter = service.Get("Win32_Service=\"ALERTER\"");
At this point, itï¿½s worth discussing some of the tools that are out there to help browse the information thatï¿½s available through WMI. WMI ships with a test application called WBEMTest.exe. This is a very powerful app, since it provides access to almost every DCOM interface exposed by WMI, but it is very difficult for a novice to understand. The WMI SDK ships with a set of tools that make exploring WMI easy. The WMI SDK is available from http://msdn.microsoft.com/downloads/default.asp?URL=/code/topic.asp?URL=/MSDN-FILES/028/000/015/topic.xml. The complete contents of the WMI SDK are also available through the Platform SDK, but the tools are not installed by default. Youï¿½ll have to look carefully through the setup options to enable installation of the WMI Tools.
|Figure 7 CIM Studio |
CIM Studio is one of the most useful tools for exploring schemas in the WMI SDK (see Figure 7). The left pane shows the inheritance tree for classes in a namespace. The right pane shows class or instance information. CIM Studio also allows you to enter WQL queries and examine the results.
Security Since WMI is a service, it runs as Local System with virtually unlimited control over the computer. When a client makes a request, it is this service that actually has to call Win32 APIs to get the information or perform the action. Does this mean that you can request information through WMI and essentially circumvent the usual Windows NT security that applies to your user account? Absolutely not.
Whenever a connection is established, your user credentials are passed to WMI by the underlying DCOM infrastructure. WMI will impersonate your credentials before attempting to fulfill any requests. This ensures that you cannot perform any action that you would not otherwise be allowed to do. Consider the case of calling the Win32 DeleteFile function, versus using the Win32_DataFileï¿½s DeleteFile method. If youï¿½re calling the Win32 DeleteFile function, Windows NT is responsible for making sure you have the proper permissions. If youï¿½re using the Win32_DataFileï¿½s DeleteFile, you are asking the WMI service to delete the file. Eventually, the WMI service will have to call the Win32 DeleteFile function. Instead of trying to figure out for itself if you should be allowed to do this, WMI just impersonates you and attempts the call. Windows NT makes proper security checks and returns an error to WMI if you donï¿½t have permission. WMI can then propagate the error back to the client application.
When connecting to remote machines, it is possible to specify the credentials of another user as parameters to ConnectServer. If these are left out, the credentials of the current user are used instead. When youï¿½re connecting to the local machine, only the credentials of the current user can be used.
The previous explanations are an oversimplification of what really goes on, but itï¿½s enough to get by on if you are writing client applications with the scripting objects. When using the DCOM interfaces, there are additional DCOM rules that apply. In particular, any interface that is remoted needs to be configured with the correct security blanket using IClientSecurity. For local connections, privileges for the client thread must be enabled to perform actions that normally require the privileges. The WMI SDK covers these issues in more detail, but it is worthwhile to invest in some good books on the finer points of DCOM. Fortunately, the scripting objects handle most of these complexities automatically, which makes them an excellent choice for DCOM novices.
|Figure 8 Securing WMI Access |
To provide additional protection, WMI supports traditional Windows NT security on each namespace. This is useful to specify restrictions such as who will be able to connect to WMI from remote machines. This can be configured through an extension to the Computer Management snap-in on Windows 2000 (see Figure 8). To access this, right-click the My Computer icon on the desktop, and then choose Manage. Under Services and Applications, choose WMI Control, then display its properties. On the Security tab, youï¿½ll be able to set the permissions for each namespace just as you set permissions for folders on your hard drive. If you are running Windows 9x or Windows NT 4.0, running WBEMCNTL.EXE will bring up the same utility. When connecting to a machine running Windows 9x, there is no support for Windows NT security on the objects managed through WMI. However, WMI allows namespaces to be configured with simulated Windows NT permissions. This lets an administrator control who connects to a namespace, but once connected, the client will essentially have full control over the objects exposed.
Events So far youï¿½ve seen how applications query WMI for information about classes and instances. WMI also allows applications to receive events from the system. Events are defined in the schema just as other classes. For example, a modem manufacturer could write a provider that exposed a MyModem_Ring event. The MyModem_Ring class could have properties, such as CallerIDName or CallerIDNumber. To register for an event, an application uses WQL with a syntax similar to data queries. The following event query specifies that an application is interested in incoming calls from Stephanie:
In addition to events defined by providers, applications can register for events that monitor changes in classes or instances. This is very powerful since it allows an application to respond to any change in state that is exposed through WMI. The code in Figure 9 shows how easy it is to monitor the creation of arbitrary objects. The HTML Application (HTA) watches for the creation of any new process.
SELECT * FROM MyModem_Ring WHERE CallerIDName="Stephanie"
Copy the code in Figure 9 into a file called NewProcs.HTA, and launch the file from Explorer. HTA files are very similar to HTM files, but run with lower security restrictions. Since WMI exposes low-level functionality of the operating system, the scripting objects are not marked as safe for scripting. This does not mean that you should not script these objects. But you should not allow untrusted HTM files to script WMI. If you use WMI scripting objects in HTM files, youï¿½ll receive security warnings about using objects not marked as safe for scripting. When you launch an HTA file, the system grants it the same access as if you launched an executable. This allows WMI scripting objects to be used without generating additional warnings.
As you launch new processes, you should see each process name show up in the sample application. This sample introduces two new concepts. First, you register for events using a WQL event query with the ExecNotificationQueryAsync method. The full syntax of WQL event queries is covered in the Platform SDK, but the code in Figure 9 can serve as a template for monitoring instance creation for any class.
WMI providers are responsible for notifying WMI when new instances are created, but this is only an optional requirement. If a provider does not provide notification for a particular class, applications can still register for instance creation notification, and WMI will simulate the event by polling the provider for the list of instances. The WITHIN 1 clause tells WMI that it should poll every second for new instances of the Win32_Process class. Unfortunately, the polling method is not foolproof. If a process is created and destroyed within the 1 second between the queries made by WMI, applications will not receive an event notification. Even with this limitation, though, events are remarkably powerful.
In addition to querying for instance creation, it is possible to watch for instance deletion or modification of specific properties of an instance. Figure 10 shows how an application can monitor for the insertion of a disk into a floppy drive. This is done by watching for changes to the A: logical disk. When the Size property changes, and the new Size is greater than zero, you can assume that a disk has been inserted. This is not a very graceful sample, but it shows how a creative WQL query can generate events to respond to almost any condition in the system.
The second new concept, introduced in Figure 10, is the use of SWbemSink. Sinks are used to perform asynchronous operations with the scripting interface. The variable mysink is created through the class ID for SWbemSink. The sink can then be used in any SWbemServices method that returns information asynchronously. When asynchronous data becomes available, the sink will fire events and pass the data to the application. In the previous examples, I used the ExecNotificationQueryAsync method to register the mysink object with the WQL event query. When the specified event occurs, the mysink object fires an OnObjectReady event. The handler for the OnObjectReady event adds text to the bottom of the HTML document.
There are three more samples Iï¿½ve included in the code download that demonstrate most of the concepts covered in this article. (The code is available from the link at the top of this article.) KillCalc.vbs is a VBScript file that terminates all running instances of CALC.EXE. ProcTree.HTA uses the ParentProcessId property of Win32_Process to generate a parent/child tree of all the running processes in the system. This sample is not optimized for performance, but it demonstrates the sort of useful information you can get from WMI that sometimes is not exposed through the Win32 API. Finally, PrcWatch.HTA demonstrates advance uses of sinks. This sample shows a list of all processes launched by EXPLORER.EXE. It presents them in a table with a Delete button that will kill each process (make sure you have x.gif in the same directory). As new processes are launched from Explorer, they are added to the table. As processes go away, they are removed from the table. This sample also shows how to use a single sink with more than one concurrent asynchronous request.
Conclusion As you can see, WMI is a complex, powerful service. This article has just scratched the surface of what can be done with WMI today. As other companies adopt the WBEM standards, the power of WMI will be expanded even more. I hope this has encouraged you to look at WMI for your own applications. I think youï¿½ll find that it will become an indispensable tool.