共用方式為


WMI Scripting Primer: Part 3

 

Greg Stemp, Dean Tsaltas, and Bob Wells
Microsoft Corporation

Ethan Wilansky
Network Design Group

January 23, 2003

Summary: Defines the WMI scripting library and shows how to use it to access and manage WMI managed resources. Walks through the seven basic script types that can be created using the WMI scripting library for such tasks as creating, deleting, and retrieving instances of managed resources, and more. (22 printed pages)

Greetings fellow script scribes! It's been awhile. Rather than bore you with excuses—like we were finishing the Microsoft® Windows® 2000 Scripting Guide (more on that later)—let's dive in, shall we? We're going to pick up where we left off in our WMI Scripting Primer series by turning your attention to the remaining piece of the WMI scripting puzzle, the WMI Scripting Library.

Before we get too far along, let's briefly recap what we've covered so far. In WMI Scripting Primer: Part 1, we covered the architecture and major components of WMI as they relate to WMI scripting. In WMI Scripting Primer: Part 2, we talked about the Common Information Model, the repository that holds the blueprints (class definitions) for WMI managed resources. Although we know many of you skipped Part 2 (based on the number of "Rate this page" hits), we're still going to assume you know the material. If you don't, well, you know where to find it.

WMI Scripting Library Defined

So, just what is the WMI scripting library? Let's use an analogy to answer that question. Think about your stereo system or the media player on your computer for a moment. What do all stereos have in common? Well, they all have a volume control, treble and bass controls, a tuner in the case of a radio, and perhaps an equalizer. And it doesn't matter whether you choose to listen to Beethoven, Led Zeppelin, Art of Noise, or whoever; the controls always work the same.

The WMI scripting library is like—not really, but humor us—the controls on your stereo. That is, the WMI scripting library provides a consistent set of controls (in the form of automation objects) that allow you to access and manage WMI managed resources. It doesn't matter whether you're managing computers, event logs, the operating system, processes, services, or pick your favorite resource; the objects in the WMI scripting library always work the same.

The consistency provided by the automation objects in the WMI scripting library is best conveyed by the finite set of tasks you can perform using the WMI scripting library. All told, you can create seven basic script types using the WMI scripting library.

  1. You can retrieve instances of WMI managed resources.
  2. You can read the properties of a WMI managed resource.
  3. You can modify the properties of a WMI managed resource.
  4. You can call a method of a WMI managed resource.
  5. You can create a new instance of a WMI managed resource.
  6. You can delete an instance of a WMI managed resource.
  7. You can subscribe to events to monitor the creation, modification, and/or deletion of a WMI managed resource.

You can think of the seven basic script types as script templates. And just like the volume control adjusts the loudness of any CD, cassette, 8-track tape, or .wma file, the WMI script templates can be used to manage any WMI managed resource. Once you understand a template well enough to manage one type of WMI managed resource, you can easily adapt the same template to manage hundreds of other WMI managed resources.

WMI Scripting Library Object Model

Now that we've established the WMI scripting library is your control panel to the entire WMI infrastructure, let's open the chassis and look inside. Figure 1 in Part 1 of this series showed you that the WMI scripting library is implemented in a single automation component named wbemdisp.dll that physically resides in the %SystemRoot%\system32\wbem directory.

All told, the WMI scripting library consists of twenty-four automation objects (nineteen in Windows 2000 and earlier), twenty-one of which are illustrated in the WMI scripting library object model diagram shown in Figure 1. Now, before you go into meltdown thinking you must learn the gory details of all twenty-four objects, let us politely point out that you do not. In fact, you'll be happy to learn you can create 6 of the 7 script templates listed earlier with a basic understanding of just two or three of the objects shown in Figure 1. What are those objects? Sit tight, you're getting ahead of us.

In addition to the twenty-four automation objects in the Microsoft Windows XP and Windows Server 2003 version of wbemdisp.dll, the scripting library also contains thirteen enumerations. Enumeration is just a fancy name for a group of related constants. We're not going to cover the groups of constants here, because they're covered quite well in the WMI SDK. To learn more about the WMI scripting constants, see Scripting API Constants in the WMI SDK.

In many ways, you can compare the automation objects in the WMI scripting library to the core interfaces provided by ADSI. What do we mean by that? Well, the ADSI core interfaces—IADs and IADsContainer, for example—provide a consistent approach to scripting objects in the Active Directory, irrespective of an object's class and attributes. Similarly, the automation objects in the WMI scripting library provide a consistent and uniform scripting model for WMI managed resources.

It's important to understand the relationship between the automation objects in the WMI scripting library (wbemdisp.dll) and the managed resource class definitions that reside in the CIM repository (objects.data). As we explained in Part 2, managed resource class definitions are the blueprints for the computer resources exposed through WMI. In addition to defining the resources that can be managed, the blueprints define the methods and properties unique to each managed resource.

