1、VB 使用 WMI 编程 (零号 )一、认识 WMI通常 VB 程序员经常抱怨 VB 在编制 Windows 系统软硬件设备控制方面的程序比较困难,即使能实现一些功能,那也是要通过调用繁复的 API 函数,即难找寻到合适的函数,又难以理解设置函数中的各项参数,尤其是调试异常困难,动不动整个程序连带 VB 环境一起死悄悄。此外,用 API 编程几乎完全抛弃了面向对象的编程方法,迫使我们回到过程方式的编程。那怎么办呢?_别急,微软在 WIN2000 系统中推出了 VBScript 脚本语言替代原来的批命令,同时提供了一个供 VBScript 管理 Windows 系统的对象WMI。那这个 WMI
2、对象我们 VB 能用吗?答案是肯定的,当然能用,而且用起来还非常方便,在网上也已经有了一些 VB 写的利用 WMI 实现 Windows 系统管理编程的例程,但却缺少系统性介绍使用 VB 对WMI 编程的资料。本文就针对此问题,进行一个尝试。当然要学习 WMI 编程,需要有一定的 VB 基础,但要求不高,只要会用控件、对象,能理解和使用对象的方法、属性,但不需要任何 API 方面的知识和编程经验。首先我们要知道什么是 WMI?WMI 是 Windows Management Instrumentation (Windows 管理工具) 的缩写,是内置在 Windows 2000、Windows
3、 XP 和 Windows Server 2003 系列操作系统中核心的管理支持技术。基于由 DistributedManagement Task Force (DMTF) 所监督的业界标准,WMI 是一种规范和基础结构,通过它可以访问、配置、管理和监视所有的 几乎所有的 Windows 资源。呵呵,不要对这段文字进行咬文嚼字,通俗的讲,就是 WMI 是一个用于管理 Windows 系统的对象,就像 ADO 对象是用于数据库操作的。利用 WMI 我们可以管理 Windows 系统中的磁盘、事件日志、文件、文件夹、文件系统、网络组件、操作系统设置、性能数据、打印机、进程、注册表设置、安全性、服务
4、、共享、用户、组等等。而 WMI 适用的运得环境也是有些限制的, WMI 附带在 Windows Me、Windows 2000、Windows XP 和 WindowsServer 2003 之中。对于 Windows 98 和 Windows NT 4.0,可以访问http:/ 并搜索“Windows ManagementInstrumentation (WMI) CORE 1.5 (Windows 95/98/NT 4.0)”。注意:在 Windows NT 4.0 上安装并运行 WMI 之前,需要首先安装 Service Pack 4 或更高版本。WMI 需要的其他软件包括:1. Mi
5、crosoft Internet Explorer 5.0 或更高版本。 2. Windows Script Host(WSH) 。Windows2000、Windows XP、Windows Server 2003、和 Windows Me 附带的 WSH,而不是 Windows NT4 或 Windows 98 附带的 WSH。您可以从以下地址下载 WSHhttp:/ 的最新版本 包括在 Windows XP 和 Windows Server 2003 之中是 WSH 5.6。 要使 WMI 脚本可以正常的运行,Windows 里的 WMI 服务(winmgmt)保证是运行的,这样才可以实
6、现 WMI 里的更多功能。好了,关于 WMI 的一些基本的信息资料就说到这,要想看更多的可以到 MicroSoft 网站的 MSDN 找。大家都喜欢通过例程来学东西,那我们也先编一段程序来看看。不知道大家看到过没有本人在本论坛中曾经写过一篇“自己做进程管理器”的帖子,在那篇帖子里主要是采用 API 函数罗列出当前系统下正在运行的所有进程,下面我们利用 WMI 也来做一个进程管理器。首先建立一个新工程具有 Form1 窗体,在菜单中的【工程】【部件】下,添加“MicrosoftWindows Common Controls 6.0”,在菜单中的【工程】【引用】下,添加“Microsoft WMI
7、 Scripting V1.1Library”,然后在 Form1 窗体上添加 1 个 ListView1和 Command1、Command2,在代码窗口添加如下代码(例程 1):Option ExplicitDim objSWbemLocator As New SWbemLocatorDim objSWbemServices As SWbemServicesDim objSWbemObjectSet As SWbemObjectSetDim objSWbemObject As SWbemObjectPrivate Sub Form_Load()Me.Caption = “进程管理器“ Co
8、mmand1.Caption = “刷新“ Command2.Caption = “结束进程“ListView1.ColumnHeaders.ClearListView1.ColumnHeaders.Add , “a“, “进程 ID“, 600ListView1.ColumnHeaders.Add , “b“, “进程名“, 2000ListView1.ColumnHeaders.Add , “c“, “路径“, 6000ListView1.View = lvwReportCommand1_Click 刷新进程列表End SubPrivate Sub Command1_Click()Dim
9、i As LongListView1.ListItems.Clear 清空 ListViewSet objSWbemServices = objSWbemLocator.ConnectServer() 连接到本机的 WMI,返回一个对 SWbemServices 对象的引用Set objSWbemObjectSet = objSWbemServices.InstancesOf(“Win32_Process“) 返回 Win32_Process类名标识的所有实例i = 0For Each objSWbemObject In objSWbemObjectSet 枚举每一个 Win32_Proces
10、s 的实例ListView1.ListItems.Add , “a“ & i, objSWbemObject.Handle 将进程 ID 添加到 ListView1 第一列ListView1.ListItems(“a“ & i).SubItems(1) = objSWbemObject.Name 将进程名添加到 ListView1 第二列If Not IsNull(objSWbemObject.ExecutablePath) Then _ListView1.ListItems(“a“ & i).SubItems(2) = objSWbemObject.ExecutablePath 将进程路径添
11、加到 ListView1 第三列i = i + 1NextSet objSWbemObjectSet = NothingEnd SubPrivate Sub Command2_Click()Dim TMBack As LongIf ListView1.SelectedItem.Text 0 ThenSet objSWbemObjectSet = objSWbemServices.ExecQuery(“SELECT * FROM “ & strClass & “ WHERE DisplayName = “ & ListView1.SelectedItem.Text & “) 查询类中 Displ
12、ayName 属性等于指定值的实例For Each objSWbemObject In objSWbemObjectSetIf objSWbemObject.StopService = 0 Then 停止指定服务MsgBox ListView1.SelectedItem.Text & “服务已经被停止!“ElseMsgBox ListView1.SelectedItem.Text & “服务不能被停止!“End IfNextRefreshList 刷新服务列表End IfEnd Sub启动指定服务Private Sub Command2_Click()Set objSWbemObjectSet
13、 = objSWbemServices.ExecQuery(“SELECT * FROM “ & strClass & “ WHERE DisplayName = “ & ListView1.SelectedItem.Text & “) 查询类中 DisplayName 属性等于指定值的实例For Each objSWbemObject In objSWbemObjectSetIf objSWbemObject.StartService = 0 Then 启动指定服务MsgBox ListView1.SelectedItem.Text & “服务已经被启动!“ElseMsgBox ListVi
14、ew1.SelectedItem.Text & “服务不能被启动!“End IfNextRefreshList 刷新服务列表End SubPrivate Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem)If Item.SubItems(1) = “Stopped“ ThenCommand1.Enabled = FalseCommand2.Enabled = TrueElseCommand1.Enabled = TrueCommand2.Enabled = FalseEnd IfEnd Sub刷新服务列表Sub Refresh
15、List()Dim i As LongListView1.ListItems.ClearSet objSWbemObjectSet = objSWbemServices.ExecQuery(“SELECT * FROM “ & strClass) 通过 WQL查询,返回指定类的所有For Each objSWbemObject In objSWbemObjectSetListView1.ListItems.Add , “a“ & i, objSWbemObject.DisplayName 将服务名称添加到 ListView1 第一列ListView1.ListItems(“a“ & i).Su
16、bItems(1) = objSWbemObject.State 将服务的状态添加到 ListView1第二列ListView1.ListItems(“a“ & i).SubItems(2) = objSWbemObject.StartMode 将服务的启动方式添加到ListView1 第三列ListView1.ListItems(“a“ & i).SubItems(3) = objSWbemObject.PathName 将服务程序的路径添加到ListView1 第四列ListView1.ListItems(“a“ & i).SubItems(4) = objSWbemObject.Star
17、tName 将服务的登录身份添加到ListView1 第五列ListView1.ListItems(“a“ & i).SubItems(5) = objSWbemObject.ProcessId 将服务的进程 ID 添加到ListView1 第六列i = i + 1NextSet ListView1.SelectedItem = ListView1.ListItems(1)ListView1_ItemClick ListView1.ListItems(1)End Sub例程 13 是一个使用 Win32_Service 类的 StopService、StartService 方法的样例,虽然对
18、我们来说有很大的实际意义:在 VB 程序中可以轻松启动和终止系统服务。但本节我们关心的是“我们如何知道某个类有那些方法呢”?我们有了上面例程 12 的经验,同时告诉大家 SWbemObject 对象还有 1 个 Methods_属性,那我们应该很容易编写出罗列指定命名空间的某个类的全部方法,我们在代码窗口添加 1 个 List1 控件,代码如下(例程 14):Option ExplicitDim objSWbemObject As SWbemObjectDim objSWbemMethod As SWbemMethodDim strComputer As String, strNameSpac
19、e As String, strClass As StringPrivate Sub Form_Load()strComputer = “.“ 计算机名,. 为本机 strNameSpace = “rootcimv2“ 指定命名空间为rootcimv2strClass = “Win32_NetworkAdapterConfiguration“ 指定类为 Win32_NetworkAdapterConfigurationMe.Caption = strClass & “类的方法名称“Set objSWbemObject = GetObject(“winmgmts:“ & strComputer
20、& “ & strNameSpace & “:“ & strClass)For Each objSWbemMethod In objSWbemObject.Methods_List1.AddItem objSWbemMethod.Name 将 Win32_NetworkAdapterConfiguration 的方法名称添加到 List1NextSet objSWbemObject = NothingSet objSWbemMethod = NothingEnd Sub例程 14 与例程 12 的差异除了所选择的类不同外(例程 12 选择的是类 Win32_Process,例程 14 选择的是
21、类 Win32_NetworkAdapterConfiguration) ,主要是在 ForEach 循环中,例程 14 是通过 SWbemObject 对象的 Methods_ 属性枚举了类的 SWbemMethodSet 集合(注意在例程 12 中是SWbemPropertySet 集合) ,并列表显示 SWbemMethodSet 集合中每个 SWbemMethod(objSWbemMethod) 的 Name 属性。 3)限定符限定符是提供关于类、属性或方法的附加信息到其所应用的事物的形容词。这个概念对我们这些 VB 程序员来说可能感到比较陌生,其实也不难理解,例如,问题“Win32_
22、Service 类的类型是什么?”是由该类的 Dynamic 限定符来回答的。当您开始编写不只是简单地检索信息的 WMI 程序(例如,修改属性或调用方法)时,限定符变得愈加重要,因为它们定义了您正在更新的属性或正在调用的方法的操作特性。那么限定符提供哪种信息呢?我们接着来看: 类限定符 类限定符提供了关于类的操作信息。例如: 如我们之前了解的, Abstract(抽象类) 、Dynamic(动态类)和 Association(关联类)限定符会告诉我们类的类型。 Provider 限定符告诉您服务这个类的提供程序。例如, Win32_Service 类的 Provider 限定符告诉您这个类使用
23、 CIMWin32 提供程序(cimwin32.dll)。另一方面,如由 Win32_NTLogEvent 类的 Provider 限定符表明的那样,Win32_NTLogEvent 类使用 MS_NT_EVENTLOG_PROVIDER 提供程序 (ntevt.dll)。 Privileges 限定符告诉您要使用这个类所需要的专用特权。例如,Win32_NTLogEvent 类的 Privileges 限定符告诉您在 Win32_NTLogEvent 类可以用来管理安全日志前,SeSecurityPrivilege 必须被启用。 属性限定符属性限定符提供关于每个属性的信息。例如: CIMTy
24、pe 限定符告诉您属性的数据类型。 Read 限定符指出这个属性是可读的。 Write 限定符指出您是否可以修改属性的值。例如,我们在清单 4 中修改的 Win32_WMISetting 类的 ASPScriptDefaultNamespace 属性被标记为可写。另一方面,例程 12 中回显的所有 Win32_Service 属性都被定义为只读 就是说,它们不定义 Write 限定符。 Key 限定符指出该属性是类的键,并且用于识别在相同资源集合中的托管资源的唯一实例。 方法限定符方法限定符提供关于每个方法的信息。例如: Implemented 限定符表明这个方法有一个由提供程序提供的实现。
25、ValueMap 限定符为方法参数或返回类型定义了一组允许的值。 Privileges 限定符告诉您调用这个方法所需的专用特权。 不过我们还应该知道,其实实际情况比我们这里所提到的限定符更多。有关完整的列表,参阅 WMISDK 中的 WMI Qualifiers 主题( http:/ . /wmi_qualifiers.asp) 。现在我们关心的问题是不是应该为“我们如何知道某个类有那些限定符呢”?我们有了上面例程 12 和例程 14 的经验,同时告诉大家 SWbemObject 对象还有 1 个 Qualifiers_属性,那我们是不是应该很容易编写出罗列指定命名空间的某个类的所有限定符呢?
26、是的,我们同例程 12、例程 14 一样在代码窗口添加1 个 List1 控件,代码如下(例程 15):Option ExplicitDim objSWbemObject As SWbemObjectDim objSWbemQualifier As SWbemQualifierDim strComputer As String, strNameSpace As String, strClass As StringPrivate Sub Form_Load()strComputer = “.“ 计算机名,. 为本机 strNameSpace = “rootcimv2“ 指定命名空间为rootci
27、mv2strClass = “Win32_NTLogEvent“ 指定类为 Win32_NTLogEventMe.Caption = strClass & “类的限定符“Set objSWbemObject = GetObject(“winmgmts:“ & strComputer & “ & strNameSpace & “:“ & strClass)For Each objSWbemQualifier In objSWbemObject.Qualifiers_If VarType(objSWbemQualifier.Value) = (vbVariant + vbArray) ThenLi
28、st1.AddItem objSWbemQualifier.Name & “ = “ & Join(objSWbemQualifier.Value, “,“)ElseList1.AddItem objSWbemQualifier.Name & “ = “ & objSWbemQualifier.ValueEnd IfNextSet objSWbemObject = NothingSet objSWbemQualifier = NothingEnd Sub不过我们来看一下例程 15 与例程 12 和例程 14 的差异还是不小的:1 )For Each 循环枚举类的SWbemQualifierSe
29、t 集合(通过 SWbemObject 对象的 Qualifiers_ 属性) ,并回显 SWbemQualifierSet 集合中每个 SWbemQualifier (objSWbemQualifier 对象) 的 Name 属性。2)因为类限定符是类定义的一部分而且限定符有值,例程 15 也检索并列表显示了 SWbemQualifierSet 集合中每个 SWbemQualifier(objSWbemQualifier) 的 Value 属性。3 )因为一个限定符可以有多个存储在数组中的值,持续必须在读取限定符的值前说明此点。如果不这么做,那么程序试图将一个基于数组的限定符作为标量变量来读
30、取,将会导致运行时错误。Win32_NTLogEvent 类的 Privileges 限定符是一个基于数组的限定符实例。但大概你可能已经注意到的,例程 12 和例程 14 无法显示属性和方法的限定符。说实话,这是故意让程序比较简洁,而使其容易理解。在本节最后我们将给出一个例程,包含了列表显示完整的类限定符、属性、属性限定符、方法和方法限定符。你是不是对我们总是用这种相同的方法来做例程有点烦了,呵呵,你如果想要知道是否还有其它方法来获得指定命名空间的某个类的所有限定符,那我告诉你当然有,而且还不止一种。还记得在前面我们说过可以直接从 MOF 文件(包含类的定义)中检索管理资源类定义。例如,如果您
31、想要查找 Win32_Service 类,可以查看%SystemRoot%system32wbemcimwin32.mof 文件。但是,直接使用 MOF 文件是有代价的。您必须检查托管资源的类层次结构中的每个类,以获得完整的托管资源的模板。 比如说,您想要查找 Win32_Service。您就不得不检查 Win32_Service 类层次结构中所有的 5 个类,以获得完整的模型,如图 1 所示。如果您使用 WMI 测试器 (wbemtest.exe) 的 Show MOF 按钮,也是同样。获得类的 MOF 表示形式的较简单方法是使用 WMI 脚本对象库的 SWbemObject 对象的GetO
32、bjectText_ 方法,如例程 16 中演示的。使用这个方法可以得到很大的收获,因为它对我们编制 WMI 应用程序提供了详尽的说明,希望大家务必将该例程实际做一下。在代码窗口添加 1 个 Text1 控件,并将它的 MultiLine 属性设置为True、ScrollBars 属性设置为 3,代码如下(例程 16):Option ExplicitDim objSWbemServices As SWbemServicesDim objSWbemObject As SWbemObjectDim strComputer As String, strNameSpace As String, str
33、Class As StringPrivate Const wbemFlagUseAmendedQualifiers = &H20000Private Sub Form_Load()strComputer = “.“ 计算机名,. 为本机 strNameSpace = “rootcimv2“ 指定命名空间为rootcimv2strClass = “Win32_NTLogEvent“ 指定类为 Win32_NTLogEventMe.Caption = strClass & “类的限定符“Set objSWbemServices = GetObject(“winmgmts:“ & strComput
34、er & “ & strNameSpace)Set objSWbemObject = objSWbemServices.Get(strClass, wbemFlagUseAmendedQualifiers)Text1.Text = objSWbemObject.GetObjectText_Set objSWbemObject = NothingEnd Sub与例程 12、例程 14、例程 15 不同,例程 16 使用 SWbemServices 的 Get 方法来检索类,必须使用SWbemServices 的 Get 方法,这样才能启用 wbemFlagUseAmendedQuailifier
35、s 标志。启用wbemFlagUseAmendedQuailifiers 标志,告诉 WMI 返回整个托管资源模板(类定义)而不仅仅是局部的定义。 使用 wbemFlagUseAmendedQualifiers 标记还有第二个好处。您也取回了类描述,以及对每个类的属性、方法和限定符的描述。类、属性、方法和限定符描述通常在一个本地化目的、单独的 MOF文件中定义。例如,Win32_Service 类的中性语言部分是在 cimwin32.mof 中定义的。Win32_Service 类的特定语言部分,包括描述信息,是在 cimwin32.mfl 中定义的。特定语言的(或本地化的) MOF 文件通常
36、带有一个 .mfl(而非.mof)扩展名。 SWbemServices 的 Get 方法返回对表示目标类的 SWbemObject (objSWbemObject)的引用,然后调用 SWbemObject 的 GetObjectText_ 方法。GetObjectText_ 方法返回该类的 MOF 表示形式。如果我们使用了 GetObjectText_ 而没有启用 wbemFlagUseAmendedQuailifiers 标记,这个方法将只返回那些由 Win32_Service 定义的属性、方法和限定符,继承的属性和方法会被省略。 我们已经讲了第二种方法,其实还可以使用 SWbemObjec
37、t 的GetText_方法获得托管资源类定义的 XML 表现形式,这里就不再介绍了。本节的内容比较多,主要是介绍了 CIM 储存库的结构以及其包含的命名空间、类、属性、方法和限定符,同时通过例程来列表显示这些内容,通过本节的学习,至少应该是加深了对 CIM 储存库的了解,希望对大家有所帮助。那我们是否可以提一个要求:能不能把本节的例程 9(获得 CIM 所有命名空间) 、例程 11(获得指定命名空间中定义的所有类) 、例程 12(获得指定命名空间的某个类的全部属性) 、例程14(获得指定命名空间的某个类的全部方法) 、例程 15(获得指定命名空间的某个类的全部限定符)结合起来。那么我告诉大家,
38、我也正有此意,下面给出的例程 17,能够列表显示完整的 CIM 储存库包含的所有命名空间、类、类限定符、属性、属性限定符、方法和方法限定符。首先建立一个新工程具有 Form1窗体,在菜单中的【工程】【部件】下,添加“MicrosoftWindows Common Controls 6.0”,在菜单中的【工程】【引用】下,添加“Microsoft WMI Scripting V1.1Library”,然后在 Form1 窗体上添加 1 个TreeView1,在代码窗口添加如下代码(例程 17):Option ExplicitDim objSWbemServices As SWbemService
39、sDim colSWbemObjectSet As SWbemObjectSetDim objSWbemObject As SWbemObjectDim strComputer As String, strClass As String , ClassNum As LongPrivate Sub Form_Activate()strComputer = “.“TreeView1.Nodes.Add , , “root“, “命名空间:根(Root )“ EnumNameSpaces “root“ 添加 root 下的所有命名空间到 TreeView1TreeView1.Nodes(“root“
40、).Expanded = TrueTreeView1_ClickEnd SubPrivate Sub TreeView1_Click()Dim objClassQualifier As Object, objClassProperty As Object, objClassMethod As Object, objPropertyQualifier As Object, objMethodQualifier As ObjectDim strQualifier As String, NodeX As Node判别 TreeView1 当前是否存在选定项On Error Resume NextIf
41、 IsError(TreeView1.SelectedItem.Index) ThenSet TreeView1.SelectedItem = TreeView1.Nodes(1) 若没有选定任何项,则设选定项为 1End IfOn Error GoTo 0判别是否点击 TreeView1 中的是命名空间项 If InStr(1, TreeView1.SelectedItem.Key, “类“) “类“ ThenIf IsError(TreeView1.Nodes(“限“ & TreeView1.SelectedItem.Key) ThenOn Error GoTo 0添加类限定符Set No
42、deX = TreeView1.Nodes.Add(TreeView1.SelectedItem.Key, tvwChild, “限“ & TreeView1.SelectedItem.Key, “类限定符“)NodeX.Sorted = TrueNodeX.EnsureVisibleSet objSWbemObject = GetObject(“winmgmts:“ & Mid(TreeView1.SelectedItem.Key, 2) 返回指定类对象For Each objClassQualifier In objSWbemObject.Qualifiers_If VarType(obj
43、ClassQualifier.Value) = (vbVariant + vbArray) ThenstrQualifier = objClassQualifier.Name & “ = “ & Join(objClassQualifier.Value, “,“)ElsestrQualifier = objClassQualifier.Name & “ = “ & objClassQualifier.ValueEnd IfTreeView1.Nodes.Add “限“ & TreeView1.SelectedItem.Key, tvwChild, “限“ & TreeView1.Selecte
44、dItem.Key & objClassQualifier.Name, strQualifierstrQualifier = “Next添加属性Set NodeX = TreeView1.Nodes.Add(TreeView1.SelectedItem.Key, tvwChild, “属“ & TreeView1.SelectedItem.Key, “属性“)NodeX.Sorted = TrueNodeX.EnsureVisibleFor Each objClassProperty In objSWbemObject.Properties_TreeView1.Nodes.Add “属“ &
45、TreeView1.SelectedItem.Key, tvwChild, “属“ & TreeView1.SelectedItem.Key & objClassProperty.Name, objClassProperty.Name添加属性限定符 TreeView1.Nodes.Add “属“ & TreeView1.SelectedItem.Key & objClassProperty.Name, tvwChild, “限属“ & TreeView1.SelectedItem.Key & objClassProperty.Name, “属性限定符“For Each objPropertyQ
46、ualifier In objClassProperty.Qualifiers_If VarType(objPropertyQualifier.Value) = (vbVariant + vbArray) ThenstrQualifier = objPropertyQualifier.Name & “ = “ & Join(objPropertyQualifier.Value, “,“)ElsestrQualifier = objPropertyQualifier.Name & “ = “ & objPropertyQualifier.ValueEnd IfTreeView1.Nodes.Add “限属“ & TreeView1.SelectedItem.Key & objClassProperty.Name, tvwChild, “限属“ & TreeView1.SelectedItem.Key & objClassProperty.Name & objPropertyQualifier.Name,