Main Contents »

Copyright © 2015 Ashok P. Nadkarni. All rights reserved.

Windows Management Instrumentation (WMI) is Microsoft’s implementation of an industry standard for managing distributed computer systems. This chapter describes the use of WMI from Tcl.

Note WMI is a vast topic. And that is understating it. However, the major concepts are straightforward and the most common tasks are not difficult. This chapter focuses on these but provides enough background that those who need to go deeper can do so by reading the references at the end of the chapter.

1. Introduction

Employers Computers are like horses, they require management.
— P. G. Wodehouse (adapted)

WMI is Microsoft’s implementation of the Web-Based Enterprise Management (WBEM) architecture. WBEM in turn is an industry-wide initiative by the Distributed Management Task Force (DMTF) to define a common architecture and framework for management of heterogeneous computers systems. Its primary goals are to define a common, extensible data model for describing and exchanging data across differing platforms in a distributed network. This data model is referred to as the Common Information Model (CIM).

In this chapter, we use the terms WBEM and WMI somewhat interchangeably.

The Windows WMI implementation includes an extensive set of WBEM providers which implement access through WMI to the various components of the system. This chapter describes how to access these WMI API’s from Tcl. It is focused on the practical aspects of working with WMI and does not go into the details of the WBEM/WMI architecture and model. Readers wishing to learn more can see the references listed at the end of this chapter.

It is not possible to implement a WMI provider directly in Tcl or any other scripting language. See Developing WMI Solutions for sample implementations in C.

Important To run the code in this chaper, the Tcl interpreter process must be running under an account with administrative privileges. If running on Vista or later, it must also be running in elevated mode.

The sample code in this chapter assumes the following commands have been executed to load the TWAPI twapi_wmi package for accessing WMI.

% package require twapi_wmi
→ 4.1.27
% package require twapi_process 1
→ 4.1.27
% namespace path twapi
1 Not needed for WMI, but used in some example code

A simple example of the use of WMI is this snippet to enumerate the OS updates installed on the system.

% set wmi [wmi_root]
→ ::oo::Obj315
% $wmi -with {
    {ExecQuery "select * from Win32_QuickFixEngineering"}
} -iterate app {
    puts "[$app HotFixID] [$app Description]"
    $app -destroy
}
→ KB2712101_Microsoft-Windows-CameraCodec-Package Update
  KB2727528 Security Update
  KB2736693 Security Update
  KB2737084 Security Update
  KB2739987 Update
...Additional lines omitted...
% $wmi -destroy

The neat thing about WMI is that with just a change to the first line, the same snippet will enumerate the updates on a remote system.

2. WMI Concepts

Working with WMI and WBEM requires an understanding of some common concepts and terms. This section is only a brief introduction to these. More detail and explanation is provided in subsequent sections as we work through examples.

2.1. Classes, Properties, Methods and Qualifiers

A WBEM class defines a particular type of managed resource or some aspect of the WMI environment itsef. A class definition includes includes

  • properties (data) associated with that resource type, for example the number of packets sent by a network adapter.

  • methods (operations) applicable to that resource type, for example starting a Windows service.

  • qualifiers which provide additional information about the class, for example, the privileges required for access.

  • metadata for the class itself

Note What is metadata? Simply put, metadata is data that describes data. For example, a class describing printers may define properties related to a printer, such as manufacturer, supported paper sizes and so on. How does one discover what properties a particular class has ? That is where metadata for the class comes in. Among other things, it stores information about what properties are defined by the class, the types, and whether they are modifiable. Metadata allows us to write programs in generic fashion, for example to write a script that prints all properties for any class by looking up the property names associated with each.

2.1.1. Class inheritance

The Common Information Model is based on object oriented concepts so WBEM classes follow an inheritance hierarchy. For example, the Win32_Process class is defined by Microsoft and describes instances of running programs. This class is derived from the CIM_Process class defined in the CIM and extends that class with properties and methods specific to Windows.

One of the goals, and consequently benefits, of WBEM was to provide a consistent means of access to all manageable components in the computing environment. Thus the same mechanisms can be used to access information pertaining to a printer as that for a network adapter. Most WMI classes are directly or indirectly derived from classes defined in the CIM, such as CIM_ManagedSystemElement. This allows them to share some generic properties and methods that applications can use without having to know class details.

2.1.2. Association classes

Most classes we will work with correspond to specific manageable resources such as devices or processes. There is another type of class called an association class which does not correspond to a resource. Instead its instances reflect a relationship between two or more classes. For example, an association class may reflect the fact that disk drives (class Win32_DiskDrive) are attached to a computer (class Win32_ComputerSystem).

2.2. Instances

While class defines the logical information associated with a managed resource type, an instance of a class contains the actual information for a specific resource of that type. For example, the Win32_Service class defines properties and methods for a Windows service. Each Windows service, like Rpcss, will have a Win32_Service instance that contains the values of the properties for that service, such as its display name, status and so forth.

2.3. The CIM Repository

The WMI providers installed on a system need to make their class definitions accessible to applications. This is done by registering their class definitions with a database that stores the metadata and class definitions for all WMI classes on the system. This database is called the CIM repository.

On Windows 8, the repository is stored as a set of files in the %SYSTEMROOT%\system32\wbem\repository directory.

2.4. Schemas and Namespaces

A WMI schema is a group of classes, generally defined by a single entity, that represent a certain type of managed environment. For example, the Win32 schema is directed towards Windows-based computers.

By convention, the class name prefix, for example Win32_, reflects the schema that defines it.

For the most part, in WMI we work with two schemas:

  • the CIM schema which is owned by the DMTF and defines the core classes in the CIM.

  • the Win32 schema which is owned by Microsoft and defines the extended classes specific to Windows. These are generally derived from the core classes in CIM.

On Windows, both the above schemas are loaded in the root\cimv2 namespace.

A WMI namespace contains a subset of classes and instances from one or more schemas that are relevant to a particular operating environment. Namespaces are hierarchical and may be nested. They are stored as a logical databases in the CIM repository. Applications need to specify the namespace they are interested in when accessing WMI services.

Namespaces and schemas should not be confused with each other. A network adapter vendor may define a class or an entire schema for its products. In a Windows network, the schema or a subset of it may be loaded into an existing namespace in the CIM repository or even a new namespace as appropriate.

2.5. WMI Query Language

The classes and properties in WMI can be viewed as a database and correspondingly, WMI provides a subset of SQL, the WMI Query Language (WQL) that allows for extracting data from this database. Using WQL instead of programmatically accessing data can often be more efficient, particularly when dealing with remote systems. Although we do not discuss WQL in complete detail the sections below provide examples of using WQL from Tcl. For a detailed description, see WQL Reference.

2.6. WMI names

Names are generally case-insensitive in WMI. This includes names for namespaces, classes, methods, properties and WQL keywords. Also, hierachical names, as are used for nested namespaces, may use either \ or / as a separator. Thus root/cimv2 and root\CIMV2 refer to the same namespace.

2.7. Object paths