The WMI scripting library, on the other hand, provides the general purpose set of automation objects scripts used to authenticate and connect to WMI, and subsequently access instances of WMI managed resources. Once you obtain an instance of a WMI managed resource using the WMI scripting library, you can access the methods and properties defined by the managed resource's class definition—as if the methods and properties were part of the scripting library itself.

Click here for larger image.

Figure 1. WMI scripting library object model, wbemdisp.dll

Interpreting the WMI Scripting Library Object Model

Although Figure 1 may not appear very intuitive at first glance, the WMI scripting library object model provides a great deal of insight into the mechanics of how WMI scripting works. The lines in Figure 1 point to the object that is obtained by calling a method (or accessing a property) of the originating object. For example, calling the SWbemLocator ConnectServer method returns a SWbemServices object. Calling the SWbemServicesExecNotificationQuery method returns a SWbemEventSource object. On the other hand, calling the SWbemServicesExecQuery or InstancesOf method returns a SWbemObjectSet collection. And calling the SWbemServicesGet method returns a SWbemObject.

Let's compare the WMI scripts presented in Part 1 and Part 2 of this series to the object model to see how they worked. Each script performed three basic steps common to many WMI scripts.

  1. Each script started by connecting to the WMI service on a target computer. The scripts used the Microsoft® Visual Basic® Scripting Edition (VBScript) GetObject function combined with a WMI connection string consisting of the WMI moniker, "winmgmts:", followed by a WMI object path to the target computer and namespace.

    strComputer = "."
    Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
    

    Connecting to WMI in this way returns a reference to the SWbemServices object shown in Figure 1. Once you obtain a reference to a SWbemServices object, you can call one of SwbemServices methods. The SWbemServices method you call largely depends on the type of WMI script you're creating.

  2. Next, each script retrieved instances of a WMI managed resource using the SWbemServicesInstancesOf method.

    Set colSWbemObjectSet = objSWbemServices.InstancesOf("Win32_Service")
    

    InstancesOfalways returns a SWbemObjectSet collection. As shown by the line between the SWbemServices and SWbemObjectSet objects in Figure 1, SWbemObjectSet is one of the three WMI scripting library object types SWbemServices can return.

  3. Finally, each script accessed properties of a WMI managed resource by enumerating the instances in the SWbemObjectSet.

    For Each objSWbemObject In colSWbemObjectSet
        WScript.Echo "Name: " & objSWbemObject.Name
    Next
    

    As illustrated in Figure 1, each managed resource instance in a SWbemObjectSet collection is represented by a SWbemObject.

Of the twenty-four automation objects in the WMI scripting library, the three most important—that is, the three you should learn first—are SWbemServices, SWbemObjectSet, and SWbemObject. Why? Because SWbemServices, SWbemObjectSet, and SWbemObject are an essential part of darn near every WMI script. In fact, if we revisit our ADSI analogy, SWbemServices, SWbemObjectSet, and SWbemObject are to WMI scripting what IADs and IADsContainer are to ADSI scripting. (Scripting Guys Freebie: There's a tip in there for those of you that haven't dabbled in ADSI scripting.—.That is, of the sixty interfaces provided by the ADSI library, IADs and IADsContainer are the two you should learn first. Trust us.)

SWbemServices is the object that represents an authenticated connection to a WMI namespace on a local or remote computer. Additionally, SWBemServices plays an important role in every WMI script. For example, you use the SWbemServicesInstancesOf method to retrieve all instances of a managed resource. Similarly, you use the SWbemServicesExecQuery method combined with a WQL (WMI Query Language) query to retrieve all or a subset of instances of a managed resource. And you use the SWbemServicesExecNotificationQuery method to subscribe to events that represent changes in the managed environment.

A SWbemObjectSet is a collection of zero or more SWbemObject objects. Why zero? Because it's possible for a computer to have zero instances of, say, a tape drive (modeled by Win32_TapeDrive). Each SWbemObject in a SWbemObjectSet can represent one of two things:

  1. An instance of a WMI managed resource.
  2. An instance of a class definition.

SWbemObject is the multiple-identity object that masquerades as the resource you're managing. For example, if you retrieve instances of the Win32_Process managed resource, SWbemObject takes on an identity modeled after the Win32_Process class definition, as shown on the left of Figure 2. On the other hand, if you retrieve instances of the Win32_Service managed resource, SWbemObject takes on an identity modeled after the Win32_Service class, as shown on the right of Figure 2.

Figure 2. SWbemObject masquerading as a Win32_Process and Win32_Service

If you examine Figure 2 closely, you'll notice SWbemObject exposes two distinct sets of methods and properties. The top set with the names that end with an underscore are part of SWbemObject and live in wbemdisp.dll. The underscores are used to prevent name collisions with methods and properties defined by a managed resource's class definition.

The bottom set of methods and properties are not part of SWbemObject. They are defined by a managed resource's class definition in the CIM. When you retrieve an instance or instances of a managed resource, SWbemObject dynamically binds to the methods and properties defined by the managed resource's class definition. You use SWbemObject to call the methods and access the properties defined in the managed resource's class definition as if the methods and properties were part of SWbemObject. The ability of SWBemObject to morph into any managed resource defined in the CIM is what makes WMI scripting so intuitive. Once you know how to connect and retrieve instances, everything is a SWbemObject.

