WMI Scripting Primer: Part 2
Greg Stemp, Dean Tsaltas, and Bob Wells
Microsoft Corporation
Ethan Wilansky
Network Design Group
August 13, 2002
Summary: The Scripting Guys continue their discussion of writing WMI scripts, this time focusing on the CIM repository(
容器) and CIM classes to help you tap the full power of WMI scripting. (39 printed pages)
If you're going to build a house, you need to know how to read and interpret an architectural drawing. If you're going to build an electronic gadget, you need to know how to read and interpret a schematic diagram. And if you're going to write WMI scripts, you guessed it, you need to know how to interpret WMI's blueprint for management—the CIM repository. The CIM repository, which is also referred to as the WMI repository in the WMI SDK, is the WMI schema that stores the class definitions that model WMI managed resources.
To emphasize the importance of the CIM and CIM classes, consider the four scripts presented in WMI Scripting Primer: Part 1, and Listings 1 and 2 below. Listing 1 is a slightly enhanced version of the services script from Part 1, and Listing 2 is yet another variation of the same script using the Win32_OperatingSystem class.
Note If you find Listings 1 and 2 confusing, we encourage you to read (or reread as the case may be) Part 1 of this series.
Listing 1. Retrieving services information using WMI and VBScript
strComputer = "." ' Dot (.) equals local computer in WMI
Set objWMIService = GetObject("winmgmts:\\" & strComputer)
Set colServices = objWMIService.InstancesOf("Win32_Service")
For Each objService In colServices
WScript.Echo "Name: " & objService.Name & vbCrLf & _
"Display Name: " & objService.DisplayName & vbCrLf & _
" Description: " & objService.Description & vbCrLf & _
" Path Name: " & objService.PathName & vbCrLf & _
" Start Mode: " & objService.StartMode & vbCrLf & _
" State: " & objService.State & vbCrLf
Next
Listing 2. Retrieving operating system information using WMI and VBScript
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer)
Set colOperatingSystems = objWMIService.InstancesOf("Win32_OperatingSystem")
For Each objOperatingSystem In colOperatingSystems
Wscript.Echo "Name: " & objOperatingSystem.Name & vbCrLf & _
"Caption: " & objOperatingSystem.Caption & vbCrLf & _
"CurrentTimeZone: " & objOperatingSystem.CurrentTimeZone & vbCrLf & _
"LastBootUpTime: " & objOperatingSystem.LastBootUpTime & vbCrLf & _
"LocalDateTime: " & objOperatingSystem.LocalDateTime & vbCrLf & _
"Locale: " & objOperatingSystem.Locale & vbCrLf & _
"Manufacturer: " & objOperatingSystem.Manufacturer & vbCrLf & _
"OSType: " & objOperatingSystem. OSType & vbCrLf & _
"Version: " & objOperatingSystem.Version & vbCrLf & _
"Service Pack: " & objOperatingSystem.ServicePackMajorVersion & _
"." & objOperatingSystem.ServicePackMinorVersion & vbCrLf & _
"Windows Directory: " & objOperatingSystem.WindowsDirectory
Next
The only distinguishing characteristic in each of the scripts in Part 1 of this article series, and in Listings 1 and 2 in this article, is the class name identifying a WMI managed resource and a subset of each class's properties. The fact that the same script template can be used to retrieve total physical memory, services, event-log records, processes, and operating-system information demonstrates the important role CIM classes play in WMI scripting. Once you know how to write a script to manage one type of WMI managed resource, you can use the same basic technique for other managed resources.
Of course, knowing a managed resource's class name and the class's corresponding properties is only part of the story. Before you can tap the full power of WMI scripting, you need to know a little bit more about the structure of the CIM repository and CIM classes. Why? We'll give you two important reasons.
Both points are true regardless of the WMI tool you use; whether you use the WMI scripting library, the WMI command-line tool (wmic.exe), or an enterprise-management application, you need to know how to navigate the CIM and interpret CIM classes.
A less obvious, yet equally important reason to learn the CIM, is that the CIM is an excellent source of documentation for WMI managed resources. Yes, if you need detailed information about a WMI class, you can use the WMI SDK. But what if you don't need detailed information about a WMI class? Suppose you only want to know if a specific class, method, or property is supported on the version of Microsoft® Windows® you're running. Well, look in the target computer's CIM.
For example, we're often asked, "Why doesn't the Join Computer to a Domain script in TechNet's Script Center work on Windows 2000?" The answer is because the Win32_ComputerSystem class (which is the WMI class used in the script) doesn't support the JoinDomainOrWorkGroup method on Windows 2000. The JoinDomainOrWorkGroup method was added to the Win32_ComputerSystem class in the version of WMI built-in to Windows XP and Windows Server 2003.
So how would you know or learn this? One way is by using the collection of WMI tools we listed in Part 1. A second, more powerful and flexible approach is by using the WMI scripting library. One of the really cool things about WMI is that you can use the WMI scripting library to learn about WMI. That's right; in the same way you write WMI scripts to retrieve WMI managed resources, you can also write WMI scripts to learn all sorts of interesting details about WMI itself. You can write WMI scripts to list all the namespaces and classes in the CIM repository. You can write scripts to list all the providers installed on a WMI-enabled computer. You can even write WMI scripts to retrieve managed-resource class definitions.
Whether you choose to use existing tools or create your own, you need a basic understanding of the CIM repository's structure, its contents, and how to interpret managed-resource class definitions. So let's pick up where we left off in Part 1 by taking a closer look at WMI's blueprint for management—the CIM repository. Throughout the discussion, we'll show you how to retrieve WMI configuration information and managed-resource class definitions using the WMI scripting library.
Blueprint for Management
In Part 1, we said WMI is based on the idea that configuration and management information from different sources can be uniformly represented with a schema, and that the CIM repository is the schema for WMI. Think of a schema as a blueprint or model that represents something that exists in the real world. Much like an architectural drawing models a physical structure, such as a house, the WMI CIM models the hardware, operating system, and software that make up a computer. The CIM is the data model for WMI.
Note Although the CIM repository is capable of storing some data (and it does), its primary purpose is to model the managed environment. The CIM is not designed to store the volumes of management information it defines. Instead, most of the data is dynamically retrieved, on demand, from a WMI provider. The exception is WMI operational data. WMI operational data, such as namespace information, provider registration information, managed-resource class definitions, and permanent event subscriptions, is stored in the CIM repository.
Figure 1 provides a conceptual view of the internal structure and organization of the CIM repository. As illustrated in Figure 1, the CIM uses classes to create the data model. Admittedly, the CIM contains far more classes than the eleven shown in the diagram—somewhere in the neighborhood of 5000 the last time we counted on Windows Server 2003. The fact that the schema is a complex mesh of approximately 5000 classes is largely academic in the context of WMI scripting. What's important to understand is that the CIM repository is the class store that defines the WMI managed environment and every manageable resource exposed through WMI.
There are three important CIM concepts illustrated in Figure 1 that you need to understand to successfully navigate and interpret the WMI schema.
Let's examine each of these CIM concepts in more detail.
Figure 1. Structural view of the CIM repository—the WMI schema
Note The CIM physically resides in the file named %SystemRoot%\system32\wbem\Repository\FS\objects.data on Windows XP and Windows Server 2003. Windows 2000 and Windows NT 4.0 Service Pack 4 store the CIM in %SystemRoot%\system32\wbem\Repository\cim.rep. And in Windows Millennium Edition (Me), Windows 98, and Windows 95 OSR 2.5 operating systems, the CIM is stored in %windir%\system\wbem\Repository\cim.rep.
Namespaces Defined
CIM classes are organized into namespaces. Namespaces are the partitioning mechanism employed by the CIM and control the scope and visibility of managed-resource class definitions. Each namespace in the CIM contains a logical group of related classes representing a specific technology or area of management. All classes within a namespace must have a unique class name, and classes in one namespace cannot be derived from classes in another namespace, which is why you'll find identical system, core, and common classes defined in multiple namespaces.
Most of the classes that model Windows managed resources reside in the root/cimv2 namespace. However, root\cimv2 is not the only namespace you need to be aware of, as suggested in Figure 1. For example, the event log, performance counter, Windows Installer, and Win32 providers all store their managed-resource class definitions in the root\cimv2 namespace. The registry provider, on the other hand, stores its class definitions in the root\default namespace. And the new Windows Server 2003 DNS provider stores its managed-resource class definitions in the root\MicrosoftDNS namespace.
Namespace Usage
So how do namespaces affect your WMI scripts? Every WMI script connects to a namespace as part of the initial connection step we briefly discussed last month, as shown below:
strComputer = "."
Set wbemServices = GetObject("winmgmts:\\" & strComputer)
If the target namespace is not specified, as is the case above, the script connects to the namespace identified by the following registry setting:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WBEM\Scripting\Default Namespace.
The default namespace setting is to WMI scripting what the %PATH% environment variable is to the operating system. When you submit a command via the command prompt without specifying the command's fully qualified path, the operating system uses the %PATH% environment variable to locate the command's executable file. If the operating system cannot find the file, an error is generated.
Similarly, when you retrieve a managed resource in a WMI script, the CIMOM (WMI service) looks for the managed resource's blueprint (class definition) in the default namespace if no namespace is specified. If the CIMOM cannot find the managed-resource class definition in the default namespace, a WBEM_E_INVALID_CLASS (0x80041010) error is generated.
Note Don't confuse the Default Namespace setting with the root\DEFAULT namespace. They are unrelated, unless of course, you set root\DEFAULT as your default namespace.
The root\cimv2 namespace is initially configured as the default namespace for scripting; however, the default scripting namespace can easily be changed. As such, you should always identify a managed resource's namespace in your WMI scripts rather than assume the default. Had we followed our own advice last month, the connection step in all four listings (and Listings 1 and 2 of this article) would have been written as follows.
strComputer = "."
Set wbemServices = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Adding the target namespace to the connection string tells the CIMOM where to look for the managed resource's class definition in the CIM, much like a fully qualified path tells the operating system where exactly to look for a file. When you specify the target namespace, the default namespace is not used.
(在连接字符串中增加目标的命名空间可以直接告诉CImom在CIM中到那里去寻找托管资源的类,类似于在操作系统中使用全路径来确切说明一个文件的位置一样。当指定了目标命名空间后, 默认的命名空间不再使用)
Managing the Default Namespace for Scripting
You can use the WMI scripting library in combination with the Win32_WMISetting class to read and change the default namespace for scripting as demonstrated in Listings 3 and 4. Win32_WMISetting is a dynamic class that models operational parameters for the WMI service. The writeable property representing the default namespace for scripting is ASPScriptDefaultNamespace.
Listing 3 uses the same three WMI scripting steps—connect, retrieve, and display—that we've been using all along with one notable change. As we recommended earlier, we specify the fully qualified namespace for the Win32_WMISetting class in the WMI connection string passed to the GetObject function of Microsoft® Visual Basic®, Scripting Edition (VBScript). And here you thought Microsoft didn't follow their own advice. Not only do we follow our namespace recommendation in Listing 3, but we'll be qualifying namespaces from this point forward. Yes, it's that important if you want to avoid invalid class errors in your WMI scripts.
Listing 3. Retrieving the default namespace for scripting using WMI and VBScript
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colWMISettings = objWMIService.InstancesOf("Win32_WMISetting")
For Each objWMISetting in colWMISettings
Wscript.Echo "Default namespace for scripting: " & _
objWMISetting.ASPScriptDefaultNamespace Next
To run the example script in Listing 3, copy and paste the script into your favorite text editor, save the script with a .vbs extension, and run the script as shown in Figure 2. You should see the local computer's default namespace echoed to the console.
Figure 2. GetDefaultNamespace.vbs output
To set the default namespace for scripting, you perform the same scripting steps as in Listing 3 with one important change—well, two if you're counting lines of script. Rather than using the WMI scripting library's SWbemObject to read a property from an instance of a WMI managed resource, you use SWbemObject to set the property followed by calling SWbemObject's Put_ method to commit the change to the WMI managed resource. The set and commit operations are performed inside the For Each loop in Listing 4 because the SWbemServices InstancesOf method always returns an SWbemObjectSet collection, even when there's only one instance of the target WMI managed resource, as is the case with Win32_WMISetting.
Listing 4. Setting the default namespace for scripting using WMI and VBScript
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colWMISettings = objWMIService.InstancesOf("Win32_WMISetting")
For Each objWMISetting in colWMISettings
objWMISetting.ASPScriptDefaultNamespace = "root\cimv2"
objWMISetting.Put_
Next
Don't get too wrapped up in the mechanics of the WMI scripting library, because we cover the WMI scripting library in detail in Part 3 of this series. For now, let's turn our attention back to the CIM.
Listing Namespaces
Thus far, we've been using the same WMI scripting technique to retrieve instances of dynamic WMI managed resources. In Part 1, for example, we used the same script template to retrieve total physical memory, services, event log records, and processes. And in Part 2, we've retrieved services, operating system information, and the default namespace for scripting. As it turns out, you can use the same WMI scripting technique to retrieve namespace information from the CIM. As was the case in the managed resource scripts, the only change you need to make to the script is the target class name.
Namespace information is stored inside the CIM as static instances of the __NAMESPACE class. The __NAMESPACE class is an example of the static class type we briefly defined earlier. Unlike dynamic WMI managed resources that are retrieved on demand from a provider, static class instances are stored in the CIM and retrieved directly from the CIM without the use of a WMI provider. Listing 5 uses the __NAMESPACE class to retrieve and echo all of the namespaces directly beneath the root namespace.
Listing 5. Retrieving CIM namespaces using WMI and VBScript
strComputer = "."
Set objServices = GetObject("winmgmts:\\" & strComputer & "\root")
Set colNameSpaces = objServices.InstancesOf("__NAMESPACE")
For Each objNameSpace In colNameSpaces
WScript.Echo objNameSpace.Name
Next
Figure 3 shows the result of running Listing 5 on a Windows Server 2003 computer. The list of namespaces will vary based on the version of Windows and WMI installed on the target computer.
Figure 3. GetNamespaces.vbs output
As you may have noticed, Listing 5 does not provide a complete picture of all of the namespaces available on the target computer. It only retrieves and displays the namespaces beneath a single, specified namespace. To echo all of the namespaces on a local or remote WMI-enabled computer, you need to modify Listing 5 to recursively connect to and enumerate each namespace. Fortunately, this is not as difficult as you might think—especially when we do the work for you, as shown in Listing 6.
Changing Listing 5 into the recursive namespace script shown in Listing 6 primarily involves implementing the body of the original script inside a subroutine and providing a mechanism to call the subroutine for each namespace instance retrieved from the CIM. Listing 6 accomplishes this by performing the following steps:
Listing 6. Retrieving all CIM namespaces using WMI and VBScript
strComputer = "."
Call EnumNameSpaces("root")
Sub EnumNameSpaces(strNameSpace)
WScript.Echo strNameSpace
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\" & strNameSpace)
Set colNameSpaces = objWMIService.InstancesOf("__NAMESPACE")
For Each objNameSpace In colNameSpaces
Call EnumNameSpaces(strNameSpace & "\" & objNameSpace.Name)
Next
End Sub
Figure 4 shows the result of running Listing 6 on the same Windows Server 2003 computer.
Figure 4. GetAllNamespaces.vbs output
Class Categories Defined
As illustrated earlier in Figure 1, there are three general categories of classes used to construct the CIM: system, core and common, and extension.
System Classes
System classes are classes that support internal WMI configuration and operations, such as namespace configuration, namespace security, provider registration, and event subscriptions and notifications. When browsing the CIM, you can easily identify system classes by the two underscores prefacing each system class's name. For example, the __SystemClass, __Provider, and __Win32Provider classes shown in Figure 1 are system classes. The __NAMESPACE class we examined in the previous section is another example of a system class.
System classes are either abstract or static. Abstract system classes are templates used to derive (define) other abstract or static system classes. Static system classes define WMI configuration and operational data that's physically stored in the CIM repository. For example, the __Win32Provider system class defines provider registration information stored in the CIM. The CIMOM (WMI Service) uses the provider registration information stored in the CIM to map requests for dynamic managed resources to the appropriate provider.
As we demonstrated with the __NAMESPACE system class earlier, you can use the same WMI scripting technique to retrieve static instances of system classes stored in the CIM. Listing 7, for example, retrieves and displays all of the __Win32Provider instances registered in the root\cimv2 namespace.
Listing 7. Retrieving Win32 providers registered in the root\cimv2 namespace using WMI and VBScript
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colWin32Providers = objWMIService.InstancesOf("__Win32Provider")
For Each objWin32Provider In colWin32Providers
WScript.Echo objWin32Provider.Name
Next
Unless you're writing a book about WMI, it's unlikely you'll use system classes in your WMI scripts. The one exception is WMI monitoring scripts. WMI monitoring scripts are scripts that subscribe to WMI events. (Events are real-time notifications that something of interest has changed to a WMI managed resource.) We'll cover WMI event subscriptions and notifications in a future column.
Note For those of you that just can't wait, the three most common WMI __Event system classes are __InstanceCreationEvent, __InstanceModificationEvent, and __InstanceDeletionEvent. Although we're still not going to cover them here, you can find example monitoring scripts that demonstrate how to use these system classes in the Monitoring section of TechNet's Script Center.
Core and Common Classes
The core and common classes serve two roles. First and foremost, they represent the abstract classes from which system and application software developers, such as those at Microsoft, derive and create technology-specific extension classes. Second, they define resources common to particular management areas, but independent of a particular technology or implementation. The Distributed Management Task Force (DMTF) defines and maintains the set of core and common classes, which can be identified by the CIM_ prefix. The four classes prefaced with CIM_ in Figure 1 are core and common classes.
Of the approximately 275 core and common classes defined in the root\cimv2 namespace, all are abstract classes with a few exceptions. What does this mean to you? It means you'll rarely use core and common classes (classes prefaced with CIM_) in your WMI scripts. Why? Because you cannot retrieve instances of abstract classes; abstract classes can only be used as a base for new classes. Since 271 of the core and common classes are abstract, they are primarily used by software developers creating technology-specific extension classes.
So, what are the exceptions? Four of the 275 core and common classes are not abstract classes. They are dynamic classes that use the Win32 provider (cimwin32.dll) to retrieve instances of managed resources. For the record, the four dynamic classes are CIM_DataFile, CIM_DirectoryContainsFile, CIM_ProcessExecutable, and CIM_VideoControllerResolution.
Extension Classes
Extension classes are technology-specific classes created by system and application software developers. The Win32_BaseService, Win32_Service, Win32_SystemServices, and Win32_ComputerSystem classes shown in Figure 1 are Microsoft extension classes. Microsoft extension classes in the root\cimv2 namespace can be identified by the Win32_ prefix. That said, you should not conclude that all Microsoft extension class names begin with Win32_ because they do not. For example, the StdRegProv class defined in the root\DEFAULT namespace is not prefaced with Win32_ despite the fact that the StdRegProv class is Microsoft's extension class for registry management tasks. And before you ask: No, we do not know why the StdRegProv class is defined in the root\DEFAULT namespace rather than root\cimv2.
As we write this, there are approximately 463 Win32 extension classes defined in the root\cimv2 namespace. Of the 463 Win32 classes, 68 are abstract classes and the remaining 395 are dynamic. What does this mean to you? It means extension classes are the primary category of classes you'll use in your WMI scripts.
Note The class statistics we're providing are based on a beta version of Windows Server 2003 and are only intended to illustrate general CIM concepts. Your numbers will be different based on several factors, such as Windows version, WMI version, and installed software.
Listing Classes
At this point, what we're about to tell you shouldn't come as a big surprise. You can write a script to retrieve all of the classes defined within a namespace. Listing 8, for example, lists all classes defined in the root\cimv2 namespace. However, unlike all of the previous scripts that used the SWbemServices InstancesOf method, Listing 8 uses a different method, SubclassesOf, which is also provided by the WMI scripting library's SWbemServices object.
As the method's name suggests, SubclassesOf returns all of the child (or sub-) classes of a specified parent (super-) class, or namespace when no parent class is provided. Like InstancesOf, SubclassesOf returns all of the subclasses inside a SWbemObjectSet collection, where each item in the collection is a SWbemObject representing a single class.
Another important difference in Listing 8 is the objClass.Path_.Path property echoed in the body of For Each loop. Let's examine the For Each loop to understand exactly what this is. The For Each loop is enumerating each SWbemObject (objClass) in the SWbemObjectSet (colClasses) collection returned by the SWbemServices SubclassesOf method. Each SWbemObject represents a discrete class in the root\cimv2 namespace.
Here's the potentially confusing part. Unlike all of our previous scripts that displayed properties defined by a managed resource's blueprint (class definition), Path_ is a property provided by the WMI scripting library's SWbemObject. To understand this, you have to think about the context in which you're using SWbemObject. Are you using SWbemObject to access an instance of a managed resource? Or are you using SWbemObject to access the managed resource's class definition?
When you use SWbemObject to access an instance of a managed resource, you're more likely to use SWbemObject to access properties and methods defined by the managed resource's blueprint (class definition). When you use SWbemObject to get detailed class information, such as supported properties, methods, and qualifiers, you use properties and methods provided by SWbemObject itself. Path_ is one such property.
Path_ actually references another WMI scripting library object named SWbemObjectPath, which provides the Path property. The SWbemObjectPath Path property contains the fully qualified path to the class referenced by SWbemObject (objClass in Listing 8). Again, don't get too wrapped up in the scripting objects now because we'll cover them in detail in Part 3.
Listing 8. Retrieving all classes defined in the root\cimv2 namespace using WMI and VBScript
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colClasses = objWMIService.SubclassesOf()
For Each objClass In colClasses
WScript.Echo objClass.Path_.Path
Next
Running Listing 8 on our Windows Server 2003 computer displays a long list of 914 classes, a portion of which are shown in Figure 5.
Figure 5. GetClasses.vbs output
Of course, you can easily modify Listing 8 to list classes in other namespaces by simply changing the script's target namespace. You can also use Listing 8 in combination with the findstr.exe command to search for classes. For those of you unfamiliar with the findstr.exe command, findstr.exe is a command-line tool that searches for strings in files.
Suppose, for example, you need to know if the new Windows XP and Windows Server 2003 Win32_TSSessionSetting class is supported on the version of Windows you're running. You could use the following command to determine if the Win32_TSSessionSetting class exists in the root\cimv2 namespace.
C:\Scripts> cscript GetClasses.vbs |findstr /I "win32_tssessionsetting"
Here are few additional scenarios to try.
CIM Class Types Defined
It should be obvious at this point that classes are the basic building blocks in the CIM repository. WMI configuration information and WMI managed resources are defined by one or more classes. Similar to the Active Directory schema, CIM classes are organized hierarchically where child classes inherit properties, methods, and qualifiers from parent classes (don't worry about properties, methods, and qualifiers for the moment; we'll get to those in the next section). For example, the Win32_Service dynamic class is inherited from the Win32_BaseService abstract class, which is inherited from the CIM_Service abstract class, which is inherited from the CIM_LogicalElement abstract class, which is inherited from the CIM_ManagedSystemElement abstract class, as illustrated in Figure 1. It is the sum of the classes in a managed resource's class hierarchy that ultimately defines a managed resource.
As mentioned earlier, there are three primary class types: abstract, static, and dynamic.
Abstract Classes
An abstract class is a template used to define new classes.(抽象类是用于定义新类的模板) Like abstract classes in the Active Directory schema, CIM abstract classes serve as base classes for other abstract, static, and dynamic classes. Every—well, almost every—WMI managed resource class definition is built (or derived) from one or more abstract classes.
You can identify an abstract class by examining the class's Abstract qualifier. An abstract class must define the Abstract qualifier and set the Abstract qualifier's value to true. Supplementary Listing A at the end of this article demonstrates how to use the WMI scripting library to list all abstract classes defined in the root\cimv2 namespace.
The most common use of the abstract class type is in the definition of core and common classes. Abstract classes are rarely used in WMI scripts because you cannot retrieve instances of abstract classes.(因为在抽象类中不能获取实例,所以你就会很少使用它来写WMI脚本)
Static Classes
A static class defines data that's physically stored in the CIM repository.Static classes have instances just like dynamic classes; however, instances of static classes are stored in the CIM repository. Likewise, static class instances are retrieved directly from the CIM. They do not use a provider.
(静态类定义在CIM容器中实际存储的数据,而且和动态类一样有实例;但是静态类的实例存储在CIM容器中,是直接从CIM中获取的,不需要一个Provider)
You can identify a static class by examining the class's qualifiers. However, unlike abstract and dynamic class types that are identified by the presence of a specific qualifier, static classes are identified by the absence of the Abstract and Dynamic qualifiers.
The most common use of the static class type is in the definition of system classes. Static classes are rarely used in WMI scripts.(静态类大多用于系统类的定义,在脚本写作中很少用到)
Dynamic Classes
A dynamic class is a class that models a WMI managed resource that's dynamically retrieved from a provider.
You can identify a dynamic class by examining the class's Dynamic qualifier. A dynamic class must define the Dynamic qualifier and set the Dynamic qualifier's value to true. Supplementary Listing B at the end of this article demonstrates how to use the WMI scripting library to list all dynamic classes defined in the root\cimv2 namespace.
The most common use of the dynamic class type is in the definition of extension classes. Dynamic classes are the most common types of classes used in WMI scripts.(动态类最多用于扩展类的定义,是在WMI脚本中使用的最多的类别)
Association Classes
A fourth class type, known as an association class, is also supported. An association class is an abstract, static, or dynamic class that describes a relationship between two classes or managed resources. The Win32_SystemServices class, shown in Figure 1, is an example of a dynamic association class that describes the relationship between a computer and the services running on the computer.(复合类是第四种类,它可以是抽象的、静态的或动态的;用于描述两个类或托管资源之间的关系,Win32_systemservices类就是动态复合类的一个,它是用来描述计算机和运行在此机器上的服务的关系的)
You can identify an association class by examining the class's Association qualifier. An abstract, static, or dynamic association class must define the Association qualifier and set the Association qualifier's value to true.
Dissecting解剖;切开 a Class(对一个类的分析)
At the risk of sounding like a broken record, every hardware and software resource that's manageable through WMI is defined by a class. A class is a blueprint (or template) for a discrete WMI managed resource, and all instances of the resource use the blueprint. Classes represent the things computers have. And because computers have things like disks, event logs, files, folders, memory, printers, processes, processors, services, and so on, WMI has classes for disks, event logs, files, folders, memory, printers, processes, processors, services, and so on. Although there are exceptions (like __Event abstract system classes), most classes that are used in scripting can be directly tied to real, live things.
These so-called blueprints consist of properties, methods, and qualifiers. Before we examine properties, methods, and qualifiers, let's briefly discuss where managed-resource class definitions originate.(这些蓝图由属性,)
Suppose Microsoft decides to create a new WMI provider that system administrators can use to manage and monitor Microsoft DNS servers. At a minimum, the DNS provider development team would need to create two files: a provider and something called a Managed Object Format (MOF) file.
The provider is the dynamic link library that acts as the intermediary between the WMI infrastructure and the underlying managed resource—the Microsoft DNS server in this case. The provider services WMI requests by calling the managed resource's native APIs.
The MOF file contains the class definitions that describe the capabilities provided by the DNS provider. The MOF file describes the DNS provider's capabilities using classes that model resources commonly associated with a DNS server (zone files and resource records, for example). Each class defined in the DNS MOF file defines the data (properties) associated with a specific DNS-related resource and the actions (methods) you can perform on the resource.
When the DNS provider is installed, the DNS provider dynamic link library is registered with the operating system and WMI, and the DNS MOF file undergoes a compilation process, which loads the DNS provider's class definitions into the CIM repository. At this point, the DNS provider can be used by any WMI-enabled consumer, including scripts.
While our story is true—Microsoft developed a new DNS provider for Windows Server 2003—the important take away is that managed-resource class definitions originate in MOF files. MOF files are to WMI what MIB files are to SNMP.
MOF files are text files based on the MOF language created and maintained by the Distributed Management Task Force (DMTF). Every managed resource's class definition follows a well-defined structure and syntax as illustrated in Figure 6.
Figure 6. Structure of a managed-resource class definition
As shown in Figure 6, every managed-resource class definition consists of properties, methods, and qualifiers.
Properties
Properties are nouns that describe a managed resource. Classes use properties to describe things like the identity, configuration, and state of a managed resource. Services, for example, have a name, display name, description, startup type, and status. The Win32_Service class has the same things.
Each property has a name, type, and optional property qualifiers. You use the property's name in combination with the WMI scripting library's SWbemObject to access a managed resource's property, as demonstrated way back in Listing 1.
Methods
Methods are verbs that perform an action on a managed resource. What can you do with services? Well, you can start them, stop them, pause them, and resume them. Turns out there are methods that allow you to start, stop, pause, and/or resume services. Nothing magical at all.
Each method has a name, return type, optional parameters, and optional method qualifiers. Like properties, you use the method's name in combination with the WMI scripting library's SWbemObject to call a method.
Not all classes define methods.
Qualifiers
Qualifiers are adjectives that provide additional information about the class, property, or method to which they apply. For example, the question, "What type of class is Win32_Service?" is answered by the class's Dynamic qualifier. As you begin to write WMI scripts that do more than simply retrieve information, such as modify properties or call methods, qualifiers become increasingly important because they define the operational characteristics of the property you're updating or the method you're calling. So what kind of information do qualifiers provide?
Class Qualifiers
Class qualifiers provide operational information about a class. For example:
Property Qualifiers
Property qualifiers provide information about each property. For example:
Method Qualifiers
Method qualifiers provide information about each method. For example:
Note There are many more qualifiers than those mentioned here. For the complete list, see the WMI Qualifiers topic in the WMI SDK.
You can examine a class's properties, methods, and qualifiers using the WMI Tester (wbemtest.exe) tool, as shown in Figure 7. Of course, you can also use the WMI scripting library to retrieve the same information, as you'll see shortly.
Figure 7. Viewing the Win32_Service class using WMI Tester (wbemtest.exe)
Comparing Classes to Managed Resources
Most WMI properties and methods are reasonably well named. For example, if you compare the properties and methods defined by the Win32_Service class to the Services' Properties dialog, as shown in Figure 8, it's not too hard to figure out what Win32_Service.Name, Win32_Service.DisplayName, or Win32_Service.Descritpion might contain.
Figure 8. Services properties dialog and Win32_Service class properties and methods
So why do we care about all this stuff? Well, classes determine what you can and cannot do with WMI. If you have a class for services, you can manage services; if you don't, you can't. Properties and methods are important because the versions of WMI differ between operating systems. The Win32_ComputerSystem class in Windows XP has many new properties and methods not in the Win32_ComputerSystem class in Windows 2000. You have to know the WMI details, too, because unlike ADSI, the WMI properties and methods must be available on the target computer in order to get things to work.
How do you determine if a property or method is supported on a remote Windows computer? You examine the class definition.
Retrieving Class Definitions
Like everything in WMI, there are a gazillion ways to retrieve a managed resource's class definition. OK, so maybe we're exaggerating, but suffice to say, there are so many ways that there's a solution for every user interface preference. If you like to grep text files, slice and dice the MOF file. If you prefer the command line, use the WMI Command-line tool, wmic.exe (Windows XP only). If you enjoy spending time in graphical tools, use WMI Tester (wbemtest.exe) or CIM Studio. Or if you're like us, reach for the WMI scripting library.
You can retrieve managed-resource class definitions three different ways using the WMI scripting library.
Let's briefly look at each scripting solution and then call it a day.
Using SWbemObject Properties_, Methods_, and Qualifiers_
Listings 9, 10, and 11 demonstrate how to use the Properties_, Methods_, and Qualifiers_ properties of the WMI scripting library's SWbemObject to retrieve information about the Win32_Service class. We'll examine Listing 9 and then point out the differences in Listings 10 and 11 since all three scripts employ the same basic approach.
Listing 9 begins by initializing three variables: strComputer, strNameSpace, and strClass. The value assigned to strComputer is the target WMI-enabled computer. The value assigned to strNameSpace is the namespace to connect to. And the value assigned to strClass is the name of the class within the target namespace whose properties are going to be retrieved and displayed. Separating the three values into multiple variables makes it easy to reuse the script for other computers, namespaces, and classes. In fact, you could easily turn Listing 9 into a command-line script using the Windows Script Host (WSH) Arguments collection.
Next, the script uses VBScript's GetObject function to connect to the WMI service on the target computer. Notice anything different about the connection string passed to GetObject? In addition to specifying the target namespace, the class name is also specified, which has a profound impact on what GetObject and the WMI scripting library return. Rather than returning a reference to a SWbemServices object, as was the case in all of our previous scripts, GetObject returns a reference to a SWbemObject representing the target class. Why? The answer lies in something called an object path. Although we cover object paths in detail in Part 3, we'll give you a quick explanation here to help you understand what's going on in Listings 9 through 11 (and Supplementary Listing C).
Every WMI class and every instance of a WMI managed resource has an object path. Think of an object path as the WMI version of a file's fully qualified path. Every file has a fully qualified path that consists of a device name, followed by zero or more directory names, followed by the file name. Likewise, every class and managed resource has an object path that consists of the WMI-enabled computer name, followed by the CIM namespace, followed by the managed resource's class name, followed by the class's key property and the key property's value, as shown below. (Note the square brackets only serve to delimit the four permissible parts of an object path; they are not part of the object path.)
[\\ComputerName][\Namespace][:ClassName][.KeyProperty='Value']
When you use all or part of an object path in the connection string passed to GetObject (which, by the way, we've been doing all along), the object path you use determines the type of reference returned by GetObject and the WMI scripting library. For example, if you only include the computer name part of an object path, you get back a SWbemServices object reference connected to the default namespace. If you include the computer name and/or namespace, you also get a reference to a SWbemServices object. If you include the computer name, namespace, and class name, you get back a reference to a SWbemObject representing the class. And if you include all four parts, you get back a SWbemObject representing the managed resource instance identified by the class, key, and value. Again, we'll cover object paths in more detail in Part 3 of this series. For now, understand objClass in Listing 9 is a reference to a SWbemObject representing the Win32_Service class.
The remainder of the script is reasonably straightforward. After echoing a simple header identifying the class name whose properties are going to be displayed, the script use's the SWbemObject reference (objClass) returned from GetObject to access the SWbemObject Properties_ property (objClass.Properties_). The SWbemObject Properties_ property references a SWbemPropertySet, which is the collection of properties for the class. Each property in the SWbemPropertySet collection is a SWbemProperty (objClassProperty) object, which we use to read and echo each property's name.
So to summarize, the For Each loop enumerates the class's SWbemPropertySet collection (by way of the SWbemObject Properties_ property) and echoes the Name property for each SWbemProperty in the SWbemPropertySet collection.
Listing 9. Using SWbemObject properties_ to retrieve Win32_Service properties
strComputer = "."
strNameSpace = "root\cimv2"
strClass = "Win32_Service"
Set objClass = GetObject("winmgmts:\\" & strComputer & _
"\" & strNameSpace & ":" & strClass)
WScript.Echo strClass & " Class Properties"
WScript.Echo "------------------------------"
For Each objClassProperty In objClass.Properties_
WScript.Echo objClassProperty.Name
Next
Figure 9 displays the names of the 25 properties defined (or inherited) by the Win32_Service class.
Figure 9. GetProperties.vbs output
Listing 10 is identical to Listing 9 with one primary exception. The For Each loop enumerates the class's SWbemMethodSet collection (by way of the SWbemObject Methods_ property) and echoes the Name property for each SWbemMethod (objClassMethod) in the SWbemMethodSet collection.
Listing 10. Using SWbemObject methods_ to retrieve Win32_Service methods
strComputer = "."
strNameSpace = "root\cimv2"
strClass = "Win32_Service"
Set objClass = GetObject("winmgmts:\\" & strComputer & _
"\" & strNameSpace & ":" & strClass)
WScript.Echo strClass & " Class Methods"
WScript.Echo "---------------------------"
For Each objClassMethod In objClass.Methods_
WScript.Echo objClassMethod.Name
Next
Figure 10 displays the names of the 10 methods defined (or inherited) by the Win32_Service class.
Figure 10. GetMethods.vbs output
Listing 11 is identical to Listings 9 and 10 with three exceptions.
Listing 11. Using SWbemObject qualifiers_ to retrieve Win32_Service class qualifiers
strComputer = "."
strNameSpace = "root\cimv2"
strClass = "Win32_Service"
Set objClass = GetObject("winmgmts:\\" & strComputer & _
"\" & strNameSpace & ":" & strClass)
WScript.Echo strClass & " Class Qualifiers"
WScript.Echo "------------------------------"
For Each objClassQualifier In objClass.Qualifiers_
If VarType(objClassQualifier.Value) = (vbVariant + vbArray) Then
strQualifier = objClassQualifier.Name & " = " & _
Join(objClassQualifier.Value, ",")
Else
strQualifier = objClassQualifier.Name & " = " & _
objClassQualifier.Value
End If
WScript.Echo strQualifier
strQualifier = ""
Next
Figure 11 displays the names and values of the 5 class qualifiers defined (or inherited) by the Win32_Service class.
Figure 11. GetClassQualifiers.vbs output
As you might have noticed, Listings 9 and 10 fail to show you the property and method qualifiers. To be honest, it was intentional to keep the scripts to a size that could be easily explained. The good news is we've included the complete class qualifier, property, property qualifier, method, and method qualifier script at the end of the column (see Supplementary Listing C). You're welcome.
And in case it's not obvious, you could combine Listings 9, 10, and 11 with Listing 6 (the GetAllNamespaces.vbs script) and Listing 8 (the GetClasses.vbs script) to retrieve the properties, methods, and qualifiers for every class defined in the CIM. Use your resulting script with the findstr.exe command, and you've got a solution to search for any class, property, method, or qualifier defined in the CIM.
Using SWbemObject GetObjectText_
Earlier we said that you can retrieve managed-resource class definitions directly from the MOF file in which the class is defined. And you can. For example, if you want to look up the Win32_Service class, look in the %SystemRoot%\system32\wbem\cimwin32.mof file. However, using MOF files directly comes with a price. You must examine every class in a managed resource's class hierarchy to obtain the complete blueprint for the managed resource.
Say, for example, you did want to lookup Win32_Service. You would have to examine all five classes in the Win32_Service class hierarchy, as illustrated in Figure 1, to get the complete picture. The same is true if you use the WMI Tester's (wbemtest.exe) Show MOF button (see Figure 7). An easier approach to obtaining the MOF representation of a class is to use the WMI scripting library's SWbemObject GetObjectText_ method, as demonstrated in Listing 12.
Unlike Listings 9 through 11, Listing 12 uses the SWbemServices Get method to retrieve the class rather than GetObject. The SWbemServices Get method must be used so the wbemFlagUseAmendedQuailifiers flag can be enabled. Enabling the wbemFlagUseAmendedQuailifiers flag tells WMI to return the entire managed resource blueprint (class definition) rather than just the local definition.
Using the wbemFlagUseAmendedQualifiers flag has a second benefit as well. You also get back the class description, and descriptions for each of the class's properties, methods, and qualifiers. Class, property, method, and qualifier descriptions are commonly defined in a separate MOF file for localization purposes. For example, the language-neutral portion of the Win32_Service class is defined in cimwin32.mof. The language-specific portion of the Win32_Service class, which includes description information, is defined in cimwin32.mfl. Language-specific (or localized) MOF files commonly have a .mfl extension rather than .mof.
The SWbemServices Get method returns a reference to a SWbemObject (objClass) representing the target class, which is used to call the SWbemObject GetObjectText_ method. The GetObjectText_ method returns the MOF representation for the class. Had we used GetObjectText_ without enabling the wbemFlagUseAmendedQuailifiers flag, the method would have only returned those properties, methods, and qualifiers defined by Win32_Service; inherited properties and methods would have been omitted.
Listing 12. Using SWbemObject GetObjectText_ to retrieve the MOF representation of the Win32_Service class
strComputer = "."
strNameSpace = "root\cimv2"
strClass = "Win32_Service"
Const wbemFlagUseAmendedQualifiers = &h20000
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\" & strNameSpace)
Set objClass = objWMIService.Get(strClass, wbemFlagUseAmendedQualifiers)
strMOF = objClass.GetObjectText_
WScript.Echo strMOF
There is a caveat to using GetObjectText_, however: there's no information about inherited qualifiers included in the MOF syntax returned by the method. This could present a problem if you wanted to use GetObjectText_ to determine a class's Key property when the Key qualifier is defined on a property in a parent class.
Using SWbemObjectEx GetText_
Windows XP and Windows Server 2003 both include a new method named GetText_ that can be used to retrieve the XML representation of a managed-resource class definition.
Using GetText_ is similar to GetObjectText_ with one noticeable exception: the three parameters passed to the GetText_ method.
The first parameter is required and identifies the resulting XML format. It can currently be one of two values as defined by the WMI WbemObjectTextFormatEnum: wbemObjectTextFormatCIMDTD20 (value: 1) or wbemObjectTextFormatWMIDTD20 (value: 2). A value of 2 (wbemObjectTextFormatWMIDTD20) tells GetText_ to format the resulting XML according to the extended WMI version of the Distributed Management Task Force CIM document type definition (DTD) Version 2.0.
The second parameter is optional and is currently reserved for operation flags. It should be set to 0 (zero).
The third parameter (also optional), colNamedValueSet, is a SWbemNamedValueSet collection that provides special instructions to GetText_. Here we're telling GetText_ to:
Listing 13. Using SWbemObjectEx GetText_ to retrieve the XML representation of the Win32_Service class (Windows XP and Windows .NET only)
strComputer = "."
strNameSpace = "root\cimv2"
strClass = "Win32_Service"
Const wbemFlagUseAmendedQualifiers = &h20000
Const wbemObjectTextFormatWMIDTD20 = 2
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\" & strNameSpace)
Set objClass = objWMIService.Get(strClass, wbemFlagUseAmendedQualifiers)
Set colNamedValueSet = CreateObject("Wbemscripting.SWbemNamedValueSet")
colNamedValueSet.Add "LocalOnly", False
colNamedValueSet.Add "IncludeQualifiers", True
colNamedValueSet.Add "ExcludeSystemProperties", False
colNamedValueSet.Add "IncludeClassOrigin", True
strXML = objClass.GetText_(wbemObjectTextFormatWMIDTD20, 0, colNamedValueSet)
WScript.Echo strXML
To successfully run Listing 13, copy and paste the script into your favorite text editor, save the script with a .vbs extension (for example, GetXML.vbs), and run the script using the command line shown below.
C:\Scripts> cscript //nologo GetXML.vbs >Win32_Service.xml
Figure 12 displays the resulting XML file, Win32_Service.xml, using Microsoft® Internet Explorer.
Figure 12. GetXML.vbs output
All for Now
If there's anything confusing, difficult, or overwhelming about WMI, it's getting a handle on the mountain of data exposed through WMI. We'd like to think we've equipped you with the knowledge and tools necessary to effectively find and interpret WMI managed resource class definitions. Of course, you're the best judge of that, so by all means let us know if and where we've fallen short. With the boring stuff out of the way, be prepared to have some fun in Part 3 when we dive into the details of the WMI scripting library. Oh, and by all means, feel free to take the rest of the day off.
Wait! One final note before you pack-up and head home: The WMI Development Team asked us to let you know that the WMI Tools, formerly only available with the WMI SDK, have been updated. The updated tools (which include WMI CIM Studio, WMI Event Registration, WMI Event Viewer, and the WMI Object Browser), are now compatible with Windows XP and Windows Server 2003, as well as with Windows 2000. In addition, the updated tools do not include the WMI SDK, which means you can now install the tools without installing the entire WMI SDK. Is that cool or what?
Supplementary Scripts
Listing A. Listing abstract class types defined in the root\cimv2 namespace
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colClasses = objWMIService.SubclassesOf()
For Each objClass in colClasses
For Each objClassQualifier In objClass.Qualifiers_
If LCase(objClassQualifier.Name) = "abstract" Then
WScript.Echo objClass.Path_.Class & ": " & _
objClassQualifier.Name & "=" & _
objClassQualifier.Value
End If
Next
Next
Listing B. Listing dynamic class types defined in the root\cimv2 namespace
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colClasses = objWMIService.SubclassesOf()
For Each objClass in colClasses
For Each objClassQualifier In objClass.Qualifiers_
If LCase(objClassQualifier.Name) = "dynamic" Then
WScript.Echo objClass.Path_.Class & ": " & _
objClassQualifier.Name & "=" & _
objClassQualifier.Value
End If
Next
Next
Listing C. Using SWbemObject Qualifiers_, Properties_, and Methods_ to retrieve class qualifiers, properties, property qualifiers, methods, and method qualifiers
strComputer = "."
strNameSpace = "root\cimv2"
strClass = "Win32_Service"
Set objClass = GetObject("winmgmts:\\" & strComputer & _
"\" & strNameSpace & ":" & strClass)
WScript.Echo strClass & " Class Qualifiers"
WScript.Echo "------------------------------"
i = 1
For Each objClassQualifier In objClass.Qualifiers_
If VarType(objClassQualifier.Value) = (vbVariant + vbArray) Then
strQualifier = i & ". " & objClassQualifier.Name & " = " & _
Join(objClassQualifier.Value, ",")
Else
strQualifier = i & ". " & objClassQualifier.Name & " = " & _
objClassQualifier.Value
End If
WScript.Echo strQualifier
strQualifier = ""
i = i + 1
Next
WScript.Echo
WScript.Echo strClass & " Class Properties and Property Qualifiers"
WScript.Echo "------------------------------------------------------"
i = 1 : j = 1
For Each objClassProperty In objClass.Properties_
WScript.Echo i & ". " & objClassProperty.Name
For Each objPropertyQualifier In objClassProperty.Qualifiers_
If VarType(objPropertyQualifier.Value) = (vbVariant + vbArray) Then
strQualifier = i & "." & j & ". " & _
objPropertyQualifier.Name & " = " & _
Join(objPropertyQualifier.Value, ",")
Else
strQualifier = i & "." & j & ". " & _
objPropertyQualifier.Name & " = " & _
objPropertyQualifier.Value
End If
WScript.Echo strQualifier
strQualifier = ""
j = j + 1
Next
WScript.Echo
i = i + 1 : j = 1
Next
WScript.Echo
WScript.Echo strClass & " Class Methods and Method Qualifiers"
WScript.Echo "-------------------------------------------------"
i = 1 : j = 1
For Each objClassMethod In objClass.Methods_
WScript.Echo i & ". " & objClassMethod.Name
For Each objMethodQualifier In objClassMethod.Qualifiers_
If VarType(objMethodQualifier.Value) = (vbVariant + vbArray) Then
strQualifier = i & "." & j & ". " & _
objMethodQualifier.Name & " = " & _
Join(objMethodQualifier.Value, ",")
Else
strQualifier = i & "." & j & ". " & _
objMethodQualifier.Name & " = " & _
objMethodQualifier.Value
End If
WScript.Echo strQualifier
strQualifier = ""
j = j + 1
Next
WScript.Echo
i = i + 1 : j = 1
Next
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, 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.