A WMI object path is a string that describes the location of a WMI object. An `object' in this context can be a namespace, a class, or instances of a class. The path itself contains three components:

  • the name of the target system and the namespace of interest. This is of the form \\SYSTEM\NAMESPACE or //SYSTEM/NAMESPACE. If this is the only component specified, the resulting path refers to the entire namespace.

  • the name of the class. This is separated from the first component by a : so the entire path looks like \\SYSTEM\NAMESPACE:CLASS. If the first component is not specified, it defaults to the current namespace. If the third component is not specified, the resulting path refers to the class itself.

  • the instance identifier. This is separated from the class by a . character. The instance is identified by specifying a property value separated from the property name by a = character. The entire path then looks like \\SYSTEM\NAMESPACE:CLASS.PROPNAME="PROPVALUE". Again, the system and namespace may be defaulted but the class name must be specified.

Object paths are used in WMI monikers which can be used to connect to a WMI object.

2.8. WMI Events

WMI also provides a mechanism for applications to be asynchronously notified for events. Like the rest of WMI, this mechanism is both flexible and extensible. WMI events may correspond to events like services starting or to a change in state or values of WMI objects, like disk space, or even changes to the WMI schemas.

2.9. Security

Because it is used to manage important computing resources, access to WMI is controlled. This security mechanism is based on the standard Windows security model and uses Windows user and group accounts for authentication and authorization. This access control extends to specific namespaces within WMI and includes control of what specific actions a particular user is allowed to invoke. WMI also makes use of impersonation so that access to resources is attempted under the account requesting the access so the WMI service itself, which runs as a privileged process, cannot be used to subvert the protections.

2.10. WMI service

Access to WMI from applications, whether locally or remote, is channeled through the winmgmts service. All access to WMI on a system requires connecting either explicitly or implicitly to this service.

Now that we have the basic concepts out of the way, we can move on to the nitty gritty details and how to access WMI from Tcl.

  • We first cover the basics of the WMI scripting library since all WMI access makes used of it.

  • Any access to WMI requires connecting to the WMI service on the target computer so we cover that next.

  • We examine the security considerations for WMI objects.

  • We then interactively explore the structure of the CIM. This will flush out the concepts discussed above and lay the groundwork for working with WMI.

  • We next describe how to use WMI for its intended purpose - management and administration of distributed computer systems.

  • Finally, we describe the use of WMI for monitoring and event notification.

3. WMI scripting library

Scripting languages such as Tcl or VBScript access WMI through the WMI scripting library which provides a set of COM automation objects. These objects provide access to the WMI server, repository and managed resources. Since these form the basis for all WMI programming, we summarize their function and role here. Subsequent sections will demonstrate actual usage.

Note This section is only contains a partial list of commonly used types. The same is true of the methods and properties. Types related to WMI events are discussed in WMI Events. Other types are discussed as and when they are encountered in the rest of the chapter. As always, see WMI Reference for full details.

3.1. The SWbemLocator object

Generally the first WMI object used, it is used to establish an authenticated connection to a namespace in a WMI service. It has a single method.

Table 1. SWbemLocator methods

ConnectServer_

Connects to a namespace on a local or remote system.

3.2. The SWbemServices object

Represents an established, authenticated connection as created by an SWbemLocator object. Its primary use is to retrieve and manipulate SWbemObject objects within the connected namespace.

Table 2. SWbemServices methods

AssociatorsOf

Returns a SWbemObjectSet containing objects associated with a specified object, for example all disks attached to a system.

Delete

Deletes an object from the namespace. Note this does not have any effect on the corresponding physical resource, if any. Generally used for deleting metadata like class definitions.

ExecMethod

Invokes any specified method of an object identified by its object path.

ExecQuery

Returns a SWbemObjectSet containing objects matching a specified WQL query.

Get

Retrieves a single SWbemObject based on its object path

InstancesOf

Returns a SWbemObjectSet containing instances of any specified class, including system classes.

ReferencesTo

Returns a SWbemObjectSet containing classes and instances that refer to a specific class or instance.

SubclassesOf

Returns a SWbemObjectSet containing class definitions.

3.3. The SWbemObject object

An SWbemObject automation object can represent a managed WMI resource or a WMI class definition. It exposes generic methods can be used to access the underlying resource irrespective of the actual class representing the resource, for example, Win32_Process versus Win32_Service, to discover properties and methods defined for the resource etc. In addition, it also exposes the type-specific specialized properties and methods underlying resource.

Tip The methods and properties that are part of SWbemObject itself have a _ suffix. This distinguishes them from the specialized properties of the actual underlying type.
Table 3. SWbemObject methods

Associators_

Returns a SWbemObjectSet containing objects associated with the object.

Delete_

Deletes the object from the namespace. Note this does not have any effect on the corresponding physical resource, if any. Generally invoked on an SWbemObject that contains metadata like class definitions.

ExecMethod_

Invokes any specified method of the object. This is used to invoke type specific methods on the underlying object when the actual type is not known a priori.

ExecQuery

Returns a SWbemObjectSet containing objects matching a specified WQL query.

GetObjectText_

Retrieves a text description of the object in MOF syntax.

Instances_

Returns a SWbemObjectSet containing instances of the object. Only valid when the object represents a class.

Put_

Modifications to an object’s properties are only written back to WMI when this method is invoked.

References_

Returns a SWbemObjectSet containing classes and instances that refer to this object.

Subclasses_

Returns a SWbemObjectSet containing subclass definitions that inherit from this object which must be a class definition itself.

Table 4. SWbemObject properties

Derivation_

A list of class names comprising the inheritance chain of the obect which must correspond to a class definition.

Methods_

An SWbemMethodSet containing method definitions for the object.

Path_

An SWbemObjectPath object containing the object’s object path

Properties_

An SWbemPropertySet containing properties for the object.

Qualifiers_

An SWbemQualifierSet containing qualifiers for the object.

3.4. The SWbemObjectEx object

An SWbemObjectEx automation class extends SWbemObject with additional methods and properties.

Note In this book, we will use SWbemObject generically to reference either type even if the referenced property or method actually belongs to SWbemObjectEx.
Table 5. SWbemObjectEx methods

GetText_

Returns a text representation of the object in various XML formats.

Refresh_

Updates the property value cache of the object from the values in the underlying resource.

Table 6. SWbemObjectEx properties

SystemProperties_

An SWbemPropertySet containing system properties for the object.

3.5. The SWbemObjectPath object

A SWbemObjectPath contains a WMI object path and provides properties and methods to manipulate it. It can be used to parse the path for an object or to construct one.

Table 7. SWbemObjectPath properties

Authority

Authority component of path

Class

Name of the class

DisplayName

Full moniker for the object

IsClass

Boolean that indicates whether the object is a class definition or an instance

Namespace

Namespace component of path

Path

Absolute path to the object

Server

Name of the WMI server

3.6. The SWbemObjectSet object

Contains a collection of SWBemObject objects. You can loop over the objects in the collection with the comobj wrapper’s -iterate method.

Table 8. SWbemObjectSet methods

Item

Returns an item by indexed by its relative object path

Table 9. SWbemObjectSet properties

Count

Number of items in the collection.

Example

The following short code snippet demonstrates use of the above automation objects.

% set wbem_locator [comobj WbemScripting.SWbemLocator] 1
→ ::oo::Obj1067
% set wbem_services [$wbem_locator ConnectServer] 2
→ ::oo::Obj1072
% $wbem_locator -destroy 3
% set wbem_object_set [$wbem_services InstancesOf Win32_Process] 4
→ ::oo::Obj1077
% $wbem_object_set Count 5
→ 118
% $wbem_object_set -iterate wbem_object {
    puts [$wbem_object Name]
    $wbem_object -destroy
} 6
→ System Idle Process
  System
  smss.exe
  csrss.exe
  wininit.exe
...Additional lines omitted...
% comobj_destroy $wbem_object_set $wbem_services
1 Get hold of a SWbemLocator to establish a connection to a WMI service
2 Returns a SWbemServices representing the local WMI service
3 The locator is no longer needed
4 Returns a SWbemObject set collection containing SWbemObjects corresponding to processes
5 Count of items in the collection (number of processes for this example)
6 Iterates over each SWbemObject, retrieving its name

The rest of the chapter illustrates use of these objects in detail.

4. Connecting to WMI with SWbemLocator

Warning Connecting to a remote system requires that various system components on the local and remote system are correctly configured. These include the Windows Firewall, DCOM and UAC. The application must also be running with appropriate privileges. See the WMI Reference for details.

The first step in accessing WMI on a system is establishing an authenticated connection to the WMI service on the target system. There are two ways in which this can be done. The first method, which is more general, is described here. The second method, using monikers, is described in WMI monikers.

The SWBemLocator COM automation object, implemented in the WMI scripting library, can be used to establish a connection to a namespace within the local or remote WMI service.

Note Because WMI security can be on a per-namespace basis, a WMI connection is always to a specific namespace on the target system. Accessing a different namespace will require a separate connection, potentially with different credentials, even if the target system is the same.

We create the object passing its ProgID WbemScripting.SWbemLocator to the TWAPI comobj call used for creating any COM object.

% set wbem_locator [comobj WbemScripting.SWbemLocator]
→ ::oo::Obj1556

We then call its ConnectServer method to establish an authenticated connection to the desired namespace on the target server. In the samples below, we demonstrate various calls to ConnectServer with different combination of parameters. After each call, we destroy the created object as we do not plan to use it just yet.

Without any parameters, ConnectServer will connect to the default namespace on the local system. We will have more to say on default namespaces below.

% set wbem_services [$wbem_locator ConnectServer]
→ ::oo::Obj1560
% $wbem_services -destroy

To connect to a remote system, we specify its name or IP address. Note that specifying . as the remote computer name indicates a connection to the local system.

% set wbem_services [$wbem_locator ConnectServer $::env(COMPUTERNAME)] 1
→ ::oo::Obj1562
% $wbem_services -destroy
% set wbem_services [$wbem_locator ConnectServer 127.0.0.1]
→ ::oo::Obj1564
% $wbem_services -destroy
1 For illustrative purposes the remote computer name we specify is the local system itself

If a connection to a namespace that is not the default namespace is desired, we can specify it as the second parameter.

% set wbem_services [$wbem_locator ConnectServer . root/cimv2]
→ ::oo::Obj1566
% $wbem_services -destroy

If the connection to the WMI server cannot be made for whatever reason, the above calls will hang. To prevent this, we can make use of the (inappropriately named) iSecurityFlags parameter. Setting this to a value of 0x80 will cause the call to return with an error code instead of hanging if the connection cannot be made.

% set wbem_services [$wbem_locator -callnamedargs ConnectServer iSecurityFlags \
    0x80] 1
→ ::oo::Obj1568
% $wbem_services -destroy
1 Note the -callnamedargs modifier which allows parameters to be passed by name without having to pass intermediate parameters.

Note that like all COM objects, the SWbemLocator objects also have to be destroyed when no longer needed. This might be as soon as right after the ConnectServer call if no more connections are to be created.

% $wbem_locator -destroy

4.1. Authenticating connections

Connections to a WMI service are always authenticated connections. The above examples use the user credentials of the account the process is running under to authenticate to the WMI server. If we want connect using different credentials, we can specify the account name and password as two additional parameters.

set wbem_services [$wbem_locator ConnectServer REMOTESYSTEM root/cimv2 ACCOUNTNAME PASSWORD]

The ACCOUNTNAME may be a simple name such as Administrator or in domain\name format such as Mydomain\Administrator.

Note You cannot specify a user name and password when connecting to the local system. Local connections are always made using the credentials of the calling thread.

The ConnectServer call has other optional parameters as well, for example to control the security authority, NTLM or Kerberos, to be used for authentication. See WMI Reference for details on these.

5. WMI Security Settings

An important consideration when working with WMI is security. We saw one aspect of this, connection authentication, previously. In addition, there are security settings associated with the connection and each object (class, instance, system metadata etc.) in the CIM. These settings control how objects are accessed, what privileges are required and so on. This settings are summarized in Summary of WMI security settings.

Table 10. Summary of WMI security settings
Setting Description

Impersonation level

Determines the COM impersonation level which controls the manner in which the remote WMI service can use the client’s credentials. The level can be one of Anonymous, Identify, Impersonate or Delegate. The first two of these cannot be used with WMI for any practical purpose. The last, Delegate, is rarely needed and must be used with extreme care as it can otherwise pose a security risk. Because Impersonate is the default and sufficient for almost all scenarios, we do not discuss this setting in any further detail.

Authentication level

Controls the packet-level authentication and encryption to be used on a connection.

Privileges

Certain WMI access may require special privileges to be owned by the calling client. These have to be set on the connection before the target object is accessed. Note that the calling process has to actually already have the privilege before it can be set on the connection. Obviously, it cannot be allowed to grant privileges to itself!

5.1. Modifying security settings with SWbemSecurity

Security settings in WMI are manipulated through objects of type SWbemSecurity. These settings may be associated with several different object types including the SWbemLocator object used for connecting, the SWbemServices object representing a connection and SWbemObject objects. All these objects have the Security_ property which can be used to retrieve the associated SWbemSecurity object.

Security settings are passed on through objects so for example setting the privileges associated with a SWbemLocator object will affect not only any SWbemServices objects retrieved with ConnectServer but also all SWbemObject objects retrieved through them.

5.1.1. Setting privileges

Some objects and operations may require the invoker to have passed certain operating system privileges in the connection. For example, querying the Windows Security event log requires the SeSecurityPrivilege privilege to be held. This section provides a sample of how the SWbemSecurity object may be used to add privileges.

Note Setting of privileges only require for local connections. When running against a remote WMI service, the RPC service takes care of enabling all permitted privileges.

We start off with the usual boilerplate assuming we do not already have an established connection.

% set wbem_locator [comobj WbemScripting.SWbemLocator]
→ ::oo::Obj1570
% set wbem_services [$wbem_locator ConnectServer]
→ ::oo::Obj1574
% $wbem_locator -destroy

We then retrieve the SWbemSecurity object for the connection. Its Privileges method will return a SWbemPrivilegeSet object containing the currently enabled privileges.

% set privileges [$wbem_services -with Security_ Privileges] 1
→ ::oo::Obj1584
% $privileges Count
→ 0
1 Note use of -with so we do not have explicitly deal with the SWbemSecurity object just for retrieving the privileges.

We now add the desired SeSecurityPrivilege using the AddAsString method. Once we have made the changes, we are free to destroy the SWbemPrivilegeSet object.

% set privilege [$privileges AddAsString SeSecurityPrivilege]
→ ::oo::Obj1590
% puts "Name: [$privilege Name], Value: [$privilege Identifier], Display: \
    [$privilege Displayname]"
→ Name: SeSecurityPrivilege, Value: 7, Display: Manage auditing and security log
% $privilege -destroy
% $privileges -destroy
Tip In the above code, we used the AddAsString method which accepts the defined names assigned to privileges by Microsoft. We could have also used the Add method. However, that method and its converse Remove, expect to be passed the corresponding integer value of the privilege as shown in the puts statement output above. You would need to look that up manually or use one of the techniques described in the COM chapter to map mnemonics to integer values.

Finally, we print the event count and clean up.

% set logs [$wbem_services ExecQuery "Select * from Win32_NTEventLogFile where \
    LogFileName='Security'"]
→ ::oo::Obj1597
% $logs -iterate log {puts [$log NumberOfRecords]; $log -destroy}
→ 32404
% $logs -destroy
% $wbem_services -destroy

5.1.2. Setting authentication levels

Authentication levels in this context does not mean authentication of user accounts. Rather it refers to DCOM authentication and encryption settings which control whether packets sent between systems are protected against tampering and whether data is kept private.

The different authentication levels are shown in the table below.

Table 11. Summary of WMI authentication settings
Level Description

0

This is not a level per se. Rather it specifies that the system default Windows Authentication setting should be used.

1

No authentication is done.

2

The client is authenticated when it establishes a relationship with the server.

3

The client is authenticated at the beginning of every call.

4

Authenticates every packet. Packet headers are signed but not the data.

5

Headers and data of every packet are signed and authenticated.

6

Headers and data of every packet are encrypted in addition to being signed and authenticated.

Setting the authentication level is illustrated in the sample below where we get the current level for a connection and then change it to require only authentication.

% set wbem_locator [comobj WbemScripting.SWbemLocator]
→ ::oo::Obj1607
% set wbem_services [$wbem_locator ConnectServer]
→ ::oo::Obj1611
% $wbem_locator -destroy
% set wbem_security [$wbem_services Security_]
→ ::oo::Obj1615
% $wbem_security AuthenticationLevel
→ 6
% $wbem_security AuthenticationLevel 5
% $wbem_security -destroy
% $wbem_services -destroy

6. Retrieving WMI objects

To retrieve data or invoke operations on any WMI object, we need to retrieve as a COM automation object.

Tip One of the nice, but also confusing, features of WMI is that it presents the same uniform access not just to different types of managed resources but also to the WMI infrastructure and metadata itself. Thus a WMI object may contain the properties of a specific network adapter. At the same time, its class definition, Win32_NetworkAdapter, is also itself an object. Even the WMI infrastructure can be accessed as objects; for example the __NAMESPACE class whose instances correspond to WMI namespaces. All these are WMI objects and can be treated in a uniform manner from a programming perspective.

There are two general mechanisms for attaching to a WMI object.

  • We can explicitly connect to the SWbemServices object for a WMI service using the SWbemLocator object as in the previous section and then invoke methods to retrieve the desired object.

  • We can directly retrieve to the object of interest by specifying its WMI moniker, a string that uniquely identifies the object to WMI.

The following short examples contrast the two methods by using them to find state of the Rpcss Windows service, first using SWbemServices.

% set wbem_locator [comobj WbemScripting.SWbemLocator]
→ ::oo::Obj1622
% set wbem_services [$wbem_locator ConnectServer]
→ ::oo::Obj1626
% $wbem_locator -destroy
% set rpcss [$wbem_services Get "Win32_Service.Name='RPCSS'"]
→ ::oo::Obj1631
% $wbem_services -destroy
% puts "[$rpcss Displayname]: [$rpcss Status]"
→ Remote Procedure Call (RPC): OK
% $rpcss -destroy

The same may written using monikers as

% set rpcss [comobj_object \
    {winmgmts:\\VULCAN\root\cimv2:Win32_Service.Name="Rpcss"}] 1
→ ::oo::Obj1635
% puts "[$rpcss Displayname]: [$rpcss Status]"
→ Remote Procedure Call (RPC): OK
% $rpcss -destroy
1 Note comobj_object, not comobj, has to be used with monikers

which is considerably less code.

How do you choose between these methods ?

  • If you need to specify credentials (password and/or the authority) you have to use SWbemLocator. There is no choice because these cannot be specified in monikers.

  • For interactive use or short programs, monikers are convenient as the code is more succinct.

  • On the other hand, monikers can be less efficient when accessing multiple objects in the target namespace.

  • In case of errors connecting to an object, use of SWbemLocator often provides more detailed errors that help in diagnosis.

We will illustrate both of these methods throughout this chapter but we first need to describe monikers in more detail.

6.1. WMI monikers

Microsoft COM uses monikers as specially formatted names that identify COM objects. A WMI moniker is just a special case of this where the moniker uniquely identifies objects in the WMI world, whether they be namespaces, classes or class instances.

A WMI moniker takes the form of a string with a specific format:

  • The moniker always starts with the literal winmgmts: prefix.

  • This is followed by a optional string which specifies the security settings, such as impersonation level and privileges and locale. If not present, the default settings are used.

  • The last component is also optional and specifies the WMI object path. If not present, the default namespace on the local system is assumed.

For full syntax details for WMI monikers, see WMI Reference. Some examples of monikers are shown in WMI moniker examples.

Table 12. WMI moniker examples
Moniker Target

winmgmts:

The SWBemServices object for the default namespace on the local system. using default security settings.

winmgmts://webserver

The SWBemServices object for the default namespace on the remote webserver system.

winmgmts://webserver:Win32_Service

The WMI class Win32_Service in the default namespace on the remote webserver system. Note the colon separating the class from server and namespace component.

winmgmts:Win32_Service

The WMI class Win32_Service in the local system’s default namespace.

winmgmts:Win32_Service='Rpcss'

The WMI class instance for the Windows Rpcss service in the local system’s default namespace.

winmgmts:root/cimv2

The SWBemServices object for the root/cimv2 namespace on the local system.

winmgmts: {impersonationLevel=impersonate, `(security,!debug)}! //./root/cimv2:Win32_Service='Rpcss'