OK. So what else does the WMI scripting library object model tell you? The object model tells you SWbemServices and SWbemObject can be directly created using the VBScript (or WSH) GetObject function combined with the WMI moniker (winmgmts:) and a WMI object path (for example, "[\\ComputerName][\Namespace][:ClassName][.KeyProperty='Value']"). On the other hand, SWbemLocator, SWbemLastError, SWbemObjectPath, SWbemNamedValueSet, SWbemSink, SWbemDateTime, and SWbemRefresher objects are created using the VBScript (or WSH) CreateObject function. The remaining objects cannot be created using GetObject or CreateObject. Instead, they are obtained by calling a method or accessing a property.

The object model also tells you that seven of the objects in the WMI scripting library expose a SWbemSecurity object, as indicated by the security callout icon immediately beneath or to the right of the object.

For more information about a specific scripting library object, method, or property, see Scripting API for WMI in the WMI SDK Documentation. To understand the basic mechanics of the WMI scripting library, let's turn our attention to the seven WMI script templates we listed earlier. Before we do that, how about a paragraph or two about variable naming conventions.

A Paragraph or Two About Variable Naming Conventions

In the example scripts that follow, the variable names used to reference each WMI automation object follow a consistent naming convention. Each variable is named according to the automation object's name in the WMI scripting library, and prefaced with "obj" (to indicate an object reference) or "col" (to indicate a collection object reference). For example, a variable that references a SWbemServices object is named objSWbemServices. A variable that references a SWbemObject is named objSWbemObject. And a variable that references a SWbemObjectSet is named colSWbemObjectSet.

Why is this important? Well, one could certainly argue that it's not. However, the idea is to help you understand the type of WMI object you're working with at different points in a WMI script. If it helps, great. If it doesn't, just ignore it. The other mildly important thing to keep in mind is that the object reference variable names can be whatever suits your fancy. If you prefer variable names like foo and bar, or dog and cat, that's fine too. There is no requirement stating you must name a reference to a SWbemServices object objSWbemServices. That's just how we did it.

The Scripting Guys Guide to WMI Script Templates

Admittedly, WMI has a reputation for being very difficult to learn and even more difficult to use. In many respects, this reputation has been acquired not so much because WMI really is difficult, but simply because it is so big. WMI can be used to manage computer hardware, computer software, and nearly everything in between. The assumption is that any technology that encompasses so many different elements must be difficult.

In reality, though, many of the tasks that can be performed by using WMI follow one of a handful of standard approaches. For example, you've already seen how a template can serve as the basis for scripts that return information about almost any managed resource. In Part 1 of this series, the same basic script (with one or two minor modifications) was used to return information about items as disparate as installed memory, services, and events recorded in the event logs.

The following topics present basic WMI script templates that can be used to:

  • Retrieve instances of a managed resource.
  • Display the properties of a managed resource.
  • Modify the properties of a managed resource.
  • Call a method of a managed resource.
  • Create a new instance of a managed resource.
  • Delete an instance of a managed resource.
  • Subscribe to events to monitor the creation, modification, and/or deletion of a managed resource.

Before we get started, we need to make sure we're perfectly clear on one important point: What you can and cannot do to a WMI managed resource is governed by the managed resource's blueprint (that is, class definition) in the Common Information Model (CIM) repository, and not the WMI scripting library. This is why Part 2 of this series is mildly important (hint, hint). Still not convinced? We'll give you a couple of examples.

You can only modify writeable properties. How do you determine if a property is writeable? You use WbemTest.exe, WMI CIM Studio, WMIC.exe, or a script to examine the property's Write qualifier. (See Figure 7 or Listing C in Part 2 of this series for an example of how to examine property qualifiers.) If the Write qualifier isn't defined for a property, the default value is FALSE, which means the property is read-only.

Here's another example. You can only create new instances of managed resources if the resource's class definition sets the SupportsCreate class qualifier to TRUE. How do you determine if a managed resource's class definition sets SupportsCreate to TRUE? You examine the managed resource's class qualifiers, again, as shown in Figure 7 and demonstrated in Listing C in Part 2 of this series.

**Note   **In practice you'll find some managed resources can be created, updated, and/or deleted even though the managed resource's class definition fails to set the appropriate qualifiers. We're told the situation is being corrected.

One more thing before we get started. All of the following script templates are designed to work on the local computer. This is done by setting the value of the variable strComputer to a dot ("."). To run a script against a remote computer, simply set the value of strComputer to the name of the remote computer. For example, this line of code causes a script to run against a computer named atl-dc-01:

strComputer = "atl-dc-01"

Retrieving Instances of a Managed Resource

Up to this point, we've used the SWbemServicesInstancesOf method to retrieve instances of managed resources, as shown in Listing 1.

Listing 1.   Retrieving services information using SWbemServices InstancesOf

