WMI 脚本入门:第三部分
Greg Stemp、Dean Tsaltas 和 Bob Wells
Microsoft Corporation
Ethan Wilansky
网络设计小组
摘要:定义 WMI 脚本库并展示如何使用它来访问和管理 WMI 托管资源。通览 7 个可以用 WMI 脚本库创建的基本脚本类型,它们用于如创建、删除、检索托管资源实例等任务。
![*](http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/3squares.gif)
脚本编写的同仁们大家好!已经有好一段时间没见了。与其用借口让您厌烦 — 比如我们正在完成 Microsoft Windows 2000 脚本指南(以后会有更多关于它的内容) — 不如让我们就此开始,好么?我们将从 WMI 脚本入门系列暂停的内容开始,将您的注意力转向 WMI 脚本之谜剩下的部分 — WMI 脚本库。
在深入本节内容之前,让我们简要地回顾一下到目前为止我们已经讨论过的内容。在 WMI 脚本入门:第一部分中,我们讨论了 WMI 的结构和主要组件,因为它们与 WMI 脚本相关。在 WMI 脚本入门:第二部分中,我们讨论了公共信息模型(Common Information Model,CIM),它是存放 WMI 托管资源蓝图(类定义)的知识库。虽然我们知道许多人都跳过了第二部分(基于“Rate this page”的点击量),我们还是假定您已经了解了这部分内容。如果不知道,我想,您知道该到哪里去找到它。
本页内容
![]() |
WMI 脚本库定义 |
![]() |
解释 WMI 脚本库对象模型 |
![]() |
Scripting Guys 的 WMI 脚本模板指南 |
![]() |
检索托管资源实例 |
![]() |
显示托管资源属性 |
![]() |
修改托管资源的属性 |
![]() |
调用托管资源方法 |
![]() |
创建托管资源的一个新实例 |
![]() |
删除托管资源实例 |
![]() |
订阅事件 |
![]() |
就到这里吧 |
WMI 脚本库定义
那么,到底什么是 WMI 脚本库呢?让我们用一个比喻来回答这个问题。回想一下您计算机上的立体声系统或媒体播放器。所有立体声系统的共同点是什么?对,它们都有音量控制、高音和低音控制,对收音机来说就是调谐器,可能还有均衡器。无所谓您是否选择听贝多芬、齐柏林飞船乐队、Art of Noise,或者无论是谁的音乐,控制器总是同样工作。
WMI 脚本库就像(不是真正像,只是将就我们)您立体声系统上的控制器。就是说,WMI 脚本库提供了一套一致的控制器(以自动化对象的形式),它们允许您访问并管理 WMI 托管资源。无论您是正在管理计算机、事件日志、操作系统、进程、服务或选择您所喜爱的资源都无所谓,WMI 脚本库中的对象总是同样工作。
WMI 脚本库中的自动化对象提供的一致性,通过您使用 WMI 脚本库可以执行的有限任务最佳地传达出来。总的来说,您可以使用 WMI 脚本库创建七个基本脚本类型。
1. |
您可以检索 WMI 托管资源实例。 |
2. |
您可以读取 WMI 托管资源属性。 |
3. |
您可以修改 WMI 托管资源属性。 |
4. |
您可以调用 WMI 托管资源的方法。 |
5. |
您可以创建 WMI 托管资源的新实例。 |
6. |
您可以删除 WMI 托管资源的实例。 |
7. |
您可以预定事件来监视 WMI 托管资源的创建、修改和/或删除。 |
您可以将 7 个基本脚本类型当作脚本模板。就像音量控制器调节任何 CD、盒式磁带、8 轨磁带或 .wma 文件的音量一样,WMI 脚本模板可以用于管理任何 WMI 托管资源。一旦对模板的了解充分到足以管理一类 WMI 托管资源之后,您就可以轻易地改写相同的模板来管理成百个其他的 WMI 托管资源了。
WMI 脚本库对象模型
既然我们已经确定 WMI 脚本库是整个 WMI 基础结构的控制面板,那么就让我们打开它并一探究竟。在本系列第一部分的图 1 中,为您展示了 WMI 脚本库的实现位置是在物理驻留在 %SystemRoot%\system32\wbem 目录中的名为 wbemdisp.dll 的单一自动化组件中。
总的来说,WMI 脚本库是 24 个自动化对象(在 Windows 2000 及更早版本中为 19 个)组成,其中的 21 个在 图 1 所示的 WMI 脚本库对象模型关系图中进行了说明。现在,在您认为必须了解所有 24 个对象的、多得可怕的详细信息,感觉要崩溃之前,让我们礼貌的指出,您不需要那样做。实际上,您会非常高兴知道,仅需对图 1 中所展示的两到三个对象有基本理解,您就可以创建前面列出的 7 个脚本模板中的 6 个。那些对象是什么呢?稍安毋躁,您得停停,等我们说完。
除了在 wbemdisp.dll 的 Windows XP 和 Windows Server 2003 版中的 24 个自动化对象外,脚本库还包含 13 个枚举。枚举只是一组相关常数的别名。我们不会在这里讨论这组常数,因为它们在 WMI SDK 中讲得非常清楚了。要了解更多关于 WMI 脚本常数的信息,参阅 WMI SDK 中的脚本 API 常数。
在许多方面,您可以将 WMI 脚本库中的自动化对象比做由 ADSI 提供的核心接口。我们这么说是什么意思呢?是这样,ADSI 核心接口(例如,IADs 和 IADsContainer)为 Active Directory 中的脚本对象提供了一个一致的方法 — 不论对象的类和属性。同样地,WMI 脚本库中的自动化对象为 WMI 托管资源提供了一个一致并且统一的脚本模型。
理解 WMI 脚本库 (wbemdisp.dll) 中的自动化对象与驻留在 CIM 知识库 (objects.data) 中的托管资源类定义之间的关系很重要。正如我们在第二部分中解释的,托管资源类定义是通过 WMI 公开的计算机资源的蓝图。除了定义可以托管的资源之外,蓝图还为每个托管资源定义了唯一的方法和属性。
另一方面,WMI 脚本库提供了通常意义上的自动化对象脚本集,用于验证并连接到 WMI,以及随后访问 WMI 托管资源的实例。一旦使用 WMI 脚本库获得了 WMI 托管资源的实例后,您就可以访问由托管资源的类定义定义的方法和属性 — 就好像这些方法和属性就是脚本库自己的一部分一样。
![scripting01142003_fig1thumb](http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/art/scripting01142003_fig1thumb.gif)
图 1. WMI 脚本库对象模型 wbemdisp.dll
解释 WMI 脚本库对象模型
虽然图 1 初看上去不是很直观,但 WMI 脚本库对象模型对 WMI 脚本如何工作的机制提供了大量的深入探察。图 1 中的线指向通过调用原始对象的方法(或访问属性)获得的对象。例如,调用 SWbemLocator ConnectServer 方法,返回一个 SWbemServices 对象。调用 SWbemServicesExecNotificationQuery 方法,返回 SWbemEventSource 对象。另一方面,调用 SWbemServicesExecQuery 或 InstancesOf 方法,返回 SWbemObjectSet 集合。调用 SWbemServicesGet 方法,返回 SWbemObject。
让我们将出现在本系列第一部分和第二部分的 WMI 脚本与对象模型比较一下,来看看它们是如何工作的。通常对于多数 WMI 脚本而言,每个脚本执行三个基本步骤。
1. |
每个脚本都由连接到在目标计算机上的 WMI 服务开始。该脚本将 Microsoft Visual Basic Scripting Edition (VBScript) 的 GetObject 函数与 WMI 连接字符串结合起使用,连接字符串是由 WMI 名字对象“winmgmts:”,后跟到目标计算机和命名空间的 WMI 对象路径组成。 strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") 用这种方法连接到 WMI 返回一个对图 1 中所示的 SWbemServices 对象的引用。一旦获得了一个对 SWbemServices 对象的引用,您就可以调用 SwbemServices 的方法之一了。调用的 SWbemServices 方法主要取决于您创建的 WMI 脚本的类型。 |
2. |
下一步,使用 SWbemServicesInstancesOf 方法,每个脚本检索一个 WMI 托管资源实例。 Set colSWbemObjectSet = objSWbemServices.InstancesOf("Win32_Service") InstancesOf总是 返回一个 SWbemObjectSet 集合。如图 1 中 SWbemServices 和 SWbemObjectSet 对象之间的连线所示,SWbemObjectSet 是 SWbemServices 可以返回的三个 WMI 脚本库对象类型之一。 |
3. |
最后,通过枚举 SWbemObjectSet 中的实例,每个脚本访问了一个 WMI 托管资源的属性。 For Each objSWbemObject In colSWbemObjectSet WScript.Echo "Name: " & objSWbemObject.Name Next 如图 1 所示,SWbemObjectSet 集合中的每个托管资源实例都由一个 SWbemObject 表示。 |
在 WMI 脚本库中的 24 个自动化对象中,有三个最重要的 — 也就是说,您应该最先学习的对象 — SWbemServices、SWbemObjectSet 和 SWbemObject。为什么?因为 SWbemServices、SWbemObjectSet 和 SWbemObject 是几乎每个 WMI 脚本都必不可少的修补部分。实际上,如果我们重新回顾 ADSI 类比,SWbemServices、SWbemObjectSet 和 SWbemObject 是对应 WMI 脚本,IADs 和 IADsContainer 是对应 ADSI 脚本。(Scripting Guys 免费放送:对于还未涉足 ADSI 脚本编写的人来说,这里有个技巧 — 那就是,在 ADSI 库提供的 6 个接口中,IADs 和 IADsContainer 是您应该首先学习的。相信我们。)
SWbemServices 是一个对象,它表示到在本地或远程计算机上的 WMI 命名空间的一个已验证连接。另外,SWBemServices 在每个 WMI 脚本中都扮演重要的角色。例如,您使用 SWbemServicesInstancesOf 方法来检索托管资源的所有实例。同样,您将 SWbemServicesExecQuery 方法与一个 WQL(WMI 查询语言)查询结合使用来检索托管资源的所有或一个子集的实例。此外,您使用 SWbemServicesExecNotificationQuery 方法来订阅表示托管环境中更改的事件。
SWbemObjectSet 是一个零个或更多 SWbemObject 对象的集合。为什么是零?因为对一台计算机来说拥有 — 比如说,一个磁带驱动器(由 Win32_TapeDrive建模)的零个实例是有可能的。SWbemObjectSet 中的每个 SWbemObject 都可以表示两件事之一:
1. |
WMI 托管资源的实例。 |
2. |
类定义的实例。 |
SWbemObject 是扮作您正在管理的资源的多身份对象。例如,如果您检索 Win32_Process 托管资源的实例,SWbemObject 呈现出模拟 Win32_Process 类定义的身份,如图 2 左侧所示。另一方面,如果您检索 Win32_Service 托管资源的实例,SWbemObject 呈现出模拟 Win32_Service 类的身份,如图 2 右侧所示。
![scripting01142003_fig2](http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/art/scripting01142003_fig2.gif)
图 2. SWbemObject 扮作 Win32_Process 和 Win32_Service
如果仔细研究图 2,您会注意到 SWbemObject 公开了两个截然不同的方法和属性集。带有以下划线结尾的名称的顶部集合是 SWbemObject 的一部分,它驻留在 wbemdisp.dll 中。下划线是用来防止它与托管资源类定义所定义的方法和属性的名称冲突。
图底部的方法和属性集不是 SWbemObject 的一部分。它们由 CIM 中的托管资源类定义进行定义。当您检索一个实例或托管资源的实例时,SWbemObject 动态地绑定到由托管资源类定义进行定义的方法和属性。您使用 SWbemObject 来调用方法并访问在托管资源类定义中定义的属性,就好像方法和属性是 SWbemObject 的一部分。SWBemObject 变换为在 CIM 中定义的任何托管资源的能力是让 WMI 脚本如此直观的原因。一旦您知道了如何连接并检索实例,那么一切就都集中到 SWbemObject。
好吧。那么 WMI 脚本库对象模型还告诉您什么呢?这个对象模型告诉您,可以将 VBScript(或 WSH)的 GetObject 函数与 WMI 名字对象 (winmgmts:) 以及一个 WMI 对象路径(例如,“[\\ComputerName][\Namespace][:ClassName][.KeyProperty='Value']”)结合使用,以直接创建 SWbemServices 和 SWbemObject。另一方面,SWbemLocator、SWbemLastError、SWbemObjectPath、SWbemNamedValueSet、SWbemSink、SWbemDateTime 和 SWbemRefresher 对象是使用 VBScript(或 WSH)的 CreateObject 函数创建的。剩下的对象不能使用 GetObject 或 CreateObject 来创建。相反,它们是通过调用一个方法或访问一个属性得到的。
对象模型也告诉您,WMI 脚本库中的 7 个对象公开了一个 SWbemSecurity 对象,就像紧挨着该对象下面或右侧的安全标注图标指示的一样。
如需更多关于指定脚本库对象、方法或属性的信息,参阅 WMI SDK 文档 中的 WMI 的脚本 API。要了解 WMI 脚本库的基本机制,让我们将注意力转移到我们前面列出的 7 个 WMI 脚本模板。在我们继续之前,来一两段关于变量命名惯例的内容怎么样?
关于变量命名惯例的三言两语
在随后的示例脚本中,用来引用每个 WMI 自动化对象的变量名称都遵循一致的命名惯例。每个变量都根据在 WMI 脚本库中的自动化对象的名称来命名,以“obj”为前缀(为了说明对象引用)或“col”(为了说明集合对象引用)。例如,一个引用 SWbemServices 对象的变量命名为 objSWbemServices。一个引用 SWbemObject 的变量命名为 objSWbemObject。而一个引用 SWbemObjectSet 的变量命名为 colSWbemObjectSet。
这为什么如此重要?当然,人们可以争论说它不重要。但是,这个想法是帮助您理解在 WMI 脚本中的不同点上您正在使用的 WMI 对象的类型。如果它有帮助,那很好。如果没有,就忽略它好了。另一件略微重要需要紧记的事情是,对象引用变量名称可以是任何符合您喜好的名称。如果您更喜欢像“foo”和“bar”,或者“dog”和“cat”这样的变量名,那也可以。并没有要求规定您必须将一个对 SWbemServices 对象的引用命名为 objSWbemServices。这只是我们如何做而已。
Scripting Guys 的 WMI 脚本模板指南
不可否认,WMI 有非常难于学习而且更难于使用的名声。在许多方面,与其说得到这个名声是因为 WMI 的确很难,还不如说只是因为它太大了。WMI 可以用于管理计算机硬件、计算机软件和几乎这二者范围内的任何事。有人会想当然地认为,任何包含了如此众多不同元素的技术一定是很难的。
事实上,按照众多标准方法,可以用 WMI 执行很多任务。例如,您已经看到了一个模板是如何作为脚本的基础的,这些脚本返回关于几乎任何托管资源的信息。在本系列的第一部分中,相同的基本脚本(有一两个较小的改动)被用于返回像安装的内存、服务和事件日志中的事件记录那样全异的项。
下列主题提供了基本的 WMI 脚本模板,可以用于:
• |
检索托管资源实例。 |
• |
显示托管资源属性。 |
• |
修改托管资源属性。 |
• |
调用托管资源方法。 |
• |
创建托管资源的新实例。 |
• |
删除托管资源的实例。 |
• |
订阅事件以监控托管资源的创建、修改和/或删除。 |
在我们开始之前,需要确保我们非常清楚的一个重要点是:您能(和不能)对托管资源做什么是由在通用信息模型(Common Information Model,CIM)储存库中的托管资源蓝图(即类定义)来决定的,而不是 WMI 脚本库决定的。这就是为什么本系列的第二部分 稍显重要了(暗示,暗示)。还不相信么?我们会给您一些示例的。
您只能修改可写的属性。如何确定一个属性是否可写?可以使用 WbemTest.exe、WMI CIM Studio、WMIC.exe 或者一个脚本来检查该属性的“Write”限定符。(见图 7 或本系列第二部分中的清单 C 作为如何检查属性限定符例子。)如果“Write”限定符没有为属性定义,那么默认的值是“False”,它意味着该属性是只读的。
再举一个例子:如果资源的类定义将 SupportsCreate 类的限定符设置为“True”,那么您只能创建托管资源的新实例。您如何确定一个托管资源类定义将 SupportsCreate 设置为“True”?再次,如图 7 所示以及本系列第二部分的清单 C 中的所示,您检查该托管资源的类限定符。
注在事情情况中,您会发现一些托管资源可以被创建、更新和/或删除,即使该托管资源的类定义不能设置为适当的限定符。我们被告知这种情况正在被更正。
在开始前还要说一件事。下列所有脚本模板都是设计用于本地计算机。这是通过将变量 strComputer 的值设置为一个点 (".") 来实现的。要在远程计算机上运行脚本,只要将 strComputer 的值设置为远程计算机的名称即可。例如,这行代码会让一个脚本在一台名为 atl-dc-01 的计算机上运行:
strComputer = "atl-dc-01"
检索托管资源实例
到现在为止,我们一直使用 SWbemServicesInstancesOf 方法来检索托管资源实例,如清单 1 中所示。
清单 1. 使用 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
虽然 InstancesOf 的确获得了成功,但是您只想要实例或属性的一个子集的情形又怎么样呢?假设您想要优化实例和属性恢复到最小的网络流量。在这种情况下,您会非常高兴听到 WMI 支持大量强大的查询工具。
查询 WMI 是向匹配一些预定义条件的托管资源发出请求的过程。例如,WMI 查询只能请求那些处于 Stopped(停止)状态的带有 StartMode of Auto(自动的启动模式)的服务。
WMI 查询为检索托管资源实例及其属性提供了比 InstancesOf 方法更有效率的机制。WMI 查询只返回那些匹配查询的实例和属性,但是 InstancesOf 总是返回一个指定资源的所有实例以及每个实例的所有属性。同样,查询是在与对象路径中一致的目标计算机上进行的,而不是在运行脚本的源计算机上。因此,WMI 查询可以显著地减少像 InstancesOf 那样更低效率的数据检索机制造成的网络流量。
要查询 WMI,使用 WMI 查询语言(WQL)构造一个查询字符串。查询字符串定义了必须被满足的条件来获得成功匹配的结果。在查询字符串定义后,查询使用 SWbemServicesExecQuery 方法提交到 WMI 服务。满足查询的托管资源实例以 SWbemObjectSet 集合的形式返回到脚本中。
使用 WQL 和 ExecQuery 方法(而不是 InstancesOf)为创建只返回您感兴趣的项的脚本提供了灵活性。例如,您可以使用基本的 WQL 查询来返回一个给定托管资源的所有实例的所有属性,如清单 2 所示。这与 InstancesOf 方法返回的信息是一样的。如果比较清单 1 和 2,您会注意到第 3 行的粗体部分是两个脚本间唯一的差别。
清单 2. 使用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
您也可以使用 WQL 来创建有目标的查询,即做下列事情的查询:
• |
只返回托管资源所有实例的选定的属性。 "SELECT DisplayName, State, StartMode FROM Win32_Service" |
• |
返回一个类的选定的实例的所有属性。 "SELECT * FROM Win32_Service WHERE State = 'Stopped'" |
• |
返回一个类的选定实例的选定属性。 "SELECT DisplayName,State,StartMode FROM Win32_Service WHERE State='Stopped'" |
创建有目标的查询有时会显著地提高数据返回的速度。(例如,只返回那些在应用程序事件日志中含有 EventCode 0 的事件要比返回所有事件日志中的所有事件快的多。)有目标的查询也让对返回的数据的工作变得更轻松。例如,假设您只想要应用程序事件日志中带有 EventCode 0 的事件,使用有目标的查询将只返回那些项目。相反,InstancesOf 将返回所有事件,而且您将不得不单独地检查每个事件并确定它是否:1. 来自应用程序事件日志并且,2. 带有 EventCode 0。虽然这可以完成,但是这太低效了而且对您来说需要额外的工作。
有目标的查询还可以减少返回的数据的数量,对在网络上运行的脚本来说这是个重要的考虑。表 1 展示了一些不同查询类型的相关数字。如您所见,通过各种查询类型返回的数据的数量是有相当大的差别的。
表 1. 比较不同的 WMI 实例检索方法和查询 | |
方法/WQL 查询 | 返回的字节 |
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 |
此时,我们希望我们已经说服了您,关于 ExecQuery 要比 InstancesOf 更好的问题。现在让我们从清单 2 转到一个通用的 WMI 脚本模板,它可以轻易地被修改以检索任何 WMI 托管资源的实例。清单 3 包含我们第一个模板。
清单 3. 用于检索托管资源实例的模板
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
要用其他 WMI 类使用这个模板:
1. |
将 strClass 的值设置为目标托管资源的相应的 WMI 类。 |
2. |
如必要,将 strNamespace 的值设置为目标类的 WMI 命名空间。 |
3. |
替换显示属性及其值的 For Each 循环中的语句。删除下列行,并用在显示的属性值的相应的代码行替换它们。 WScript.Echo "Display Name: " & objSWbemObject.DisplayName WScript.Echo "State: " & objSWbemObject.State WScript.Echo "Start Mode: " & objSWbemObject.StartMode Scripting Guys 免费放送如果您正在使用一个返回许多实例的托管资源(为了讨论的目的,我们将“许多”定义为超过 1000),您可以通过使用可选标记优化 ExecQuery 的行为。例如,假设您使用 ExecQuery 来查询事件日志记录(由 Win32_NTLogEvent 类建模)。如您已知的,事件日志会包含成千上万条记录。默认情况下,您可能会遇到与返回大量结果集相关的性能问题,比如事件日志查询。必须用这种方法来实现的原因是,WMI 为每个实例缓存了一个 SWbemObject 引用,或者在我们的例子中,是为每个事件日志记录。为了避免这个问题,您可以让 ExecQuery 来返回一个只进 SWbemObjectSet,如下所示。 |
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)...
注 wbemFlagReturnImmediately 标记(它是在我们前面简要说明的枚举之一中定义的)是默认的 ExecQuery 行为并且是半同步的。重要的优化是添加 wbemFlagForwardOnly 标记。将 wbemFlagReturnImmediately 与 wbemFlagForwardOnly 结合将获得一个只进的枚举数。只进的枚举数要比默认的枚举数执行快很多,因为 WMI 不维护对在 SWbemObjectSet 中对象的引用。
显示托管资源属性
清单 3 中所示脚本的一个限制是,它需要您事先知道您想要检索和显示的所有属性的名称。如果您想要显示一个资源的所有属性的值,但是您既不知道这些属性的名称,也不想键入需要显示每个属性值的 40 或 50 行代码,这又怎么办呢?在这种情况下,您可以使用清单 4 中的模板,它自动检索并显示一个类中发现的每个属性的值。
清单 4. Scriptomatic lite 模板
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 Function ConvertPropertyValueToString(ByVal PropertyValue) If IsObject(PropertyValue) Then ConvertPropertyValueToString = "" ElseIf IsNull(PropertyValue) Then ConvertPropertyValueToString = "" ElseIf IsArray(PropertyValue) Then ConvertPropertyValueToString = Join(PropertyValue, ",") Else ConvertPropertyValueToString = CStr(PropertyValue) End If End Function
要用其他 WMI 类使用这个模板:
1. |
将 strClass 的值设置为针对目标托管资源的适当的 WMI 类。 |
2. |
如必要,将 strNamespace 的值设置为针对目标类的 WMI 命名空间。 |
修改托管资源的属性
在 Windows 2000 中,WMI 是主要的只读技术。在 Windows 2000 root\cimv2 命名空间中定义的 4,395 个属性中,只有 39 个属性是可写的。这些数字在 Microsoft WindowsXP 中又增加了,在大约 6560 个属性中有 145 个是可写的。这个数字在 Windows Server 2003 中甚至更大了。
清单 5 中的模板演示了如何修改一个可写的属性。这个脚本检索由 Win32_OSRecoveryConfiguration 类模拟的托管资源的所有实例。(在这种情况下,这个类只包含单个实例。)这个脚本为三个属性(DebugInfoType、DebugFilePath 和 OverWriteExistingDebugFile)提供了新的值并使用 SWbemObjectPut_ 方法提交了改变(因此配置操作系统恢复选项)。如果您忘记调用 Put_ 方法,这些更改将不会被应用。
注此模板只对可写的属性起作用。试图更改只读的属性将导致错误。要确定一个属性是否为可写的,检查此属性的“Write”限定符。
清单 5. 用于修改托管资源的可写属性的模板
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
请注意,在 For Each 循环中 SWbemObject 是如何用以:(1)直接访问与修改由 Win32_OSRecoveryConfiguration 类定义的属性;(2)调用其本身的 Put_ 方法来提交所作更改。
要用其他实现可写属性的 WMI 类使用此模板:
1. |
将 strClass 的值设定为目标托管资源的相应 WMI 类。 |
2. |
如必要,将 strNamespace 的值设定为目标类的 WMI 命名空间。 |
3. |
替换配置新属性值的 For Each 循环中的语句。删除下列行,并用正在被修改的属性的相应的代码行替换它们: objSWbemObject.DebugInfoType = 1 objSWbemObject.DebugFilePath = "c:\tmp\memory.dmp" objSWbemObject.OverWriteExistingDebugFile = False |
调用托管资源方法
在托管资源的类定义中定义的方法允许您在托管资源上执行操作。例如,Win32_Service 类包含让您执行诸如启动与停止服务的方法;Win32_NTEventlogFile 包含备份和清除事件日志的方法;Win32_OperatingSystem 包含重新启动或关闭计算机的方法。
清单 6 提供了一个模板,能够用来编写调用 WMI 托管资源方法的脚本。这个特定的脚本使用 Win32_Service 类的 StopService 方法来停止本地计算机上的“警报器”服务。
注在调用在托管资源的类定义中定义的方法之前,该方法必须实现。如何确定一个方法是否实现?检查此方法的实现限定符。TRUE 值表明一个方法有由提供程序提供的实现。已经说过,应注意有些方法不定义实现限定符,即使方法已经实现。下面显示的 Win32_ServiceStopService 方法就是这样一个例子。确定一个方法是否被实现的结果也会卷入一些麻烦和错误。如我们前面所提到的,我们被告知此解决方案正在进行更正。
清单 6. 调用托管资源方法的模板
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
要用其他 WMI 类使用这个模板:
1. |
将 strClass 的值设置为目标托管资源的相应的 WMI 类。 |
2. |
如必要,将 strNamespace 的值设置为目标类的 WMI 命名空间。 |
3. |
将 strKey 的值设置为组成 WHERE 子句基本的属性的名称。 |
4. |
将 strKeyValue 的值设置为 strKey 的相应值。 |
5. |
替换调用方法的 For Each 循环中的语句。删除下列行,并用正在被调用的方法的相应代码行替换它们:如必要,还必须加入相应的方法参数。 objSWbemObject.StopService() |
创建托管资源的一个新实例
一些 WMI 类允许您创建其建模的资源的一个新实例。例如,您可以用 Win32_Environment 类创建环境变量,Win32_Process 类创建过程,Win32_Share 类创建共享资源。
在创建资源的新实例之前,您必须验证托管资源的类是否支持创建操作。通过检查该类的 SupportsCreate 限定符来验证。一个 TRUE 值表明该类支持实例的创建(默认值为 FALSE)。一旦您已经确定该类支持创建操作,您必须确定用以创建新实例的方法。创建新实例有两种方法:
1. |
如果该类用 PutInstance 的值定义 CreateBy 类限定符,您就使用 SWbemObjectSpawnInstance_ 和 Put_ 方法创建新实例。 |
2. |
如果分配给 CreateBy 类限定符的值不是 PutInstance(比如说,是Create),您就使用由 CreateBy 限定符标识的方法创建新实例。 |
让我们分别看一下每个模板。
清单 7 演示了当资源的类定义将 SupportsCreate 设置为 TRUE,将 CreateBy 设置为 PutInstance时,如何创建资源的实例。连接到目标计算机上的 WMI 后,您的第一个步骤应该是获得您要创建的东西的蓝图(即类定义)。要做到这一点,使用 SWbemServicesGet 方法检索实际 WMI 类(而非检索类实例)。有了表示该类的对象之后,使用 SWbemObjectSpawnInstance_ 方法创建一个新的、“空白的”类实例。设置新实例的属性,并调用 SWbemObjectPut_ 方法创建新实例。
清单 7. 用 SpawnInstance_ ºÍ 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") = "" objNewSWbemObject.Properties_.Item("VariableValue") = "c:\tmp" objNewSWbemObject.Put_
用其他支持 PutInstance 的 WMI 类使用这个模板:
1. |
将 strClass 的值设定为目标托管资源的相应 WMI 类。 |
2. |
如必要,将 strNamespace 的值设定为目标类的 WMI 命名空间。 |
3. |
替换配置环境变量值的语句。删除下列行,并用正在创建的对象的相应的代码行替换它们: objNewSWbemObject.Properties_.Item("Name") = "TMPSHARE" objNewSWbemObject.Properties_.Item("UserName") = "" objNewSWbemObject.Properties_.Item("VariableValue") = "c:\tmp" |
Scripting Guys 免费放送当使用 SWbemObjectSpawnInstance_ 和 Put_ 方法创建一个新实例时,您必须为类的所有密钥属性提供(一个或多个)值。例如,在清单 7 中使用的 Win32_Environment 类定义两个密钥属性:Name 和 UserName。如何确定一个类的密钥属性?使用 WbemTest.exe、WMI CIM Studio、WMIC.exe 或脚本来检查该属性的 Key 限定符。
清单 8 演示了在资源的类定义提供其自身的创建方法时,如何创建资源的实例。连接到目标计算机上的 WMI 后,您的第一个步骤应该是获得您要创建的东西的蓝图(即类定义)。要做到这一点,使用 SWbemServicesGet 方法检索实际 WMI 类(而非检索类实例)。有了表示该类的对象之后,用 SWbemObject 调用由该类的 CreateBy 限定符标识的方法。清单 8 中的脚本模板使用 Win32_Share Create 方法创建一个新的共享文件夹。
清单 8. 用于使用托管资源方法创建新实例的模板
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
要使用带有提供自定义创建方法的 WMI 类的模板:
• |
将 strClass 的值设置为目标托管资源的相应的 WMI 类。 |
• |
如必要,将 strNamespace 的值设置为目标类的 WMI 命名空间。 |
• |
替换初始化变量的语句,这些变量是表示传递到创建方法的参数的。删除下列行,并用正在被创建的对象的相应代码行替换它们: Const SHARED_FOLDER = 0 strPath = "c:\tmp" strShareName = "tmp" intMaximumAllowed = 1 strDescription = "Temporary share" |
• |
用您正在创建的托管资源的相应方法替换 Win32_Share Create 方法调用。删除下列行,并用正在被创建的对象的相应代码行替换它们: intReturnValue = objSWbemObject.Create(strPath, _ strShareName, _ SHARED_FOLDER, _ intMaximumAllowed, _ strDescription) |
Scripting Guys 赠品当使用由托管资源的类定义提供的方法创建新实例时,必须为由该方法定义的任何强制参数提供值。例如,在清单 8 中使用的 Win32_Share 类定义 3 个强制参数:路径、名称和类型。您如何确定一个方法的强制参数?参考 WMI SDK中的托管资源的类定义。
删除托管资源实例
如果您能够创建托管资源的新实例,那么显而易见,您也可以删除实例。实际上,控制哪些托管资源实例可以删除的规则与控制创建操作的那些规则惊人地相似。让我们查看一下要求,然后我们将看几个例子。
在删除资源实例前,必须验证托管资源的类是否支持删除操作。通过检查类的 SupportsDelete 限定符来完成这个操作。一个 TRUE 值表明此类支持删除(默认值为 FALSE)。一旦您已经确定了此类支持删除,您必须确定用来删除实例的方法。删除实例有几种方法:
• |
如果此类定义了值为 DeleteInstance 的 DeleteBy 类限定符,可以使用 SWbemServicesDelete 或 SWbemObjectDelete_ 方法删除实例。 |
• |
如果分配给 DeleteBy 类限制符的值不是 DeleteInstance (比如说,是 delete),可以使用由 DeleteBy 限制符标识的方法删除实例。 |
清单 9 和清单10 演示如何删除在清单 7 中创建的环境变量。清单 9 使用 SWbemServicesDelete 方法,清单 10 使用 SWbemObjectDelete_ 方法。在资源类定义将 SupportsDelete 设置为 TRUE,将 and DeleteBy 设置为 DeleteInstance 时,您可以使用清单 9 和清单 10。
清单 9. 用于使用 SWbemServices 删除方法删除实例的模板
strComputer = "." strNamespace = "\root\cimv2" strInstance = "Win32_Environment.Name='TMPSHARE',UserName=''" Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & strNamespace) objSWbemServices.Delete strInstance
清单 10. 用于使用 SWbemObject Delete_ 方法删除实例的模板
strComputer = "." strNamespace = "\root\cimv2" strInstance = "Win32_Environment.Name='TMPSHARE',UserName=''" Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & strNamespace) Set objSWbemObject = objSWbemServices.Get(strInstance) objSWbemObject.Delete_
要使用这些带有支持 DeleteInstance 的其他 WMI 类的模板:
1. |
将 strInstance 的值设置为目标托管资源实例的相应 WMI 类、密钥、密钥值。 |
清单 11 删除了在清单 8 中创建的共享文件夹,在这么做的同时,演示了当一个资源的类定义提供了它自己的删除方法时,如何删除该资源的实例。花一点时间来比较清单 10 和 11。查看在分配给 strInstance 的明显的值外有什么不同?当托管资源的类定义将 DeleteBy 类限定符设置为 DeleteInstance 时,清单 10 使用 SWbemObjectDelete_ 方法(注意下划线)来删除实例。另一方面,清单 11 使用 Win32_Share Delete 方法。
清单 11. 用于使用托管资源方法删除实例的模板
strComputer = "." strNamespace = "\root\cimv2" strInstance = "Win32_Share.Name='tmp'" Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & strNamespace) Set objSWbemObject = objSWbemServices.Get(strInstance) objSWbemObject.Delete
要使用这个带有提供自定义删除方法的其他 WMI 类模板:
1. |
将 strInstance 的值设定为目标托管程序实例的相关 WMI 类、密钥和密钥值。 |
订阅事件
好。该实践程序员第一长项了:偷懒!别担心,我们还是会谈到事件订阅的。不过,不是在这里,而是我们将介绍给您的我们的姊妹篇:TechNet 的 Tales from the Script 专栏,我们在那里刚发表了 A Brief Introduction to WMI Events。除了会看到对 WMI 事件订阅的介绍外,您还将在那里发现另一个脚本资源。
就到这里吧
到这里就结束了我们的 WMI 脚本三部曲。不可否认,还有很多东西需要讨论,我们以后将会继续的。如果您愿意就您希望阅读的 Scripting Clinc 主题给我们发送建议,我们会很高兴。您可以发送到 scripter@microsoft.com 或本页面顶部的“用户意见”处给我们留言。
在我们忘记之前,还有一件事。记得 Microsoft Windows 2000 Scripting Guide — 我们在本文开头提到的那本关于自动化系统管理的书吗? 已经写好了!虽然我们希望您们都冲出去买一本 — 顺便提一下,Bookpool 是地球上最低价的技术图书在线零售商,但我们也知道世界上到处都是像我们自己一样的吝啬鬼。因此,我们也已经提供了此书全部 1328 页的 在线阅读。那么下次 Scripting Clinic,嗯,晚了的时候,您就有地方可去了。如果您觉得阅读这本书的部分或全部,无论如何,让我们知道您的看法 — 好、差或是令人厌恶。当然我们更感兴趣的是,我们遗漏了哪些内容以及我们能做哪些工作来改进第二版。希望您看得开心!
Scripting Clinic
Greg StempGreg Stemp 长期以来被公认为美国国内编写脚本的权威之一,并且被大家广泛誉为世界级……哈哈!嗯,他们 的履历表中怎么都当过足球教练?真的吗?他被解雇 了?哦,很好!Greg Stemp 工作在……哦,好了,难道我连这都不能说吗?好吧!Greg Stemp从 Microsoft 领薪水,在 Microsoft 他拥有并不显赫的首席作家(System Administration Scripting Guide)的头衔。
Dean Tsaltas 是一个生活在 Redmond 的 Nova Scotian 人。他已经能说一口流利的美语了,甚至会笑他的滨海省的朋友和家人们的发音。他的计算机生涯开始于他很小的时候,那时他的祖母和父母资助他,为他买了他心爱的 C-64,并为他订阅了 Compute! 的学报。现在他已经在 Microsoft 工作了若干年,他有句话要告诉在家乡和温哥华的朋友和家人: “没有,我还没见过比尔!”
Bob Wells 漫无目的地到处闲逛,向每个听他说话的人大赞脚本编写的好处。有传言说,Bob 的两只达克思猎犬对脚本编写比大多数人类知道的都多。业余时间里,Bob 向 System Administration Scripting Guide 投稿。
Ethan Wilansky 把他的很多的工作时间花在了写作和咨询方面。他狂热于脚本编写、瑜伽、园艺和他的家庭(不一定按此顺序)。他目前正致力于一种创建能倒垃圾和洗餐盘的脚本的方法。