The WMI class instance for the Windows Rpcss service in the default namespace on the local system. The connection sets impersonation level to impersonate, enables the SeSecurityPrivilege privilege and disables the SeDebugPrivilege privilege if it was set.

Caution As seen in the last example above, the names used to specify privileges in monikers are a shortened form of the Win32 privilege names. Thus the moniker uses security, not SeSecurityPrivilege. This differs from what we saw earlier with regards to use of the Security_ object.

7. Managing resources

We are now well armed to tackle the task of actually using WMI to manage computer systems. This involves one or more of the following:

  • retrieving the object or objects that correspond to the managed resources of interest

  • reading properties from an object

  • modifying properties of an object

  • invoking actions on an object

7.1. Retrieving multiple objects

There are several mechanisms by which objects for managed resources might be obtained.

  • Using the InstancesOf method of SWbemServices

  • Using the Instances_ method of the class object

  • Using a WMI Query Language (WQL) query

7.1.1. Retrieving objects using InstancesOf

Once a SWbemServices object for a namespace is obtained as previously described, its InstancesOf method can be used to retrieve the instances for any class.

We start off by connecting to the root/cimv2 namespace.

% set wbem_services [comobj_object winmgmts:root/cimv2]
→ ::oo::Obj1639

This gives us a SWbemServices object just like before. One of the most useful methods of this object is InstancesOf which can be used to return all instances of any class in that namespace. In our example, we specify the Win32_LogicalDisk class corresponding to the drives in the system.

Tip The InstancesOf method can take additional optional parameters which impact memory and performance and control what information is returned. See WMI Reference for details.
% set disks [$wbem_services InstancesOf Win32_LogicalDisk]
→ ::oo::Obj1643

This gives us a SWbemObjectSet, each element of which is an SWbemObject representing the logical drives in the system. We know from reading the documentation for Win32_LogicalDisk that two of its properties are Name and Description.

We can use the TWAPI COM -iterate method to loop over the SWbemObjectSet.

% $disks -iterate disk {
    puts "[$disk Name]: [$disk Description]"
    $disk -destroy
}
→ C:: Local Fixed Disk
  D:: Local Fixed Disk
  E:: CD-ROM Disc
  F:: Removable Disk
% $disks -destroy
% $wbem_services -destroy

Or in a slightly shorter, but more obscure form, which negates the need for explicitly managing the disks object.

% set wbem_services [comobj_object winmgmts:root/cimv2]
→ ::oo::Obj1665
% $wbem_services -with { {InstancesOf Win32_LogicalDisk} } -iterate disk {  1
    puts "[$disk Name] [$disk Description]"
    $disk -destroy
}
→ C: Local Fixed Disk
  D: Local Fixed Disk
  E: CD-ROM Disc
  F: Removable Disk
% $wbem_services -destroy
1 See the TWAPI COM module documentation for how -with and -iterate work.

7.1.2. Retrieving objects using Instances_

In WMI, as we shall see in detail later, classes themselves are represented by an object. As an alternative to the above method, we can retrieve the object for the class Win32_LogicalDisk itself. This object is also a SWbemObject and has the Instances_ method which can be invoked to retrieve Win32_LogicalDisk instances in much the same manner as the first case.

% set disk_class [comobj_object winmgmts:root/cimv2:Win32_LogicalDisk]
→ ::oo::Obj1691
% $disk_class -with Instances_ -iterate disk {
    puts "[$disk Name] [$disk Description]"
    $disk -destroy
}
→ C: Local Fixed Disk
  D: Local Fixed Disk
  E: CD-ROM Disc
  F: Removable Disk
% $disk_class -destroy

7.1.3. Retrieving objects using WQL

The WMI Query Language (WQL) is a subset of the Structured Query Language (SQL) for databases. It can be used to selectively retrieve a set of WMI objects through a query specification that has the form

SELECT properties FROM classname WHERE conditions

where

  • properties is a comma-separated list of names of the properties that are to be retrieved with * denoting all properties.

  • classname indicates the class whose instances are to be retrieved

  • conditions is an optional clause specifying the criteria that an instance should meet in order to be included in the returned collection.

The query can be passed to the ExecQuery method of an SWbemServices object to retrieve matching objects.

% set wbem_services [comobj_object winmgmts:root/cimv2]
→ ::oo::Obj1714
% set disks [$wbem_services ExecQuery "SELECT * FROM Win32_LogicalDisk"]
→ ::oo::Obj1718
% $disks -iterate disk {
    puts "[$disk Name] [$disk Description]"
    $disk -destroy
}
→ C: Local Fixed Disk
  D: Local Fixed Disk
  E: CD-ROM Disc
  F: Removable Disk
% $disks -destroy
% $wbem_services -destroy

We will not go into WQL syntax and operators beyond what is used in the examples. It is for the most part easy to decipher from the examples. For detailed information see WQL Reference.

Contrasting WQL with other methods

The above sample using WQL may not look significantly different from the preceding ones. But WQL offers significant benefits in performance and ease when only a subset of the data is of interest.

  • WQL allows us to request only the properties of interest.

  • Similarly, WQL lets us restrict the returned collection to only those objects that match specified criteria by specifying the WHERE clause. This is usually a lot simple than iterating and and checking condition programmatically.

With large data sets like event logs, both features greatly improve memory usage and response time. When the WMI target is a remote system, network traffic is also greatly reduced.

The code prints the service name and process id only for services that are in state Running.

% set wbem_services [comobj_object winmgmts:root/cimv2]
→ ::oo::Obj1740
% set services [$wbem_services ExecQuery "SELECT Name,ProcessId FROM Win32_Service \
    WHERE State='Running'"]
→ ::oo::Obj1744
% $services -iterate service {
    puts "[$service Name]: [$service ProcessId]"
    $service -destroy
}
→ AdobeARMservice: 1708
  AeLookupSvc: 640
  AMPPALR3: 2012
  AppHostSvc: 1736
  Appinfo: 640
...Additional lines omitted...
% $services -destroy
% $wbem_services -destroy
Tip WQL allows strings to be delimited by either single quotes or double quotes. We prefer single quotes (as above) as using double quotes often entails extra escaping due to the fact that Tcl also uses double quotes as string delimiters.

7.2. Retrieving a single object

In situations where only a specific resource is of interest, there is no need to retrieve all instances and iterate looking for a match. Each instance has one or more key properties which can be used to directly access the object. For example, the Name field of a Win32_LogicalDrive object is a key.

Again, there are a couple of ways to retrieve a specific object by its key property:

  • Using its moniker

  • Using the Get method of SWbemServices

Note WQL is not listed here because it always returns a collection, not a single object, even if the collection contains a single instance.