strComputer = "."
Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colSWbemObjectSet = objSWbemServices.InstancesOf("Win32_Service")
For Each objSWbemObject In colSWbemObjectSet
    WScript.Echo "Display Name:  " & objSWbemObject.DisplayName & vbCrLf & _
                 "   State:      " & objSWbemObject.State       & vbCrLf & _
                 "   Start Mode: " & objSWbemObject.StartMode   & vbCrLf
Next

While InstancesOf certainly does the trick, what about situations where you only want a subset of instances or properties? Suppose you want to optimize instance and property retrieval to minimize network traffic. In such cases, you'll be glad to learn WMI supports a rich and powerful query facility.

Querying WMI is the process of issuing a request for managed resources that match some predefined criteria. For example, a WMI query can request only those services with a StartMode of Auto that are in a Stopped state.

WMI queries provide a more efficient mechanism for retrieving instances of managed resources and their properties than the InstancesOf method. WMI queries return only those instances and properties that match the query, whereas InstancesOf always returns all instances of a specified resource and all of the properties for each instance. Also, queries are processed on the target computer identified in the object path rather than on the source computer running the script. Therefore, WMI queries can significantly reduce the amount of network traffic that would otherwise be encountered by less efficient data retrieval mechanisms, such as InstancesOf.

To query WMI, you construct a query string using the WMI Query Language (WQL). The query string defines the criteria that must be satisfied to result in a successful match. After the query string is defined, the query is submitted to the WMI service using the SWbemServicesExecQuery method. Instances of managed resources that satisfy the query are returned to the script in the form of a SWbemObjectSet collection.

Using WQL and the ExecQuery method (rather than InstancesOf) provides you with the flexibility to create scripts that return only the items that are of interest to you. For example, you can use a basic WQL query to return all properties of all instances of a given managed resource, as shown in Listing 2. This is the same information that is returned by the InstancesOf method. If you compare Listings 1 and 2, you'll notice the bold part of Line 3 is the only difference between the two scripts.

Listing 2.   Retrieving services information using SWbemServices ExecQuery

strComputer = "."
Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colSWbemObjectSet = objSWbemServices.ExecQuery("SELECT * FROM Win32_Service")
For Each objSWbemObject In colSWbemObjectSet
    WScript.Echo "Display Name:  " & objSWbemObject.DisplayName & vbCrLf & _
                 "   State:      " & objSWbemObject.State       & vbCrLf & _
                 "   Start Mode: " & objSWbemObject.StartMode   & vbCrLf
Next

You can also create targeted queries using WQL, queries that do such things as:

  • Return only selected properties of all the instances of a managed resource.

    "SELECT DisplayName, State, StartMode FROM Win32_Service"
    
  • Return all the properties of selected instances of a class.

    "SELECT * FROM Win32_Service WHERE State = 'Stopped'"
    
  • Return selected properties of selected instances of a class.

    "SELECT DisplayName,State,StartMode FROM Win32_Service WHERE State='Stopped'"
    

Creating targeted queries will sometimes noticeably increase the speed with which data is returned. (For instance, it's much faster to return only those events in the Application event log that have EventCode 0 than to return all the events in all the event logs.) Targeted queries also make it easier to work with the returned data. For example, suppose you want only events from the Application event log with EventCode 0. Using a targeted query will return only those items. By contrast, InstancesOf would return all the events, and you would have to individually examine each one and determine whether it, 1) came from the Application event log and, 2) has EventCode 0. Although this can be done, it is less efficient and requires additional work on your part.

Targeted queries can also cut down on the amount of data that is returned, an important consideration for scripts that run over the network. Table 1 shows some relative figures for different query types. As you can see, there can be a considerable difference in the amount of data returned by the various query types.

Table 1.   Comparing different WMI instance retrieval methods and queries

Method/WQL Query Bytes Returned
objSWbemServices.InstancesOf("Win32_Service") 157,398
objSWbemServices.ExecQuery("SELECT * FROM Win32_Service") 156,222
objSWbemServices.ExecQuery("SELECT Name FROM Win32_Service") 86,294
objSWbemServices.ExecQuery("SELECT StartMode FROM Win32_Service") 88,116
objSWbemServices.ExecQuery("SELECT StartMode FROM Win32_Service WHERE State='Running'") 52,546
objSWbemServices.ExecQuery("SELECT StartMode, State FROM Win32_Service WHERE State='Running'") 56,314
objSWbemServices.ExecQuery("SELECT * FROM Win32_Service WHERE Name='WinMgmt'") 27,852
objSWbemServices.Get("Win32_Service.Name='WinMgmt'") 14,860

At this point, we hope we've convinced you ExecQuery is superior to InstancesOf. Now let's turn Listing 2 into a generic WMI script template that can easily be modified to retrieve instances of any WMI managed resource. Listing 3 contains our first template.

Listing 3.   Template for retrieving instances of managed resources

strComputer = "."
strNamespace = "\root\cimv2"
strClass = "Win32_Service"

Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & strNamespace)
Set colSWbemObjectSet = objSWbemServices.ExecQuery("SELECT * FROM " & strClass)

For Each objSWbemObject In colSWbemObjectSet
    WScript.Echo "Display Name: " & objSWbemObject.DisplayName
    WScript.Echo "State:        " & objSWbemObject.State
    WScript.Echo "Start Mode:   " & objSWbemObject.StartMode
Next

To use this template with other WMI classes:

  1. Set the value of strClass to the appropriate WMI class for the target-managed resource.

  2. If necessary, set the value of strNamespace to the WMI namespace for the target class.

  3. Replace the statements within the For Each loop that echo the properties and their values. Remove the following lines and replace them with the appropriate lines of code for the property values being displayed.

    WScript.Echo "Display Name: " & objSWbemObject.DisplayName
    WScript.Echo "State:        " & objSWbemObject.State
    WScript.Echo "Start Mode:   " & objSWbemObject.StartMode
    

**Scripting Guys Freebie   **If you're working with a managed resource that returns a lot of instances (we'll define a lot as more than 1000 for the purpose of this discussion), you can optimize the behavior of ExecQuery through the use of optional flags. For example, suppose you use ExecQuery to query Event Log records (modeled by the Win32_NTLogEvent class). As you already know, the Event Log(s) can contain thousands and thousands of records. By default, you may encounter performance problems associated with queries that return large result sets, such as Event Log queries. The reason has to do with the way WMI caches a SWbemObject reference for each and every instance, or in our example, for each and every Event Log record. To avoid the problem, you can tell ExecQuery to return a forward-only SWbemObjectSet, as demonstrated below.

strComputer = "."
strNamespace = "\root\cimv2"
strClass = "Win32_NTLogEvent"

Const wbemFlagReturnImmediately = &h10
Const wbemFlagForwardOnly = &h20

Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & strNamespace)
Set colSWbemObjectSet = objSWbemServices.ExecQuery("SELECT * FROM " & strClass, _
                        "WQL" _
                        wbemFlagReturnImmediately + wbemFlagForwardOnly)

' Insert remainder of script here (e.g., For Each Next loop)...

**Note   **The wbemFlagReturnImmediately flag (which is defined in one of the enumerations we briefly touched on earlier) is the default ExecQuery behavior and is semi-synchronous. The important optimization is the addition of the wbemFlagForwardOnly flag. Combining wbemFlagReturnImmediately with wbemFlagForwardOnly results in a forward-only enumerator. A forward-only enumerator performs much faster than the default enumerator, because WMI doesn't maintain references to objects in the SWbemObjectSet.

Displaying the Properties of a Managed Resource

One limitation of the script shown in Listing 3 is that it requires you to know, in advance, the names of all of the properties that you want to retrieve and display. What if you want to display values for all the properties of a resource, but you either do not know the property names or do not want to type the 40 or 50 lines of code required to display each property value? In that case, you can use the template in Listing 4, which automatically retrieves and displays the values of each property found in a class.

Listing 4.   Scriptomatic lite template

strComputer = "."
strNamespace = "\root\cimv2"
strClass = "Win32_Process"

Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & strNamespace)
Set colSWbemObjectSet = objSWbemServices.ExecQuery("SELECT * FROM " & strClass)

Wscript.Echo "Scriptomatic Lite - Class: " & strClass
Wscript.Echo "===========================" & String(Len(strClass), "=") & vbCrLf

intInstance = 1
For Each objSWbemObject In colSWbemObjectSet
    WScript.Echo "Instance: " & intInstance & vbCrLf & "--------------"
    For Each objSWbemProperty In objSWbemObject.Properties_
        strPropertyValue = ConvertPropertyValueToString(objSWbemProperty.Value)
        WScript.Echo objSWbemProperty.Name & ": " & strPropertyValue
    Next
    WScript.Echo
    intInstance = intInstance + 1
Next

Function ConvertPropertyValueToString(ByVal PropertyValue)
    If IsObject(PropertyValue) Then
        ConvertPropertyValueToString = "<CIM_OBJECT (embedded SWbemObject)>"
    ElseIf IsNull(PropertyValue) Then
        ConvertPropertyValueToString = "<NULL>"
    ElseIf IsArray(PropertyValue) Then
        ConvertPropertyValueToString = Join(PropertyValue, ",")
    Else
        ConvertPropertyValueToString = CStr(PropertyValue)
    End If
End Function

To use this template with other WMI classes:

  1. Set the value of strClass to the appropriate WMI class for the target-managed resource.
  2. If necessary, set the value of strNamespace to the WMI namespace for the target class.

Modifying the Properties of a Managed Resource

In Windows 2000, WMI is primarily a read-only technology. Of the 4,395 properties defined in the Windows 2000 root\cimv2 namespace, only 39 properties are writeable. Those numbers improve in Microsoft® Windows® XP, where 145 of approximately 6560 properties are writeable. And the numbers get even better in Windows Server 2003.