7.2.1. Retrieving an object using its moniker

A WMI moniker allows specification of a key property and the value to be matched. The key property and value are separated from the class name component by a ..

Thus we can find the free space on a disk with the following script.

% set disk [comobj_object winmgmts:root/cimv2:Win32_LogicalDisk.DeviceId='C:']
→ ::oo::Obj2146
% puts "[$disk FreeSpace] bytes"
→ 561493991424 bytes
% $disk -destroy

Note that the DeviceId property is a key for the class. Using Name instead would have caused the command to fail as that property is not a key.

Tip If you are not sure what the name of the key property is for a class you can leave it out (assuming you what the key value is, as in this case C:). So the above could also have been written as
set disk [comobj_object winmgmts:root/cimv2:Win32_LogicalDisk='C:']

7.2.2. Retrieving an object using Get

Using monikers is convenient but if several instances needs to be processed, it is more efficient to use the Get method of the SWbemServices object. This command takes the relative object path of the instance as an argument as illustrated in the next code fragment and returns the corresponding instance object.

% set wbem_services [comobj_object winmgmts:root/cimv2]
→ ::oo::Obj2150
% foreach service_name {rpcss eventlog} {
    set service [$wbem_services Get "Win32_Service.Name='$service_name'"]
    puts "$service_name: [$service State]"
    $service -destroy
}
→ rpcss: Running
  eventlog: Running
% $wbem_services -destroy

7.2.3. Retrieving a singleton class object

A singleton class is a class for which there is exactly one instance. Examples includes Win32_WMISetting, which contains the operational settings for the WMI installation, and StdRegProv which represents the entire contents of the Windows registry as a single instance.

The command

set wmi_settings [comobj_object winmgmts:root/cimv2:Win32_WMISetting]

would retrieve the definition of the Win32_WMISetting class, and not the instance. To retrieve an instance of a singleton class, the key value is specified as @. For example,

set wmi_settings [comobj_object winmgmts:root/cimv2:Win32_WMISetting=@]

Note that the @ is not enclosed in quotes.

7.2.4. Getting a moniker from an object

Given an object, we can extract the moniker from it through its Path_ property which returns a SWbemObjectPath whose DisplayName property contains the moniker for the object.

% set service [comobj_object winmgmts:Win32_Service='rpcss']
→ ::oo::Obj2162
% $service -with Path_ DisplayName
→ WINMGMTS:{authenticationLevel=pktPrivacy,impersonationLevel=impersonate}!\\VU...
% $service -destroy

7.3. Working with properties

Properties contain the data associated with the managed resource represented by a class instance; for example, the state of a Windows service is contained in the State property of the Win32_Service instance for that service. Most of the code examples we have seen so far have dealt with reading properties.

WMI also defines a set of system properties for every class and instances. These are not related to the underlying resource but rather reflect metadata; for example the CLASS property contains the class to which an object belongs. System property names, like system class names, always begin with .

We now discuss additional operations involving properties including

  • Enumeration of properties for an object

  • Using generic methods to read properties

  • Modifying property values

To start off, let us first retrieve an object that we will use for illustration purposes through the discussion.

% set disk [comobj_object winmgmts:root/cimv2:Win32_LogicalDisk='C:']
→ ::oo::Obj2172

7.3.1. Enumerating properties

WMI provides for fairly comprehensive introspection capabilities for classes and objects. This includes the ability to the operate on properties and their values without knowing the actual type of the object. This is important in several contexts. Properties defined for a particular class can change between WMI versions or may differ between systems. Hence it is useful to be able to determine whether a particular property is defined for a class. Introspection is also useful when writing general purpose WMI utilities such as WMI browsers.

Property introspection is facilitated by through the Properties_ property that is defined for every SWbemObject. This contains a SWbemPropertySet object which is a collection of SWbemProperty objects each of which completely describes one property.

We can enumerate the names and values of all the contained properties by iterating over this collection. The SWbemProperty object has properties called Name and Value which contain the name and value of the property it represents.

% set propset [$disk Properties_]
→ ::oo::Obj2177
% $propset -iterate prop {puts "[$prop Name] = [$prop Value]"; $prop -destroy}
→ Access = 0
  Availability =
  BlockSize =
  Caption = C:
  Compressed = 0
...Additional lines omitted...
% $propset -destroy

Similarly, the SystemProperties_ property contains the system properties for the object. Paraphrasing the above a shorter form,

% $disk -with SystemProperties_ -iterate -cleanup prop {puts "[$prop Name] = \
    [$prop Value]"}
→ __PATH = \\VULCAN\root\cimv2:Win32_LogicalDisk.DeviceID="C:"
  __NAMESPACE = root\cimv2
  __SERVER = VULCAN
  __DERIVATION = CIM_LogicalDisk CIM_StorageExtent CIM_LogicalDevice CIM_Logica...
  __PROPERTY_COUNT = 40
  __RELPATH = Win32_LogicalDisk.DeviceID="C:"
  __DYNASTY = CIM_ManagedSystemElement
  __SUPERCLASS = CIM_LogicalDisk
  __CLASS = Win32_LogicalDisk
  __GENUS = 2

When you try the above command, you might notice that we retrieved the property Properties_ from the $disk object, it was not actually listed anywhere! The reason is that Properties_ only includes the properties defined by the underlying managed resource object and system properties, not the generic ones defined by SWbemObject itself.

7.3.2. Reading properties

We have already seen many examples of reading properties. For example,

% $disk FreeSpace
→ 561493991424

SWbemPropertySet provides the method Item to retrieve the SWbemProperty object for a property based on its name. So you could also write the above as

% $disk -with {
    Properties_
    {Item FreeSpace}
} Value
→ 561493991424

which is useful if you get paid per line of code.

Note that system properties cannot be read with the first direct method. They have to be read as in the second example.

% $disk -with {
    SystemProperties_
    {Item __CLASS}
} Value
→ Win32_LogicalDisk

7.3.3. Refreshing property values

Property values reflect the underlying managed resource at the time the object was created and will diverge as the state of resource changes. The values can be refreshed by calling the object’s Refresh_ method. This is a generic method defined by SWbemObjectEx and can be invoked for any object.

% $disk Refresh_
% $disk FreeSpace
→ 561493991424

7.3.4. Writing properties

The hardest part of writing properties is finding a property that is not read-only! Most WMI providers implement methods to modify their data as opposed to using writable properties.

Writing a property value simply involves supplying a value after the property name. For example, to change the disk label,

% $disk VolumeName NEWVOLUME

However, you will find that Explorer does not show the new volume label. This is because setting a property value only changes the value in the SWbemObject representing the resource. To actually have the new value written back into the WMI provider resource (the logical drive in this case), the Put_ method of the SWbemObject must be called.

% [$disk Put_] -destroy

You should now see the value written back to disk. The Put_ command returns a SWbemObjectPath containing the object path of the written object. Since we have no use for it, we destroy it right away.

Just like for reads, we can also write to properties through the Properties_ property for the object.

% $disk -with {
    Properties_
    {Item VolumeName}
} Value NEWVOLUME
% [$disk Put_] -destroy

7.4. Working with methods

If properties are data associated with objects, methods are operations that can be invoked to take action on the underlying managed resource. For example, invoking the Shutdown method on an instance of class Win32_OperatingSystem will result in the system being shut down.

7.4.1. The Methods_ property

Similar to its Properties_ property which lists all the WMI provider properties, a SWbemObject also has the Methods_ property which contains the definitions for all methods implemented for that object by the WMI provider. In analogous fashion, this contains a SWbemMethodSet object which is a collection of SWbemMethod objects describing the methods defined by the WMI provider for that class.

7.4.2. Enumerating methods

Once we have the SWbemMethodSet, we can enumerate the methods for an object. For example,

% set regprov [twapi::comobj_object winmgmts://./root/default:StdRegProv]
→ ::oo::Obj2411
% $regprov -with Methods_ -iterate -cleanup method_obj {puts [$method_obj Name]}
→ CreateKey
  DeleteKey
  EnumKey
  EnumValues
  DeleteValue
...Additional lines omitted...
% $regprov -destroy

A short explanation of this one liner might be in order if you have not read the chapter on TWAPI's COM client interface. The -with Methods_ invokes Methods_ on the regprov object. The rest of the command, starting with -iterate, is passed to the result of this invocation, a SWbemMethodSet object, which iterates through the collection printing the Name property. The -cleanup option is simply a convenience so that the -destroy method does not need to be called on every method_obj.

Note The StdRegProv class is an example of a singleton class - a class for which there a single instance in the CIM. In this case the single instance represents the entire Windows registry on system. There are no classes or instances representing individual registry keys and values. Contrast this with the CIM_DataFile where each instance represents an individual file.

7.4.3. Invoking methods

Invoking methods is generally as straightforward as accessing properties. We have already used methods through this chapter, for example ConnectServer and Put_, without explicitly calling them out. Here we illustrate a slightly more complex method - one that takes multiple parameters one of which is an output parameter.

Direct invocation of methods

The example reads a value from the registry. The return value from the method call is a status indicator and the actual registry value is stored in variable regvalue.

Note The example also illustrates one aspect of using COM from Tcl. Tcl (actually TWAPI) and most scripting languages use COM type libraries to figure out method signatures including parameter types. One of the things to be aware of when calling WMI provider methods is that there is no type information associated with this. Consequently, the parameter types have to be determined heuristically. On occasion, it is necessary to explicitly indicate that a particular parameter is an output parameter. This is the case in the example below where the outvar command tells the TWAPI COM connector that the parameter is an output parameter whose result value is to be stored in regvalue.
% set regprov [twapi::comobj_object winmgmts://./root/default:StdRegProv]
→ ::oo::Obj2503
% $regprov GetStringValue 0x80000002 "SOFTWARE\\Microsoft\\Wbem\\Scripting" \
    "Default Namespace" [twapi::outvar regvalue] 1
→ 0
% puts [twapi::variant_value $regvalue 0 0 0] 2
→ root\cimv2
% $regprov -destroy
1 0x80000002 selects the HKEY_LOCAL_MACHINE registry hive
2 variant_value extracts the value from a COM output parameter.
Method invocation using ExecMethod_

Just like properties, methods can also be invoked indirectly through SWbemObject’s `ExecMethod_ method. Although there is rarely a need to do this, we illustrate it here because the recipe is a little more involved and can be confusing. It also further shows how deep WMI’s introspection capabilities go. We will call the same method as above as a point of comparison.

Tip You will very rarely have to use ExecMethod_ from Tcl so you can safely skip this section without impacting your understanding the rest of the chapter.

We get the StdRegProv instance in the same fashion. To avoid having to deal with intermediate objects, we use the -with option to chain methods. In essence,

  • We retrieve via Methods_ a SWbemMethodSet from which…​

  • …​we use the Item indexing command to extract a SWbemMethod containing the method definition for GetStringValue…​

  • …​from where we invoke InParameters to get its input parameter definition…​

  • …​whose SpawnInstance_ method will return an input parameter container…​

  • …​whose Properties_ property gives us properties correspond to named parameter values.

  • Phew

% set regprov [twapi::comobj_object winmgmts://./root/default:StdRegProv]
→ ::oo::Obj2506
% set inparams [$regprov -with {
    Methods_
    {Item GetStringValue}
    InParameters
} SpawnInstance_]
→ ::oo::Obj2523
% set inprops [$inparams Properties_]
→ ::oo::Obj2526

Next, we set the values of the input parameters, again using Item, this time to index the parameter collection based on parameter name.

% $inprops -with {{Item hDefKey}} Value 0x80000002
% $inprops -with {{Item sSubKeyName}} Value "SOFTWARE\\Microsoft\\Wbem\\Scripting"
% $inprops -with {{Item sValueName}} Value "Default Namespace"

We now do the actual method invocation.

% set outparams [$regprov ExecMethod_ GetStringValue $inparams]
→ ::oo::Obj2544

The result is a OutParameters object whose properties contain the return value and output parameters from the method call. We extract them using similar our standard loop for properties.

% $outparams -with Properties_ -iterate -cleanup param {puts "[$param Name] = \
    [$param Value]"}
→ ReturnValue = 0
  sValue = root\cimv2

Happily, this matches the result from the direct call earlier.

As always, we need to clean up.

% comobj_destroy $regprov $inparams $inprops $outparams

You might wonder about the rationale behind ExecMethod_ and why its needed. The truth of the matter is that in Tcl and other dynamic languages, where you can construct a call “on the fly”, it is easy enough to generate a direct call to a method with any signature and ExecMethod_ is of limited, if any, use. However, in a language like C/C++ where this is not possible, ExecMethod_ provides a means for applications like WMI browsers and management tools to call any method without a hardcoded the call for a specific purpose.

7.5. Working with associations

One of important features of the CIM is that it provides a means to record and manage relationships between managed resources. An example of a relation is the association between a logical drive C:, represented by an instance of the class Win32_LogicalDisk, and the corresponding physical disk partition to which it is assigned, represented by an instance of Win32_DiskPartition.

These relationships are represented in the CIM by a special type of class, called an association class. An instance of an association class holds references to instances of the two classes representing the relation and any additional data pertaining to that relation.

Note As is common in WMI, there are multiple ways to deal with associations. Here we describe use of the the References_ and Associators_ method of SWbemObject. The same functionality is also avaible through the ReferencesOf and AssociatorsOf methods of SWbemServices and the REFERENCES OF and ASSOCIATORS OF statements of WQL. Working with these is not described below, but is very similar.

To illustrate the use of associations, we will start off with a Win32_LogicalDisk object to use for illustrative purposes.

% set disk [comobj_object winmgmts:root/cimv2:Win32_LogicalDisk='C:']
→ ::oo::Obj2560

7.5.1. Retrieving associations using References_

An object (or the underlying class) may have multiple types of associations. For example, the Win32_LogicalDisk is associated with a physical disk partition, the computer system it is attached to and so on.

We can use the References_ system method of a SWbemObject to enumerate these associations.

% set refs [$disk References_]
→ ::oo::Obj2564
% $refs -iterate -cleanup ref {puts [$ref -with Path_ Class]}
→ Win32_LogicalDiskToPartition
  Win32_LogicalDiskRootDirectory
  Win32_VolumeQuotaSetting
  Win32_SystemDevices
  Win32_DiskQuota

We see that a logical disk is associated with three different types of relations, each represented by a different WMI class. Win32_LogicalDiskToPartition for example ties the logical disk with a physical disk partition.

We can print a little more detail to examine each association.

% $refs -iterate -cleanup ref {
    puts [$ref -with Path_ Class]
    $ref -with Properties_ -iterate -cleanup prop {
        puts "  [$prop Name] = [$prop Value]"
    }
}
→ Win32_LogicalDiskToPartition
    Antecedent = \\VULCAN\root\cimv2:Win32_DiskPartition.DeviceID="Disk #1, Par...
    Dependent = \\VULCAN\root\cimv2:Win32_LogicalDisk.DeviceID="C:"
    EndingAddress = 701837082623
    StartingAddress = 2505048064
...Additional lines omitted...
% $refs -destroy

Each association class contains properties that refer to the associated instances through their object paths, for example GroupComponent/PartComponent. In addition, the Win32_LogicalDiskToPartition association contains additional information about disk extents in the form of properties StartingAddress/EndingAddress.

7.5.2. Retrieving instances using Associators_

Suppose you needed to retrieve detailed information about the partition on which a logical drive was located. You could use the sequence in the previous section to the association classes and use the object path in the returned properties for the Win32_LogicalDiskToPartition instance to retrieve the appropriate Win32_DiskPartition instance of interest.

A more direct way is to use the Associators_ method of SWbemObject which returns a SWbemObjectSet containing the associated instances themself rather than the association instances.

% set partitions [$disk -callnamedargs Associators_ strResultClass \
    Win32_DiskPartition]
→ ::oo::Obj2738
% $partitions -iterate -cleanup partition {
    puts "[$partition Name]: [$partition Type]"
}
→ Disk #1, Partition #3: GPT: Basic Data
% $partitions -destroy

A couple of points to be noted regarding the above code:

  • Without any additional arguments, Associators_ would have returned all types of association. Because we are only interested in the partition, we restricted the returned collection to belong to class Win32_DiskPartition.

  • Associators_ takes many parameters. To avoid having to specify all of them (and take the default values), we use -callnamedargs which allows passing of arguments by name.

Note References_ and Associators_ both have several options that are useful for selecting the classes or instances returned in the collection. These are not described here.

7.6. Commonly used classes

There are hundreds of classes defined in WMI that represent all kinds of resources. The full list with details is available in WMI Reference. We only list a few commonly used ones by category in Common WMI resource classes.

Note Unless stated otherwise, all classes lie in the root/cimv2 namespace.
Table 13. Common WMI resource classes
Category Classes

Files and disks

Win32_LogicalDisk, Win32_Volume, Win32_Directory,

Processes and threads

Win32_Process, Win32_Thread

Registry

Win32_Registry, StdRegProv. Note StdRegProv lies in the root/default domain.

System information

Win32_OperatingSystem, Win32_NTDomain, Win32_ComputerSystem, Win32_BootConfiguration,

Services

Win32_Service

Network shares

Win32_Share, Win32_ServerConnection, Win32_ServerSession

Eventlog

Win32_NTLogEvent, Win32_NTEventlogFile

User accounts

Win32_Account, Win32_Group, Win32_UserAccount

Networking

Win32_NetworkConnection, Win32_NetworkProtocol

Installer

Win32_Product, Win32_Patch

Hardware and Devices

Win32_Printer, Win32_Keyboard, Win32_PointingDevice, Win32_DiskDrive, Win32_BaseBoard, Win32_Bus, Win32_USBHub, Win32_DeviceSettings, Win32_MemoryDevice, Win32_PhysicalMemory, Win32_Processor, Win32_BIOS, Win32_SystemBIOS, Win32_Battery, Win32_DesktopMonitor, Win32_DisplayConfiguration, Win32_VideoController

8. Exploring the CIM

Having looked at using WMI to access managed resources, let us now look at the CIM itself in more detail by interactively exploring its structure. What allows us to do this is the fact that the CIM, along with stored metadata, can be queried in exactly the same fashion as any managed resource in it. We have already seen this when we retrieved class property and method definitions using Properties- and Methods_.

The introspection capabilities that WMI provides are useful in many ways:

  • Not all systems have all WMI classes installed, and the same WMI class may have different properties depending on version. Introspection allows writing of robust applications that work across diverse systems.

  • Management applications have the ability to support new resources by discovering their properties at runtime without needing to be updated.

  • Additional metadata about properties, such as localization information, allows applications to better format data for display.

  • Being able to interactively introspect the CIM greatly helps in understanding its structure leading to more concise and efficient programs.

8.1. Introspection of namespaces

Namespaces partition the CIM repository into nested, logical structure. When an application initiates access to WMI, it always starts out connecting, implicitly or explicitly, to a specific namespace in the repository.

The root of the nested namespace structure is, thankfully, named root. (We say thankfully' because as we shall see, the namespace `default is not the default namespace.)

8.1.1. Enumerating namespaces

A CIM repository may have any number of namespaces. How does one go about listing what namespaces are present on a system ?

We start off by connecting to the root namespace.

% set root_ns [comobj_object winmgmts://./root]
→ ::oo::Obj2747

This gives us a SWbemServices object as always but note that this time we have connected to the toplevel root namespace. Namespaces are themselves represented as instances of the class __NAMESPACE so we can use the InstancesOf method to enumerate them.

% set namespaces [$root_ns InstancesOf __NAMESPACE]
→ ::oo::Obj2751

This gives us a SWbemObjectSet each element of which is a SWbemObject corresponding to each child namespace.

We print out the namespace names contained in the Name property.

% $namespaces -iterate ns {puts [$ns Name] ; $ns -destroy}
→ subscription
  DEFAULT
  CIMV2
  msdtc
  Cli
...Additional lines omitted...
% $namespaces -destroy
% $root_ns -destroy

You may notice that the above commands do not list any nested namespaces. This is because the InstancesOf method only returns instances of the __NAMESPACE class in the namespace root corresponding to the SWbemServices object we connected to initially. To retrieve nested namespaces, we need to connect to each child namespace recursively and repeat.

The following proc does exactly that.

proc list_namespaces {ns_path} {
    set ns_obj [comobj_object winmgmts://./$ns_path]
    try {                   1
        set children [$ns_obj InstancesOf __NAMESPACE]
        $children -iterate child {
            set child_path "$ns_path/[$child Name]"
            puts $child_path
            list_namespaces $child_path
        }
    } finally {
        if {[info exists children]} {
            $children -destroy
        }
        $ns_obj -destroy
    }
}
1 We wrap the code in a try so we can release the ns_obj if an exception is raised

And when we run it,

% list_namespaces root
→ root/subscription
  root/subscription/ms_409
  root/DEFAULT
  root/DEFAULT/ms_409
  root/CIMV2
...Additional lines omitted...

we see the nested namespaces.

Most classes used in Windows administration reside in the root/cimv2 namespace. A notable exception is that the classes pertaining to the Windows registry are present in the root/default namespace.

8.1.2. The default namespace

When the namespace component is left out in a moniker, or unspecified in a ConnectServer call, it defaults to the namespace stored in the registry key as shown below.

% registry get {HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Wbem\Scripting} {Default \
    Namespace}
→ root\cimv2
Caution The default namespace used when no namespace is specified is not the namespace named root/DEFAULT! This is a source of confusion. (Hu’s on first?)

8.1.3. Namespace security

Access to namespaces is controlled with the standard Windows security mechanisms. Security descriptors can be attached to any namespace. Generally, each top level namespace, for example, root/cimv2 has a security descriptor attached which is then inherited by nested namespaces.

If you get access denied errors when trying the code in this chapter, verify that the account in which you are running Tcl has appropriate permissions taking UAC into consideration. You may also need to enable appropriate privileges as described in Setting privileges.

WMI security descriptors are normally set via the WMI Control plug-in for the Microsoft Management Console. You can also programmatically access them through the __SystemSecurity class which has a single instance in each namespace and contains the security descriptor settings for that namespace. We do not discuss this further here.

8.2. Introspection of classes

We now look at the structure of WMI classes in more detail. WMI classes fall into three categories:

  • System classes. These classes represent information about the WMI configuration and the CIM repository. We have already seen one of these - __NAMESPACE - earlier. System class names begin with two underscore characters.

  • Core and common classes. These classes are abstract classes defined by the DMTF for common types of resources but independent of the actual implementation technology. For example, the CIM_Process class represents the abstract notion of a process. These classes have no instances as they are only used to derive extension classes. System classes generally begin with the CIM_ prefix.

  • Extension classes. These are defined by vendors to represent concrete managed resources for a particular implementation technology. They are often derived from the core and common classes. For example, the Win32_Process class is derived from CIM_Process and its instances represent running processes on Windows systems. Most, but not all, extension classes defined by Microsoft have the Win32_ prefix.

8.2.1. Enumerating classes

Enumerating classes follows very much the same sequence that we saw earlier for namespaces except that we have to use the SubclassesOf method instead of InstancesOf. This returns a SWbemObjectSet that we can iterate over.

% set wbem_services [comobj_object winmgmts://./root/cimv2]
→ ::oo::Obj3569
% $wbem_services -with SubclassesOf -iterate -cleanup cls {
    puts [$cls -with Path_ Class]
}
→ CIM_Indication
  CIM_ClassIndication
  CIM_ClassDeletion
  CIM_ClassCreation
  CIM_ClassModification
...Additional lines omitted...

This will enumerate classes within the namespace. If you only need subclasses of a specific class, you can specify that to SubclassesOf to only include those. The following will only list the WMI event classes (which all derive from __Event).

% $wbem_services -with {{SubclassesOf __Event}} -iterate -cleanup cls {
    puts [$cls -with Path_ Class]
}
→ __ExtrinsicEvent
  Win32_DeviceChangeEvent
  Win32_SystemConfigurationChangeEvent
  Win32_VolumeChangeEvent
  MSFT_WMI_GenericNonCOMEvent
...Additional lines omitted...
% $wbem_services -destroy

Alternatively, if you already have a class in hand, you can use the Subclasses_ method.

% set event_class [comobj_object winmgmts://./root/cimv2:__Event]
→ ::oo::Obj12294
% $event_class -with Subclasses_ -iterate -cleanup cls {
    puts [$cls -with Path_ Class]
}
→ __ExtrinsicEvent
  Win32_DeviceChangeEvent
  Win32_SystemConfigurationChangeEvent
  Win32_VolumeChangeEvent
  MSFT_WMI_GenericNonCOMEvent
...Additional lines omitted...
% $event_class -destroy

You can also enumerate class definitions with WQL using a SELECT query. An optional WHERE clause can be specified so that we only retrieve subclasses of Event in this example as we did in the previous. Note the use of the special class meta_class and property this.

% set wbem_services [comobj_object winmgmts://./root/cimv2]
→ ::oo::Obj13270
% set classes [$wbem_services ExecQuery "SELECT * FROM meta_class WHERE __this ISA \
    '__Event'"]
→ ::oo::Obj13274
% $classes -iterate -cleanup cls {puts [$cls -with Path_ Class]}
→ __Event
  __ExtrinsicEvent
  Win32_DeviceChangeEvent
  Win32_SystemConfigurationChangeEvent
  Win32_VolumeChangeEvent
...Additional lines omitted...
% comobj_destroy $classes $wbem_services

8.2.2. Class qualifiers

Class qualifiers provide additional information about a class. For example, the Privileges qualifier contains a list of the privileges required to access the class. Extracting qualifiers is very similar to extracting properties except that we use the Qualifiers_ method to retrieve the collection.

Some commonly used class qualifiers are shown in Common class qualifiers.

Caution All qualifiers are optional and need not be present in a class definition.
Table 14. Common class qualifiers
Qualifier Description

Abstract

If present, is always set to 1 and indicates that the class is an abstract class which cannot have instances and is used only as a base class.

Association

If present, is always set to 1 and indicates that the class is an association class.

Description

Contains a human readable description of the class.

Dynamic

If present, is always set to 1 and indicates that instances of this class contain data whose source is a WMI provider. This qualifier is mutually exclusive with the Abstract qualifier. If neither is present, the class is a static class whose instance data is statically stored in the CIM database.

EnumPrivileges

Specifiies the privileges required to access the class. In the above example SeSecurityPrivilege is listed as a required privilege. The information provided by the qualifier can be of use when writing code. Alternatively, when writing generic code to access classes of different types, a runtime check can be made to examine the privileges required. Those can then be enabled before making the call.

Provider

Name of the WMI provider implementing the class.

Singleton

If present, is always set to 1 and indicates that the class is a singleton class, like the StdRegProv class we saw earlier, which can have only one instance.

Let us look at some examples of class qualifiers.

Because we will often print qualifiers in our examples in this and later sections, we will define a couple of procedures for the purpose. .

proc print_qualifiers {obj} {
    $obj -with Qualifiers_ -iterate -cleanup qual {
        puts "[$qual Name] = [$qual Value]"
    }
}
proc print_qualifier_value {obj name} {
    puts [$obj -with {Qualifiers_ {Item $name}} Value]
}
% set eventlog_class [comobj_object winmgmts:Win32_NtLogEvent]
→ ::oo::Obj14253
% print_qualifiers $eventlog_class
→ dynamic = 1
  EnumPrivileges = SeSecurityPrivilege
  Locale = 1033
  Privileges = SeSecurityPrivilege
  provider = MS_NT_EVENTLOG_PROVIDER
...Additional lines omitted...
% print_qualifier_value $eventlog_class Privileges
→ SeSecurityPrivilege
% $eventlog_class -destroy

We see that the SeSecurityPrivilege is required for access to the class.

8.2.3. Getting the textual definition of a class

It is often useful to get the textual definition of a class for pedagogical as well as practical purposes. For example, one can export the definition of a class for importing into the CIM repository on another system.

This is done via the GetObjectText_ method which returns the class definition in MOF format suitable for import into a repository.

% set process_class [comobj_object winmgmts:Win32_Process]
→ ::oo::Obj14299
% $process_class GetObjectText_
→ [dynamic: ToInstance, provider("CIMWin32"): ToInstance, SupportsCreate, Creat...
  class Win32_Process : CIM_Process
  {
          [Override("KernelModeTime")] uint64 KernelModeTime = NULL;
          [read: ToSubClass, Override("Priority"): ToSubClass, MappingStrings{"...
...Additional lines omitted...
% $process_class -destroy

An alternative is the GetText_ method which returns the definition in several different XML formats.

Tip GetObjectText_ and GetObject_ can be used not only with the class definitions but with class instances as well. In that case the returned text includes values for the instance properties in the appropriate format.

8.3. Introspection of properties

Delving deeper into the CIM structure, let us now look at property definitions for a class.

We have already seen introspection with properties of instances where we described enumeration of properties. Exactly the same method can be used with class definitions. The only difference is that in the case of properties the values will be empty since it is a class definition and not an instance.

% set bios [comobj_object winmgmts:Win32_BIOS]
→ ::oo::Obj14303
% $bios -with Properties_ -iterate -cleanup prop {puts [$prop Name]}
→ BiosCharacteristics
  BIOSVersion
  BuildNumber
  Caption
  CodeSet
...Additional lines omitted...
% $bios -destroy

8.3.1. The SWbemProperty object

The collection returned by Properties_ is a collection of SWPropertyObject objects each describing a property of the class. Let us print some information associated with a property.

% set event_class [comobj_object winmgmts://./root/cimv2:Win32_NtLogEvent]
→ ::oo::Obj14420
% set data_property [$event_class -with Properties_ Item Data]
→ ::oo::Obj14427
% puts "[$data_property Name] : CIMType=[$data_property CIMType], \
    IsArray=[$data_property IsArray]"
→ Data : CIMType=17, IsArray=1
% comobj_destroy $event_class $data_property

Here we have listed two additional pieces of information about the Win32_NtEventLog.Data. The value of IsArray is a boolean indicates that the Data property is actually an array while the CIMType value gives the base type. Both pieces are useful and necessary when writing code that is generic and does not know the real class to which an object belongs.

Note We do not discuss the possible values of CIMType here as the next section describes another, potentially more useful, way to get the type information for a property.

Other information about a property is also available through SWbemProperty. We examine one item, Qualifiers_, in the next section. For the rest, refer to WMI Reference.

8.3.2. Property qualifiers

Similar to class qualifiers, property qualifiers provide additional information about a property. The commonly used ones are summarised in Common class qualifiers. The full list is much larger and specified in WMI Reference.

Caution You can retrieve property qualifiers from a class instance as well. However, the set of qualifiers for a property retrieved from an instance may not include all those available from the property qualifiers in the class definition.
Table 15. Common property qualifiers
Qualifier Description

CIMType

A text string describing the type of the property.

Description

Contains a human readable description of the property.

DisplayName

Name to display to the user instead of the actual property name.

Key

Must be 1 if present. Specifies that the instance property uniquely identifies the instance. If multiple properties have this qualifier, the instance is identified collectively, not individually, by the key properties.

MappingsStrings

A string that identifies where a particular property value is retrieved. The string is for human consumption only but can be very useful, for example, if you want to know how to retrieve a value natively without using WMI.

Read

Boolean specifying that a property is read-only. Default is true.

ValueMap

Contains the list of valid values for the property.

Let us examine some of the property qualifiers for the Win32_Service class. Since class and property qualifiers are both retrieved as SWbemQualifierSet instances, We can use the same print_qualifiers and print_qualifier_value procedures we used earlier.

% set service_class [comobj_object winmgmts:Win32_Service]
→ ::oo::Obj14433
% set service_type_property [$service_class -with Properties_ Item ServiceType]
→ ::oo::Obj14440
% print_qualifiers $service_type_property
→ CIMTYPE = string
  MappingStrings = {Win32API|Service Structures|QUERY_SERVICE_CONFIG|dwServiceT...
  read = 1
  ValueMap = {Kernel Driver} {File System Driver} Adapter {Recognizer Driver} {...
% comobj_destroy $service_class $service_type_property

We make the following observations about the ServiceType property based on the output:

  • The presence of the ValueMap qualifier means the property value is restricted to being one of the strings listed.

  • The presence of the Read qualifier means the property is read-only and cannot be modified.

  • The MappingStrings qualifier gives some indication of how the value for the property is retrieved by the WMI provider. In this case, we see the property comes from the dwServiceType field of the QUERY_SERVICE_CONFIG structure retrieved through a native Win32 API call.

  • Perhaps the most useful is the CIMTYPE qualifier which we discuss below.

When writing generic code, for example, to display a table of instances, it can be useful to know the underlying type. This can be useful for correct formatting of numbers, displaying hyperlinks and so on.

For example, consider the Win32_LogicalDiskToPartition class we saw earlier which represents an association between a logical drive and a physical disk partition. Let us retrieve an instance of this class.

% set wbem_services [wmi_root]
→ ::oo::Obj14467
% $wbem_services -with {{InstancesOf Win32_LogicalDiskToPartition}} -iterate \
    instance break 1
% $instance Dependent
→ \\VULCAN\root\cimv2:Win32_LogicalDisk.DeviceID="C:"
% set property [$instance -with Properties_ Item Dependent]
→ ::oo::Obj14484
% print_qualifier_value $property CIMType
→ ref:Win32_LogicalDisk
% comobj_destroy $property $instance $wbem_services
1 Places the first element in the collection into instance and aborts the loop

Notice that Dependent appears to return a string. However by examining the CIMType qualifer, in a real application you can recognize it is actually a reference to a Win32_LogicalDisk object and display a live link.

Note The CIMTYPE qualifier should not be confused with the SWbemProperty CIMType property discussed in the previous section although they are both related and there is a one-to-one correspondence between the two. The latter is an integer value specifying the type. The former is a string and provides some more detail. In particular, in the case of reference types, it provides the type of the target reference as in the example below.

8.4. Introspection of methods

The final piece of introspection that remains to be explored pertains to methods.

8.4.1. The SWbemMethod object

All information pertaining to a method is encapsulated in a SWbemMethod object.

We have already described enumeration of methods and retrieving method definitions including parameter information so we will not repeat that here. We only examine other information about a method that is available through method qualifiers.

8.4.2. Method qualifiers

Like classes and properties, methods also have qualifiers that provide further information about the method. A few commonly encountered qualifiers are shown in Common method qualifiers.

Table 16. Common method qualifiers
Qualifier Description

Constructor

If present and true, indicates that the method will create a new instance of the class.

Description

Contains a human readable description of the method.

Destructor

If present and true, indicates that the method will delete an instance of the class and depending on the class and provider, any associations as well.

DisplayName

Name to display to the user instead of the actual method name.

Implemented

Indicates that the method is actually implemented by a provider and not a stub.

MappingsStrings

A string that identifies where a particular method is implemented. The string is for human consumption only but can be very useful, for example, if you want to know how to retrieve a value natively without using WMI.

Static

Indicates that the method is class method and not to be invoked on an instance. See the example later.

ValueMap

Contains the list of valid values for the method return value.

For illustrative purposes, let use examine two methods two methods of the Win32_Process class - Create, which creates a new process, and Terminate, which terminates an existing process.

We start off by retrieving the Win32_Process class and extracting the two method definitions of interest.

% set process_class [comobj_object winmgmts:Win32_Process]
→ ::oo::Obj14496
% set create_method [$process_class -with Methods_ Item Create]
→ ::oo::Obj14503
% set terminate_method [$process_class -with Methods_ Item Terminate]
→ ::oo::Obj14509

Qualifiers for methods are retrieved the same way as for classes and properties so we can once again use the print_qualifiers we defined earlier.

% print_qualifiers $create_method
→ Constructor = 1
  Implemented = 1
  MappingStrings = {Win32API|Process and Thread Functions|CreateProcess}
  Privileges = SeAssignPrimaryTokenPrivilege SeIncreaseQuotaPrivilege SeRestore...
  Static = 1
  ValueMap = 0 2 3 8 9 21 ..
% print_qualifiers $terminate_method
→ Destructor = 1
  Implemented = 1
  MappingStrings = {Win32API|Process and Thread Functions|TerminateProcess}
  Privileges = SeDebugPrivilege
  ValueMap = 0 2 3 8 9 21 ..
% comobj_destroy $process_class $create_method $terminate_method

Some points to note from the above example:

  • the presence of the Constructor and Destructor qualifiers on Create and Terminate respectively. Methods that neither create nor destroy instances will not have either qualifier.

  • the Static qualifier on Create. Obviously, you cannot invoke a method on an instance that is yet to be created. Thus Create is a class method and to be invoked on the Win32_Process class itself, not on one of its instances. Contrast this with the Terminate method which does not have this qualifier and has to be invoked on the instance to be terminated.

  • the MappingStrings qualifier for both hints at the Win32 API calls used for implementation.

  • the different privileges required for invoking the methods.

9. WMI Events

The discussion up to this point has solely been about managing resources and WMI infrastructure. We now look at another important capability provided by WMI - notifications of events.

Note The term event is overutilized. WMI events should not be confused with events from the Windows event log or Event Tracing for Windows (ETW).

An event in the WMI world may reflect a change in some state or a happening, either within WMI itself or in the real-world being managed through WMI.

9.1. Temporary and Permanent consumers

Consumers of WMI event notifications can be of two types - temporary or permanent.

A temporary consumer is an application that subscribes to WMI notifications and receives them as long as it is running. A permanent consumer is a COM object that need not be running but can be invoked on the occurence of an event.

Tcl does not currently support permanent consumers so this chapter only describes the use of temporary consumers. Windows itself does provide some built-in permanent consumers, CommandLineEventConsumer in particular, that can run external programs, including Tcl, in response to an event. However, these have limited function and are not discussed here.

9.2. Synchronous and Asynchronous notifications

Event notifications can be received by an application in synchronous or asynchronous mode. In the former case, the application makes blocking calls waiting for an event notification. In the latter case, callbacks are delivered through a callback mechanism.

For ease of exposition, most of the examples in this section use synchronous mode. Asynchronous event delivery is described in Receiving event notifications asynchronously.

9.3. Intrinsic and Extrinsic events

Events are classified as intrinsic or extrinsic.

Intrinsic events reflect changes in the internal structure of the CIM or data such as addition of a new class or a change in the data for a class instance. For example, a service stopping will be reflected as a change in the State property of the corresponding Win32_Service instance and is considered an intrinsic event. Intrisic events are represented by system defined classes, such as __InstanceModificationEvent which might be generated for the previous service scenario.

Extrinsic events are generated by WMI providers in response to real world events, such as shutdown. The WMI provider for the Windows registry also generates an extrinsic event when a registry key is modified. Note that this is considered and extrinsic event because the registry contents are not part of the CIM database.

We will describe monitoring intrisic events in the next section. Monitoring of extrinsic events follows a similar pattern but differs in some respects. These differences are highlighted in Consuming extrinsic events.

9.4. Event subscription quotas

Like many WMI operations, providing and consuming events can be WMI therefore sets quotas on a per-user and system-wide basis. These quotas include number of concurrent subscriptions, memory usage and polling instruction limits and are controlled through the _ArbitratorConfiguration object described in WMI resource limits.

9.5. Consuming intrinsic events

Just like all other objects, events are represented in WMI as classes. Intrinsic events, because they reflect the CIM model, all have the same structure and are defined through a small set of system classes derived from the __Event class. These are shown in Intrinsic event classes. They reflect the fact that intrinsic event notifications may pertain to three types of objects - namespaces, classes and class instances - and may be generated for creation, deletion and modification of the respective types.

Table 17. Intrinsic event classes
Class Description

__NamespaceOperationEvent

Events pertaining to creation, deletion or modification of namespaces

__NamespaceCreationEvent

Events pertaining to creation of namespaces

__NamespaceDeletionEvent

Events pertaining to deletion of namespaces

__NamespaceModificationEvent

Events pertaining to modification of namespaces

__ClassOperationEvent

Events pertaining to creation, deletion or modification of classes

__ClassCreationEvent

Events pertaining to creation of classes

__ClassDeletionEvent

Events pertaining to deletion of clasess

__ClassModificationEvent

Events pertaining to modification of clasess

__InstanceOperationEvent

Events pertaining to creation, deletion or modification of instances

__InstanceCreationEvent

Events pertaining to creation of instances

__InstanceDeletionEvent

Events pertaining to deletion of instances

__InstanceModificationEvent

Events pertaining to modification of instances

Subscribing to intrinsic events involves the following steps:

  1. Connecting the WMI service on the target system

  2. Running a query requesting events belonging to one of the classes shown in Consuming intrinsic events.

  3. Reading events from the query result

Our example code illustrates these steps by subscribing to event notifications for processes starting up. Subscriptions for other intrisic events follow the same exact model differing only in the query used.

One more time, we start of by connecting to the WMI service to get a SWbemServices object.

% set wbem_services [comobj_object winmgmts:root/cimv2]
→ ::oo::Obj14571

We now submit a query that asks to be notified whenever a new process starts up.

% set event_source [$wbem_services ExecNotificationQuery "SELECT * FROM \
    __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process'"]
→ ::oo::Obj14576

Let us consider each component of the query in turn.

SELECT * FROM __InstanceCreationEvent

This follows the same form that we have seen for a WQL query earlier. We ask the query to return instances of class __InstanceCreationEvent with each instance containing all relevant properties. As summarized in Intrinsic event classes, selecting from this class means

  • events related to deletion and modification will not notified

  • events related to classes and namespaces will not be notified

Obviously, the event instances returned are only for new events. They are not persisted.

The next clause is

WITHIN 1

If a WMI provider itself does not implement a more efficient event notification mechanism, WMI itself will do a poor man’s emulation by taking these snapshots of the specified class (in this case Win32_Processes) instances at regular intervals. It then compares consecutive snapshots and generates creation, deletion and modification events based on the comparison. The WITHIN 1 clause directs the snapshots to be taken at 1 second intervals. There is a tradeoff here. Choosing a short interval means more frequent snapshots and comparisons leading to greater system load. On the other hand, increasing the interval increases the chances that an event will be missed. In our example, if a process started and exited between two snapshots, it would not result in a notification being generated. In general, applications should not rely on snapshot based notifications unless they are confident that the state change for the resource will be last longer than the snapshot interval.

Note The WITHIN clause must be specified if the relevant WMI provider relies on this snapshot mechanism. WMI will return an error on the call if the clause is not specified in such a case.

The final clause

WHERE TargetInstance ISA 'Win32_Process'

is included because we are not interested in all instance creation notifications but only those for Win32_Process as they represent process creation events. We could have even qualified it further based on a property of the target class. Thus

WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.Name='notepad.exe'

would result in notifications only when Notepad was started and not for any other process.

As we see later, TargetInstance is a property of the __InstanceCreationEvent class which will hold an instance of Win32_Process for the newly created process.

Having set up the query, we wait a short interval (for the snapshot) and then start a process just to generate a notification.

% after 1000
% set pid [exec notepad.exe &]
→ 7616

We can now read the event from the event source using NextEvent which returns an instance of __InstanceCreationEvent.

The NextEvent method is a synchronous method and thus the application will stay blocked until a notification arrives or the call times out.

Tip You can specify a timeout of 0 to implement a polling model. Alternatively, you can use the asynchronous method using callbacks described later.
% set event [$event_source NextEvent 2000] 1
→ ::oo::Obj14581
1 Call raises an exception if an event is not received within 2000 milliseconds

__InstanceCreationEvent contains three properties:

  • SECURITY_DESCRIPTOR, which is the security descriptor attached to the event, if any

  • TIME_CREATED, which is a timestamp for the event

  • TargetInstance, which is an instance of the class we are monitoring, in this case Win32_Process.

We print out the properties of the process we just created.

% puts "Created new process with properties"
→ Created new process with properties
% $event -with {
    TargetInstance
    Properties_
} -iterate -cleanup prop {puts "  [$prop Name] = [$prop Value]"}
→   Caption = notepad.exe
    CommandLine = C:\windows\SYSTEM32\notepad.exe
    CreationClassName = Win32_Process
    CreationDate = 20150508144631.427692+330
    CSCreationClassName = Win32_ComputerSystem
...Additional lines omitted...
% comobj_destroy $event $event_source

The above code sequence can be adapted for any target and event class. The event classes in Intrinsic event classes all have the same structure except that the ones pertaining to modification events have an additional field - PreviousInstance - which contains the target instance before the modification event. You can use this to detect the nature of the modification.

Tip

You can use PreviousInstance as part of a query as well for modification event classes. For example,

WHERE TargetInstance ISA 'Win32_Service' AND PreviousInstance.State='Running' AND TargetInstance.State='Stopped'

9.6. Consuming extrinsic events

Extrinsic events are events that are not directly reflected in the CIM data model. For example, since the content of the Windows registry is not part of the CIM, changes to a registry key cannot be notified through the intrinsic events. For such cases, a WMI provider, StdRegProv for the Windows registry, has to implement its own classes for event notifications, such as the RegistryKeyChangeEvent class used for registry key changes.

The code sequence for subscribing to extrinsic events is very similar to that for intrinsic events. The differences are:

  • The query selects from a provider-specific class, like RegistryKeyChangeEvent instead of from one of the intrinsic classes.

  • There is no requirement for a WITHIN clause in the query since the event generation does not need to be based on snapshots

We previously illustrated intrinsic events using process startup notifications. Because the Windows provider also implements extrinsic event classes, Win32_ProcessTrace, Win32_ProcessStartTrace and Win32_ProcessStopTrace for these, we can use the same for extrinsic events as well.

Tip Using these extrinsic classes for process start notifications is to be preferred because they do not rely on snapshots and are therefore both more efficient and reliable.

Since we are only interested in processes starting, we will query for Win32_ProcessStartTrace instances and read from an event.

% set event_source [$wbem_services ExecNotificationQuery "SELECT * FROM \
    Win32_ProcessStartTrace"]
→ ::oo::Obj14773
% set pid [exec notepad.exe &]
→ 2140
% set event [$event_source NextEvent] 1
→ ::oo::Obj14777
1 Note we have not specified the optional time out here so the call will block forever until an event is received.

Extrinsic event classes, like intrinsic classes, are also derived from the root __Event class and have the SECURITY_DESCRIPTOR and TIME_CREATED properties described earlier. The other properties are specific to the event class.

Note These process trace event classes have nothing to do with the Win32_Process class. To get full details of a process, you would have to extract the process name and PID from the event and use them to retrieve the corresponding Win32_Process instance.

Here we just print the process name and PID.

% puts "Process [$event ProcessName] (PID [$event ProcessID]) started."
→ Process notepad.exe (PID 2140) started.
% comobj_destroy $event $event_source $wbem_services

9.7. Receiving event notifications asynchronously

So far we have used NextEvent to retrieve events. This method has some drawbacks because it is a synchronous blocking call which will not return until an event notification is available:

  • The application cannot do any other work while it is waiting for an event.

  • Only one event query can be monitored at a time.

We now describe how event notifications can be received in asynchronous fashion to remove these limitations.

Caution Note however, that asynchronous event notifications have the same security issues as any asynchronous method calls as described in Method calling modes so you may still prefer to use the polling mode using NextEvent with a zero timeout value that was described earlier.

Asynchronous WMI event notifications are implemented through the Windows COM connection points. The basic steps are:

  1. Write a callback procedure that should be invoked when a notification is received.

  2. Create a event sink object, an instance of the SWbemSink class

  3. Bind the callback procedure to the sink object

  4. Pass the sink object to a query for the events of interest

We repeat out earlier demonstration of process startup notifications, this time using asynchronous notifications. Although we are demonstrating with extrinsic events here, the same steps would be used for intrinsic events as well.

We start off by writing a callback procedure that will be invoked by the sink when a notification is received.

proc sink_callback {reason args} {
    switch $reason {
        OnObjectReady {
            lassign $args event_interface context_interface
            set event [comobj_idispatch $event_interface true]
            puts "Process [$event ProcessName] (PID [$event ProcessID]) \
                started."
            $event -destroy
        }
        OnCompleted {
            lassign $args winerror error_interface context_interface
            puts "Notifications ended. Reason: [map_windows_error $winerror]"
            set ::notifications_ended 1 1
        }
    }
    return
}
1 Only used to terminate the event loop in our demonstration. A real program would not need this.

A few, or perhaps more than a few, words are in order about the callback:

  • The first argument passed to the callback determines the reason for the callback. We need to deal with only two types, OnObjectReady and OnCompletion. Other values are gracefully ignored.

  • Remaining arguments are variable, depending on the reason value.

  • A reason of OnObjectReady indicates a new notification is being passed to the callback. In this case there are two additional arguments passed. The second is an optional context pointer which we do not use and hence ignore. The first additional argument is an interface pointer to the event, of class Win32_ProcessStartTrace in our case. This is a raw pointer, which we need to convert to a comobj COM object through comobj_idispatch. We can then use it as we did in previous examples to print out the ProcessName and ProcessID properties.

  • A reason of OnCompleted indicates that the query has been completed. This may be because it was ended under application control or because of some error. The first additional argument indicates the error code. The remaining arguments provide additional information and context which we do not use and ignore.

Important Note that the second argument in the comobj_idispatch call is true. This is important as explained in the chapter The Component Object Model. In a nutshell, our COM comobj wrapper will decrement the reference count on the passed-in COM interface as part of the -destroy call. Passing true as the second parameter to comobj_idispatch does a corresponding increment on the reference count. Without this increment, the event sink caller will be left with a dangling pointer as the decrement from -destroy will cancel the reference the caller was holding.

We then create an event sink and bind the callback procedure to it.

Creating a sink is straightforward, but note that unlike most other WMI objects, a sink is independent of the target system WMI provider.

% set sink [comobj WBemScripting.SWbemSink]
→ ::oo::Obj14780
% set sink_id [$sink -bind ::sink_callback]
→ 11

Binding the callback gives a handle that we store in sink_id. This is used later to unbind the callback.

All that remains to be done is to start the notifications using ExecNotificationQueryAsync.

% set wbem_services [comobj_object winmgmts:root/cimv2]
→ ::oo::Obj14784
% $wbem_services ExecNotificationQueryAsync $sink "SELECT * FROM \
    Win32_ProcessStartTrace"

Any new processes would now result in our notification callback being invoked. As proof, we do a demonstration by firing up a couple of processes.

% exec cmd /c exit
% exec ipconfig
→
  Windows IP Configuration


  Ethernet adapter Ethernet:
...Additional lines omitted...

We will hang around for a second and then cancel the notifications.

%
% after 1000 [list $sink Cancel]
→ after#2
% vwait ::notifications_ended
→ Process unsecapp.exe (PID 5692) started.
  Process cmd.exe (PID 7268) started.
  Process ipconfig.exe (PID 7296) started.
  Notifications ended. Reason: Call cancelled

We see the output from the callback showing processes being started as well as the cancellation of the notifications.

All that’s left to do is the unbind our callback script using the handle that was returned to use earlier and clean up.

% $sink -unbind $sink_id
% comobj_destroy $sink $wbem_services

And we’re done.

10. WMI Configuration

There are several settings associated with the WMI installation on a system that control directory structure, enabling of certain features, resource limits and other operational parameters.

Here we only describe how these can be accessed and modified. For a description of what the individual parameters do, please refer to WMI Reference.

The individual settings are properties in certain system classes and are accessed in the same manner we have seen throughout this chapter.

10.1. WMI settings

Operational parameters for WMI are accessible through the singleton instance of class Win32_WMISetting.

% set settings [comobj_object winmgmts:Win32_WMISetting=@] 1
→ ::oo::Obj14801
% $settings -with Properties_ -iterate -cleanup prop {puts "[$prop Name] = [$prop \
    Value]"}
→ ASPScriptDefaultNamespace = root\cimv2
  ASPScriptEnabled =
  AutorecoverMofs = {%windir%\system32\wbem\cimwin32.mof} {%windir%\system32\wb...
  AutoStartWin9X =
  BackupInterval =
  BackupLastTime =
  BuildVersion = 9200.16384
  Caption =
  DatabaseDirectory = C:\windows\system32\wbem\repository
  DatabaseMaxSize =
  Description =
  EnableAnonWin9xConnections =
  EnableEvents = 1
  EnableStartupHeapPreallocation = 0
  HighThresholdOnClientObjects =
  HighThresholdOnEvents = 20000000
  InstallationDirectory = C:\windows\system32\wbem
  LastStartupHeapPreallocation =
  LoggingDirectory = C:\windows\system32\wbem\Logs\
  LoggingLevel = 0
  LowThresholdOnClientObjects =
  LowThresholdOnEvents = 10000000
  MaxLogFileSize = 65536
  MaxWaitOnClientObjects =
  MaxWaitOnEvents = 2000
  MofSelfInstallDirectory =
  SettingID =
% $settings -destroy
1 Note @ signifies the singleton instance

10.2. WMI resource limits

WMI often deals with huge data sets, access to which can be an expensive affair in terms of system resources. Windows therefore places limits on the resources used by WMI calls. These limits are defined through the properties of the __ArbitratorConfiguration singleton class in the root namespace.

% set arbitrator [comobj_object winmgmts://./root:__ArbitratorConfiguration=@] 1
→ ::oo::Obj14918
% $arbitrator -with Properties_ -iterate -cleanup prop {puts "[$prop Name] = \
    [$prop Value]"}
→ OutstandingTasksPerUser = 30
  OutstandingTasksTotal = 3000
  PermanentSubscriptionsPerUser = 1000
  PermanentSubscriptionsTotal = 10000
  PollingInstructionsPerUser = 1000
  PollingInstructionsTotal = 10000
  PollingMemoryPerUser = 5000000
  PollingMemoryTotal = 10000000
  QuotaRetryCount = 10
  QuotaRetryWaitInterval = 15000
  TaskThreadsPerUser = 3
  TaskThreadsTotal = 30
  TemporarySubscriptionsPerUser = 1000
  TemporarySubscriptionsTotal = 10000
  TotalCacheDisk = 1048576
  TotalCacheDiskPerTask = 51250
  TotalCacheDiskPerUser = 102500
  TotalCacheMemory = 10240
  TotalCacheMemoryPerTask = 1024
  TotalCacheMemoryPerUser = 2048
  TotalUsers = 50
% $arbitrator -destroy
1 Note the class is in the root namespace

These properties can be accessed just like properties for any other object.

11. A Final Word about WMI

WMI offers tremendous benefits for management of computer systems:

  • By design, it is flexible enough to support management of a wide variety of computing resources and applications.

  • Remote systems can be managed in practically the same fashion as the local system.

  • Some information, for example BIOS configuration, is only exposed through WMI on some versions of Windows.

  • Because of its generalized interfaces and introspection capabilities, new applications and resources can be managed with very few changes and without having to write custom code.

  • Use of WQL can reduce the amount of code that needs to written for complex queries.

However, there are a couple of caveats to all this goodness:

  • WMI comes with an associated processing cost. When available, equivalent native interfaces are often an order of magnitude more efficient in CPU utilization and memory usage. Many native interfaces even support remote invocation. Moreover, if a script results in too much CPU or memory usage, aborting it often does not help as the bulk of the processing is actually taking place in the WMI service process.

  • The reality of WMI often lags the theory. Earlier implementations were often unstable and of variable quality. Newer versions of Windows are much better in this regard. Nevertheless, you will sometimes run into situations where the implemented methods and properties do not reflect what is promised by runtime schema queries. Applications should always be prepared to deal with properties being unavailable or being null.

For this reason, use of WMI outside of management applications should be carefully considered if native interfaces are available for the same purpose.

12. References

TUNS2002

Developing WMI Solutions, Tunstall, Cole, Addison-Wesley, 2002. Provides a comprehensive guide to the WMI architecture as well as practical development of WMI applications and providers in C++ and .Net.

SCR2003

Microsoft Windows 2000 Scripting Guide, Microsoft Press, 2003. Provides a comprehensive introduction to WMI from a scripting perspective. The Visual Basic examples can be translated to Tcl in straightforward fashion.

WMIREF

WMI Reference, Windows SDK documentation, http://msdn.microsoft.com/en-us/library/aa394582(v=vs.85).aspx.

WQLREF

WQL Reference, Windows SDK documentation, http://msdn.microsoft.com/en-us/library/aa394552(v=vs.85).aspx.