The template in Listing 5 demonstrates how to modify a writeable property. The script retrieves all instances of the managed resource modeled by the Win32_OSRecoveryConfiguration class. (In this case, the class contains only a single instance.) The script provides new values for three properties—DebugInfoType, DebugFilePath, and OverWriteExistingDebugFile—and then commits the changes (and thus configures operating system recovery options) using the SWbemObjectPut_ method. If you forget to call the Put_ method, the changes will not be applied.

**Note   **This template works only for properties that are writeable. Attempting to change a read-only property will result in an error. To determine if a property is writeable, examine the property's Write qualifier.

Listing 5.   Template for modifying writeable properties of a managed resource

strComputer = "."
strNamespace = "\root\cimv2"
strClass = "Win32_OSRecoveryConfiguration"

Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & strNamespace)
Set colSWbemObjectSet = objSWbemServices.ExecQuery("SELECT * FROM " & strClass)

For Each objSWbemObject In colSWbemObjectSet
    objSWbemObject.DebugInfoType = 1
    objSWbemObject.DebugFilePath = "c:\tmp\memory.dmp"
    objSWbemObject.OverWriteExistingDebugFile = False
    objSWbemObject.Put_
Next

Notice how SWbemObject is used inside the For Each loop to: 1) Directly access and modify properties defined by the Win32_OSRecoveryConfiguration class, and 2) call its own Put_ method to commit the change.

To use this template with other WMI classes that implement writeable properties:

  1. Set the value of strClass to the appropriate WMI class for the target-managed resource.

  2. If necessary, set the value of strNamespace to the WMI namespace for the target class.

  3. Replace the statements within the For Each loop that configure new property values. Remove the following lines, and replace them with the appropriate lines of code for the properties being modified:

    objSWbemObject.DebugInfoType = 1
    objSWbemObject.DebugFilePath = "c:\tmp\memory.dmp"
    objSWbemObject.OverWriteExistingDebugFile = False
    

Calling a Method of a Managed Resource

Methods defined in a managed resource's class definition allow you to perform actions on the managed resource. For example, the Win32_Service class includes methods that let you perform such tasks as starting and stopping services; the Win32_NTEventlogFile class includes methods for backing up and clearing event logs; the Win32_OperatingSystem class includes methods for rebooting or shutting down a computer.

Listing 6 provides a template that can be used to write scripts that call WMI managed resource methods. This particular script uses the StopService method of the Win32_Service class to stop the Alerter service on the local computer.

**Note   **Before you can call a method defined in a managed resource's class definition, the method must be implemented. How do you determine if a method is implemented? Examine the method's implemented qualifier. A value of TRUE indicates that a method has an implementation supplied by a provider. Having said that, be aware that some methods do not define the implemented qualifier although the method is implemented. The Win32_ServiceStopService method, shown below, is an example of such a method. The bottom line is determining whether or not a method is implemented can also involve a bit of trial and error. As we mentioned earlier, we're told the situation is being corrected.

Listing 6.   Template for calling a method of a managed resource

strComputer = "."
strNamespace = "\root\cimv2"
strClass = "Win32_Service"
strKey = "Name"
strKeyValue = "Alerter"

Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & strNamespace)
Set colSWbemObjectSet = objSWbemServices.ExecQuery _
    ("SELECT * FROM " & strClass & " WHERE " & strKey & "='" & strKeyValue & "'")

For Each objSWbemObject in colSWbemObjectSet
     objSWbemObject.StopService()
Next

To use this template with other WMI classes:

  1. Set the value of strClass to the appropriate WMI class for the target-managed resource.

  2. If necessary, set the value of strNamespace to the WMI namespace for the target class.

  3. Set the value of strKey to the name of the property that forms the basis of the WHERE clause.

  4. Set the value of strKeyValue to an appropriate value for strKey.

  5. Replace the statement within the For Each loop that calls the method. Remove the following line, and replace it with the appropriate line of code for the method being called. If necessary, you must also include the appropriate method parameters.

    objSWbemObject.StopService()
    

Creating a New Instance of a Managed Resource

Some WMI classes allow you to create a new instance of the resource they model. For example, you can use the Win32_Environment class to create environment variables, the Win32_Process class to create processes, and the Win32_Share class to create shared resources, to name a few.

Before you create a new instance of a resource, you must verify that the managed resource's class supports the create operation. You do this by examining the class's SupportsCreate qualifier. A value of TRUE indicates the class supports the creation of instances (the default is FALSE). Once you've determined the class supports the create operation, you must determine the method used to create new instances. There are two approaches to creating new instances:

  1. If the class defines the CreateBy class qualifier with a value of PutInstance, you use the SWbemObjectSpawnInstance_ and Put_ methods to create the new instance.
  2. If the value assigned to the CreateBy class qualifier is something other than PutInstance (Create, for example), you use the method identified by the CreateBy qualifier to create the new instance.

Let's look at a template for each.

Listing 7 demonstrates how to create an instance of a resource when the resource's class definition sets SupportsCreate to TRUE and CreateBy to PutInstance. After connecting to WMI on the target computer, your first step is obtaining the blueprint (that is, class definition) for the thing you're creating. To do that, you use the SWbemServicesGet method to fetch the actual WMI class (rather than retrieve instances of the class). After you have an object representing the class, use the SWbemObjectSpawnInstance_ method to create a new, "blank," instance of the class. Set the properties for the new instance, and call the SWbemObjectPut_ method to create the new instance.

Listing 7.   Template for creating a new instance using SpawnInstance_ and Put_

strComputer = "."
strNamespace = "\root\cimv2"
strClass = "Win32_Environment"

Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & strNamespace)
Set objSWbemObject = objSWbemServices.Get(strClass)
Set objNewSWbemObject = objSWbemObject.SpawnInstance_()

objNewSWbemObject.Properties_.Item("Name") = "TMPSHARE"
objNewSWbemObject.Properties_.Item("UserName") = "<SYSTEM>"
objNewSWbemObject.Properties_.Item("VariableValue") = "c:\tmp"

objNewSWbemObject.Put_

To use this template with other WMI classes that support PutInstance:

  1. Set the value of strClass to the appropriate WMI class for the target-managed resource.

  2. If necessary, set the value of strNamespace to the WMI namespace for the target class.

  3. Replace the statements that configure the values for the environment variable. Remove the following lines, and replace them with the appropriate lines of code for the object being created:

    objNewSWbemObject.Properties_.Item("Name") = "TMPSHARE"
    objNewSWbemObject.Properties_.Item("UserName") = "<SYSTEM>"
    objNewSWbemObject.Properties_.Item("VariableValue") = "c:\tmp"
    

**Scripting Guys Freebie   **When creating a new instance using the SWbemObjectSpawnInstance_ and Put_ methods, you must provide value(s) for all of the class's key properties. For example, the Win32_Environment class used in Listing 7 defines two key properties: Name and UserName. How do you determine a class's key(s) property or properties? You use WbemTest.exe, WMI CIM Studio, WMIC.exe, or a script to examine the property's Key qualifier.

Listing 8 demonstrates how to create an instance of a resource when the resource's class definition provides its own create method. After connecting to WMI on the target computer, your first step is obtaining the blueprint (that is, class definition) for the thing you're creating. To do that, you use the SWbemServicesGet method to fetch the actual WMI class (rather than retrieve instances of the class). After you have an object representing the class, use SWbemObject to call the method identified by the class's CreateBy qualifier. The script template in Listing 8 uses the Win32_Share Create method to create a new shared folder.

Listing 8.   Template for creating a new instance using a managed resource method

strComputer = "."
strNamespace = "\root\cimv2"
strClass = "Win32_Share"

Const SHARED_FOLDER = 0
strPath = "c:\tmp"
strShareName = "tmp"
intMaximumAllowed = 1
strDescription = "Temporary share"

Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & strNamespace)
Set objSWbemObject = objSWbemServices.Get(strClass)

intReturnValue = objSWbemObject.Create(strPath, _
                                       strShareName, _
                                       SHARED_FOLDER, _
                                       intMaximumAllowed, _
                                       strDescription)

WScript.Echo "Return value: " & intReturnValue

To use this template with other WMI classes that provide a custom create method:

  1. Set the value of strClass to the appropriate WMI class for the target-managed resource.

  2. If necessary, set the value of strNamespace to the WMI namespace for the target class.

  3. Replace the statements that initialize the variables that represent the parameters passed to the create method. Remove the following lines, and replace them with the appropriate lines of code for the object being created:

    Const SHARED_FOLDER = 0
    strPath = "c:\tmp"
    strShareName = "tmp"
    intMaximumAllowed = 1
    strDescription = "Temporary share"
    
  4. Replace the Win32_Share Create method call with the appropriate method for the managed resource you're creating. Remove the following lines, and replace them with the appropriate lines of code for the object being created:

    intReturnValue = objSWbemObject.Create(strPath, _
                                           strShareName, _
                                           SHARED_FOLDER, _
                                           intMaximumAllowed, _
                                           strDescription)
    

**Scripting Guys Freebie   **When creating a new instance using a method provided by a managed resource's class definition, you must provide values for any mandatory parameters defined by the method. For example, the Win32_Share class used in Listing 8 defines three mandatory parameters: Path, Name, and Type. How do you determine a method's mandatory parameters? Refer to the managed resource's class definition in the WMI SDK.

Deleting an Instance of a Managed Resource

If you can create new instances of managed resources, it stands to reason you can delete instances too, and you can. In fact, the rules that govern which managed resource instances you can delete are strikingly similar to those governing the create operation. Let's review the requirements, and then we'll look at a couple of examples.

Before you delete an instance of a resource, you must verify that the managed resource's class supports the delete operation. You do this by examining the class's SupportsDelete qualifier. A value of TRUE indicates the class supports delete (the default is FALSE). Once you've determined the class supports delete, you must determine the method used to delete instances. There are a couple of approaches to deleting instances:

  1. If the class defines the DeleteBy class qualifier with a value of DeleteInstance, you can use SWbemServicesDelete or the SWbemObjectDelete_ method to delete an instance.
  2. If the value assigned to the DeleteBy class qualifier is something other than DeleteInstance (delete, for example), you use the method identified by the DeleteBy qualifier to delete an instance.

Listings 9 and 10 demonstrate how to delete the environment variable created in Listing 7. Listing 9 uses the SWbemServicesDelete method and Listing 10 uses the SWbemObjectDelete_ method. You can use Listings 9 or 10 when the resource's class definition sets SupportsDelete to TRUE and DeleteBy to DeleteInstance.

Listing 9.   Template for deleting an instance using the SWbemServices Delete method

strComputer = "."
strNamespace = "\root\cimv2"
strInstance = "Win32_Environment.Name='TMPSHARE',UserName='<SYSTEM>'"

Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & strNamespace)
objSWbemServices.Delete strInstance

Listing 10.   Template for deleting an instance using the SWbemObject Delete_ method

strComputer = "."
strNamespace = "\root\cimv2"
strInstance = "Win32_Environment.Name='TMPSHARE',UserName='<SYSTEM>'"

Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & strNamespace)
Set objSWbemObject = objSWbemServices.Get(strInstance)
objSWbemObject.Delete_

To use these templates with other WMI classes that support DeleteInstance:

  1. Set the value of strInstance to the appropriate WMI class, key(s), and key value(s) for the target-managed resource instance.

Listing 11 deletes the shared folder created in Listing 8, and in so doing, demonstrates how to delete an instance of a resource when the resource's class definition provides its own delete method. Take a second to compare Listings 10 and 11. See any difference outside of the obvious value assigned to strInstance? Listing 10 uses the SWbemObjectDelete_ method (note the underscore) to delete instances when the managed resource's class definition sets the DeleteBy class qualifier to DeleteInstance. Listing 11, on the other hand, is using the Win32_Share Delete method.

Listing 11.   Template for deleting an instance using a managed resource method

strComputer = "."
strNamespace = "\root\cimv2"
strInstance = "Win32_Share.Name='tmp'"

Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & strNamespace)
Set objSWbemObject = objSWbemServices.Get(strInstance)
objSWbemObject.Delete

To use this template with other WMI classes that provide a custom delete method:

  1. Set the value of strInstance to the appropriate WMI class, key(s), and key value(s) for the target-managed resource instance.

Subscribing to Events

Okay. It's time to practice programmer virtue number one: laziness! Don't worry, we're still going to cover event subscriptions. However, rather than cover them here, we're going to point you to our sister publication: TechNet's Tales from the Script column, where we just published A Brief Introduction to WMI Events. So in addition to getting an introduction to WMI event subscriptions, you discover yet another scripting resource.

All for Now

This ends our trilogy on WMI scripting. Admittedly, there's more to cover and we will. If you'd like to send us suggestions for Scripting Clinic topics you'd like to read, we'd love to hear them. You can drop us a note at scripter@microsoft.com or in the User Comments at the top of this page.

One more thing before we forget. Remember the Microsoft Windows 2000 Scripting Guide, Automating System Administration book we mentioned at the beginning of the article? Well, it's done! And while we hope you all rush out and buy a copy—Bookpool is cheapest technical book e-tailer on the planet by the way—we also recognize the planet is full of cheapskates, such as ourselves. For that reason, we've also posted all 1328 pages of the book online. So the next time Scripting Clinic is, um, late, you have somewhere to turn. Should you decide to read all or some of the book, by all means, let us know what you think—good, bad, or ugly. Of course, we're more interested in what's missing and what we can do to improve version 2. Have fun!

 

Scripting Clinic

Greg Stemp has long been acknowledged as one of the country's foremost authorities on scripting, and has been widely acclaimed as a world-class... huh? Well, how come they let football coaches make up stuff on their resumes? Really? He got fired? Oh, all right. Greg Stemp works at... Oh, come on now, can't I even say that? Fine. Greg Stemp gets paid by Microsoft, where he tenuously holds the title of lead writer for the System Administration Scripting Guide.

Dean Tsaltas is a Nova Scotian living in Redmond. He has become fluent in American and even chuckles at the accent of his friends and family back in the Maritimes. He got his start in computing at a tender age when his grandma and parents chipped in and bought him his beloved C-64 and a subscription to Compute!'s Gazette. He has been at Microsoft for a couple of years now and has a message for friends and family back home and in Vancouver: "No, I have not met Bill!"

Bob Wells wanders around aimlessly espousing the virtues of scripting to anyone who will listen. Rumor has it that Bob's two dachshunds know more about scripting than most humans. In his spare time, Bob contributes to the System Administration Scripting Guide.

Ethan Wilansky spends a lot of his work time writing and consulting. He's crazy about scripting, Yoga, gardening, and his family (not necessarily in that order). He is currently working on a way to create script that will take out the trash and wash the dinner dishes.