VB 使用WMI编程(一)

来源:互联网 发布:用java编写网页的步骤 编辑:程序博客网 时间:2024/04/30 08:04

由底至上是:
          • 托管资源

          • WMI 基础结构

          • 使用者

托管资源
    托管资源是任意逻辑或物理组件,通过使用 WMI 进行公开和管理。可以使用 WMI 管理的 Windows 资源包括:计算机系统、磁盘、外围设备、事件日志、文件、文件夹、文件系统、网络组件、操作系统子系统、性能计数器、打印机、进程、注册表设置、安全性、服务、共享、SAM 用户和组、Active Directory、Windows 安装程序、Windows 驱动程序模式 (WDM) 设备驱动程序,以及 SNMP 管理信息基 (MIB) 数据等。WMI 托管资源通过一个提供程序与 WMI 通讯。
WMI 基础结构
    中间层是 WMI 基础结构。WMI 由四个主要组件构成:公共信息模型对象管理器(Common Information Model Object Manager,CIMOM)、公共信息模型(Common Information Model,CIM)储存库,以及提供程序。这三个 WMI 组件共同提供通过其定义、公开、访问和检索配置和管理数据的基础结构,为了叙述和理解方便,我们在本文中就将这三个组件通称为CIM储存库。第四个组件是 WMI 脚本对象库,就是我们前面几个例程都必须引用的“Microsoft WMI Scripting V1.1 Library”对象库,就我们来说 — 就是使用者,首先就是通过WMI 脚本库与CIM储存库打交道去管理系统的各种托管资源。因此我们重点关注的应该是CIM储存库和WMI脚本对象库。
CIM 储存库
    WMI 的基本思想是 — 可以用一个架构统一表示来自不同源的配置和管理信息。CIM 就是这个架构,还调用了模型化托管环境和定义每个由 WMI 公开的数据块的对象储存库或类存储。该架构基于 DMTF 公共信息模型标准。
    CIM 由类 组成。类是一个 WMI 托管资源的一个模型。CIM 类通常表示动态资源,就是说托管资源的实例并不存储在 CIM 中,而是通过基于使用者请求的提供程序动态检索。其原因很简单,大多数 WMI 托管资源的操作状态更改很频繁,因而必须按需读取以确保检索的是最新的信息,就象我们例程1中的进程和例程2中的服务随时都在变动的。
    注在 CIM 的上下文中,储存库这一术语有些被误解。尽管 CIM 是一个储存库,而且能够存储静态数据,但其主要角色是存储托管资源的模型。
    CIM 类是分级组织的,每一级的子类从父类继承。DMTF 维护一组核心和公共基类,系统和应用程序软件开发人员(如 Microsoft 的那些)从这些类派生和创建系统(或应用程序)特定的扩展类。
    类被分组到命名空间 中,命名空间是表示一个特定的管理区域的类逻辑组。例如,命名空间 root/cimv2 包括大部分表示通常与计算机和操作系统相关联的资源的类。在前面的例程中使用的类(Win32_Process、Win32_Service 和为Win32_ShortcutFile)驻留在命名空间 root/cimv2,它们只是在 CIM 中定义的数百个类中的三个。
    CIM 类由属性和方法构成。属性描述 WMI 托管资源的配置和状态,方法是在 WMI 托管资源上执行操作的可执行函数。
    注 不要将由 CIM 定义的方法和属性与由 WMI 脚本对象库中的自动化对象提供的方法和属性相混淆。
WMI 脚本对象库
    WMI 脚本对象库提供一组应用对象集,WMI 脚本对象库中的这些对象为 WMI 基础结构提供一个一致且统一的模型。如前面所示,一旦您了解如何使用 WMI 脚本对象库检索一个托管资源类型,您就可以轻松使用相同的步骤来检索其它的 WMI 托管资源。例如,您可以使用前面列出的 5个例程中的任何一个,并很容易地修改这个脚本来检索在远程计算机上的事件日志记录 (Win32_NTLogEvent) 信息、处理器 (Win32_Processor) 信息、操作系统 (Win32_OperatingSystem) 信息,或者由 WMI 公开的数百个托管资源中的任何一个。
使用者
      使用者就是利用WMI的人,就是我们喽,或者说是可以利用WMI编程的程序语言,就不多说了
    本节就到这里吧,下一讲我们详细分析一下CIM的结构。


一、认识WMI
    通常VB程序员经常抱怨VB在编制Windows系统软硬件设备控制方面的程序比较困难,即使能实现一些功能,那也是要通过调用繁复的API函数,即难找寻到合适的函数,又难以理解设置函数中的各项参数,尤其是调试异常困难,动不动整个程序连带VB环境一起死悄悄。此外,用API编程几乎完全抛弃了面向对象的编程方法,迫使我们回到过程方式的编程。那怎么办呢?^_^别急,微软在WIN2000系统中推出了VBScript脚本语言替代原来的批命令,同时提供了一个供VBScript管理Windows系统的对象WMI。那这个WMI对象我们VB能用吗?答案是肯定的,当然能用,而且用起来还非常方便,在网上也已经有了一些VB写的利用WMI实现Windows系统管理编程的例程,但却缺少系统性介绍使用VB对WMI编程的资料。本文就针对此问题,进行一个尝试。当然要学习WMI编程,需要有一定的VB基础,但要求不高,只要会用控件、对象,能理解和使用对象的方法、属性,但不需要任何API方面的知识和编程经验。
    首先我们要知道什么是WMI?WMI是Windows Management Instrumentation (Windows管理工具)的缩写,是内置在 Windows 2000、Windows XP 和 Windows Server 2003 系列操作系统中核心的管理支持技术。基于由 Distributed Management Task Force (DMTF) 所监督的业界标准,WMI 是一种规范和基础结构,通过它可以访问、配置、管理和监视所有的 — 几乎所有的 Windows 资源。
    呵呵,不要对这段文字进行咬文嚼字,通俗的讲,就是WMI是一个用于管理Windows系统的对象,就像ADO对象是用于数据库操作的。利用WMI我们可以管理Windows系统中的磁盘、事件日志、文件、文件夹、文件系统、网络组件、操作系统设置、性能数据、打印机、进程、注册表设置、安全性、服务、共享、用户、组等等。
    而WMI适用的运得环境也是有些限制的, WMI 附带在 Windows Me、Windows 2000、Windows XP 和 Windows Server 2003 之中。对于 Windows 98 和 Windows NT 4.0,可以访问http://www.microsoft.com/downloads并搜索“Windows Management Instrumentation (WMI) CORE 1.5 (Windows 95/98/NT 4.0)”。注意:在 Windows NT 4.0 上安装并运行 WMI 之前,需要首先安装 Service Pack 4 或更高版本。
WMI 需要的其他软件包括:
    1. Microsoft Internet Explorer 5.0 或更高版本。
    2. Windows Script Host(WSH)。Windows 2000、Windows XP、Windows Server 2003、和 Windows Me 附带的 WSH,而不是 Windows NT4 或 Windows 98 附带的 WSH。您可以从以下地址下载 WSH http://www.microsoft.com/downloads. WSH 的最新版本—— 包括在 Windows XP 和 Windows Server 2003 之中——是 WSH 5.6。
    要使WMI脚本可以正常的运行,Windows里的WMI 服务(winmgmt)保证是运行的,这样才可以实现WMI里的更多功能。好了,关于WMI的一些基本的信息资料就说到这,要想看更多的可以到MicroSoft网站的MSDN找。
    大家都喜欢通过例程来学东西,那我们也先编一段程序来看看。不知道大家看到过没有本人在本论坛中曾经写过一篇“自己做进程管理器”的帖子,在那篇帖子里主要是采用API函数罗列出当前系统下正在运行的所有进程,下面我们利用WMI也来做一个进程管理器。首先建立一个新工程具有Form1窗体,在菜单中的【工程】—【部件】下,添加“Microsoft Windows Common Controls 6.0”,在菜单中的【工程】—【引用】下,添加“Microsoft WMI Scripting V1.1 Library”,然后在Form1窗体上添加1个ListView1和Command1、Command2,在代码窗口添加如下代码(例程1):

Option Explicit

Dim objSWbemLocator As New SWbemLocator
Dim objSWbemServices As SWbemServices
Dim objSWbemObjectSet As SWbemObjectSet
Dim objSWbemObject As SWbemObject

Private Sub Form_Load()
    Me.Caption = "进程管理器"
    Command1.Caption = "刷新"
    Command2.Caption = "结束进程"
    ListView1.ColumnHeaders.Clear
    ListView1.ColumnHeaders.Add , "a", "进程ID", 600
    ListView1.ColumnHeaders.Add , "b", "进程名", 2000
    ListView1.ColumnHeaders.Add , "c", "路径", 6000
    ListView1.View = lvwReport
    Command1_Click '刷新进程列表
End Sub

Private Sub Command1_Click()
Dim i As Long
    ListView1.ListItems.Clear '清空ListView
    Set objSWbemServices = objSWbemLocator.ConnectServer()  '连接到本机的WMI,返回一个对 SWbemServices 对象的引用
    Set objSWbemObjectSet = objSWbemServices.InstancesOf("Win32_Process")   '返回Win32_Process类名标识的所有实例
    i = 0
    For Each objSWbemObject In objSWbemObjectSet  '枚举每一个Win32_Process的实例
        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 '将进程路径添加到ListView1第三列
        i = i + 1
    Next
    Set objSWbemObjectSet = Nothing
End Sub


Private Sub Command2_Click()
Dim TMBack As Long
    If ListView1.SelectedItem.Text <> "" Then
        If MsgBox("确实要结束进程[" & ListView1.SelectedItem.SubItems(1) & "]吗?", vbYesNo) = vbYes Then
            Set objSWbemObjectSet = objSWbemServices.ExecQuery("SELECT * FROM Win32_Process WHERE Handle = '" & ListView1.SelectedItem.Text & "'")   '查询Win32_Process类中Handle属性等于指定值的所有实例
            For Each objSWbemObject In objSWbemObjectSet
                TMBack = objSWbemObject.Terminate     '终止指定进程
            Next
            If TMBack = 0 Then
                MsgBox ListView1.SelectedItem.SubItems(1) & "已经被终止!"
            Else
                MsgBox ListView1.SelectedItem.SubItems(1) & "不能被终止!"
            End If
            Command1_Click '刷新进程列表
        End If
    End If
End Sub

        呵呵,我们没有用任何系统的API函数却千真万确的实现了进程管理这个系统级的应用,而且采用的是标准的面向对象的编程,其实这段代码仅仅实现了我们在前面提到的一篇“自己做进程管理器”的帖子中用API函数也能实现的功能。嗯???怎么?还能比这更强?哈哈,当然!!下一讲我们分析该例程时,还会告诉各位,将该例程稍加修改,就可以在你的局域网中对任何一台WIN2000以上的计算机进行进程管理!晕!那不是我就能在我的局域网中为所欲为了吗?别急,那当然是有条件的,微软当然不会允许未经授权就可以随便控制其它的计算机的。
    因为本人平时工作还是比较忙的,只有抽空就继续写,打算每星期写一讲,本次算是第一讲,下一讲主要结合本讲的例程,介绍在VB中使用WMI的基本方法。

二、VB中使用WMI的基本方法
    上一讲我们初步了解了一下WMI,并在VB中使用WMI对象做了一个进程管理器,为加深各位的对WMI对象引用方法的程序结构印象和增加兴趣,我们再举一个例子,就是枚举当前系统所有服务的名称、状态和启动类型等。与例程1一样,首先建立一个新工程具有Form1窗体,在菜单中的【工程】—【部件】下,添加“Microsoft Windows Common Controls 6.0”,在菜单中的【工程】—【引用】下,添加“Microsoft WMI Scripting V1.1 Library”,然后在Form1窗体上添加1个ListView1,在代码窗口添加如下代码(例程2):

Option Explicit
Dim objSWbemLocator As SWbemLocator
Dim objSWbemServices As SWbemServices
Dim objSWbemObjectSet As SWbemObjectSet
Dim objSWbemObject As SWbemObject
Dim strComputer As String, strNameSpace As String, strClass As String

Private Sub Form_Load()
Dim i As Long
    Me.Caption = "服务"
    ListView1.ColumnHeaders.Clear
    ListView1.ColumnHeaders.Add , , "名称", 2600
    ListView1.ColumnHeaders.Add , , "状态", 1000
    ListView1.ColumnHeaders.Add , , "启动类型", 1000
    ListView1.ColumnHeaders.Add , , "路径", 2600
    ListView1.ColumnHeaders.Add , , "登录身份", 1400
    ListView1.View = lvwReport
           
    strComputer = "."           '计算机名,.为本机
    strNameSpace = "root/cimv2" '指定命名空间为root/cimv2
    strClass = "Win32_Service"  '指定类为Win32_Service
   
    Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator")    '建立1个WBEM对象的引用指针
    Set objSWbemServices = objSWbemLocator.ConnectServer(strComputer, strNameSpace)  '连接到指定计算机、命名空间的WMI,返回一个对 SWbemServices 对象的引用
    Set objSWbemObjectSet = objSWbemServices.ExecQuery("SELECT * FROM " & strClass)  '通过WQL查询,返回指定类的所有实例

    For Each objSWbemObject In objSWbemObjectSet
        ListView1.ListItems.Add , "a" & i, objSWbemObject.DisplayName '将服务名称添加到ListView1第一列
        ListView1.ListItems("a" & i).SubItems(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.StartName '将服务的登录身份添加到ListView1第五列
        i = i + 1
    Next
    Set objSWbemObject = Nothing
    Set objSWbemObjectSet = Nothing
End Sub

    嘿,我们运行这个例程后,果然把自己计算机上所有的服务都列了出来,并且还知道这些服务目前的运行状态以及服务程序所在的目录和执行文件名。美中不足的是似乎我们不能停止或启动某个服务,不要急,其实可以很方便的就增加停止或启动某个服务的功能,但因为本节主要是要讨论VB中使用WMI的基本方法,因此故意省略了其它一些功能的实现。
    闲话少说言规正传,现在我们就开始讨论一下VB中使用WMI的基本方法。例程2与例程1相比,除了功能不一样外,精简了程序结构,因此我们就以例程2的结构为主进行讨论。
    我们先来观察一下例程1和例程2,可以看到它们都添加了“Microsoft WMI Scripting V1.1 Library”的引用,这个对象库(以下简称WMI脚本对象库)就是我们在VB的WMI编程中所要依赖的。再看一下程序的定义部分和整个程序的首次执行过程,它们基本相同:都定义了SwbemLocator、SwbemServices、SwbemObjectSet、SwbemObject对象;创建了SwbemLocator指针实例;通过SwbemLocator指针连接到WMI服务;检索一个 WMI 托管资源;枚举托管资源中的每个实例;显示各实例的一些属性。其实这些步骤对于任何用于检索 WMI 托管资源信息的程序来说都是共同的,下面我们就详细的分析一下各步骤。
    1)创建SwbemLocator指针
    创建SwbemLocator指针的目的是为了建立一个引用WMI对象的实例,然后用这个实例操作WMI。有没有注意到,例程1和例程2创建SwbemLocator指针稍有不同,例程1是在定义处用Dim objSWbemLocator As New SwbemLocator定义语句直接创建了,而例程2是在执行过程Form_Load中用Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator")语句创建的,其实可以将这条语句简化为Set objSWbemLocator = New SwbemLocator来创建。不管用哪种方式创建SwbemLocator指针,都不影响下面的步骤。SwbemLocator指针对象只有1个只读属性Security_(其实也是一个对象)和1个方法ConnectServer,第二步中我们就会讨论用此方法连接到WMI服务。
    2)连接到WMI服务
    要用WMI对象编程,必须连接到目标计算机的WMI服务,然后返回一个SwbemServices对象。比较一下例程1和例程2的连接方法,它们都用SwbemLocator指针对象的ConnectServer方法实现,不过例程1没有用任何参数,例程2中增加了strComputer和strNameSpace二个参数。ConnectServer方法共有8个参数,所有参数都是可选的,因此我们看到例程1中没有用任何参数,即全部使用缺省参数。下面我们看一下这8个参数:
    strServer 计算机名字,如果这台远程计算机与你不是同一个主域,需要完整的带域名的全称,例如:”cp1.xxxx.yyy”。缺省为本机,本机也可以用”.”
    strNamespace 需要登录的CIM命名空间,例如:"root/CIMV2"。缺省为"root/CIMV2"。(CIM就是一个存储库(架构),包括模型化托管环境和定义每个由 WMI 公开的数据块的对象储存库或类存储,WMI所有的类被分组到命名空间中,命名空间是表示一个特定的管理区域的类逻辑组,在这里就不深入分析了)
    strUser 用户名,一般必须为指定计算机上管理员帐号,也可以是指定主域的用户名,例如:"DomainName/UserName"。缺省为当前登录系统帐号的用户名。
    strPassword 口令(密码),对应用户名的口令
    strLocale 本地化代码,通常为空。
    StrAuthority 验证字符串,通常为空,如果不为空,则只能用于“Kerberos”或“NTLMDomain”。
    iSecurityFlags 延迟时间(注:中文解释为本人理解,似乎和英文意思不一样),当为0时,直到连接成功为止,这就有可能因为连接不成功时长时间等待。当为wbemConnectFlagUseMaxWait (数值为128),则确保不超过2分钟返回。
    ObjwbemNamedValueSet 为wbemNamedValueSet对象,在这里就不展开了。
    如果连接到本机,通常情况下只需要设置strNamespace参数,其它参数都可以省略,但如果连接到远程计算机,一般需要对前4个参数进行设置。
    这里要注意的是,连接到WMI服务是指连接到一个指定的CIM命名空间(如果还不能理解什么是CIM命名空间,也没关系,在后面的章节中还会讨论CIM命名空间),所以也可以认为本步骤就是:连接到一个指定的CIM命名空间,即使你不设置strNamespace参数,那也是连接到缺省的CIM命名空间,通常缺省的CIM命名空间为"root/CIMV2",你可以通过你计算机上的注册表查看或修改HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/WBEM/Scripting/Default Namespace项的值。
    3)获得WMI 托管资源(类)的实例集合
    当完成了连接到WMI服务后,我们就需要根据要执行的任务,选择不同的类(这些类都是WMI 的托管资源)。在上一步中,我们已经知道是连接到一个指定的CIM命名空间,而每个命名空间都驻留了许多类,本步骤就是根据要求获得我们所需类的实例集合。
    我们还是通过例程来讨论。在例程1中,我们通过SwbemServices对象的InstancesOf方法,参数为我们所需的"Win32_Process"类,然后返回由指定类名标识("Win32_Process")的托管资源的所有实例。而在实例2中,我们通过SwbemServices对象的ExecQuery方法,参数为"SELECT * FROM Win32_Service"(细心的读者可能发现参数好像与例程2中的并不完全相同,但你再看一下该语句执行前,已经对strClass变量赋予了"Win32_Service"值),然后返回的却是由指定类名标识("Win32_Service")的托管资源的所有实例。2个例程用了2个方法都获得指定类名标识的托管资源的所有实例,那可不可以调换过来用呢?当然可以!在例程1中,我们完全可以用Set objSWbemObjectSet = objSWbemServices.ExecQuery("SELECT * FROM Win32_Process")替换原语句,在例程2中用Set objSWbemObjectSet = objSWbemServices.InstancesOf("Win32_Service ")替换原语句。既然可以互相替换,那是不是说这2种方法是一样的呢?实际上这2种方法是有区别的。InstancesOf方法就象其名字所体现的一样,返回所有指定类实例,所以用起来非常方便。但方便往往与灵活和高效是矛盾的,假设您只想要实例或属性的一个子集的情形又怎么样呢?假设您想要优化实例和属性恢复到最小的网络流量。在这种情况下,您会非常高兴听到 WMI 支持大量强大的查询工具。
    查询 WMI 是向匹配一些预定义条件的托管资源发出请求的过程。例如,WMI 查询只要求那些处于 Stopped(停止)状态的服务,则例程2中,我们可以将该条语句改成:Set objSWbemObjectSet = objSWbemServices.ExecQuery("SELECT * FROM Win32_Service  WHERE State='Stopped'")就可以了。
    WMI 查询为检索托管资源实例及其属性提供了比 InstancesOf 方法更有效率的机制。WMI 查询只返回那些匹配查询的实例和属性,但是 InstancesOf 总是返回一个指定资源的所有实例以及每个实例的所有属性。同样,查询是在与对象路径中一致的目标计算机上进行的,而不是在运行的源计算机上。因此,WMI 查询可以显著地减少像 InstancesOf 那样更低效率的数据检索机制造成的网络流量。
    要查询 WMI,使用 WMI 查询语言(WQL)构造一个查询字符串。查询字符串定义了必须被满足的条件来获得成功匹配的结果。在查询字符串定义后,查询使用 SWbemServicesExecQuery 方法提交到 WMI 服务。满足查询的托管资源实例以 SWbemObjectSet 集合的形式返回。
    使用 WQL 和 ExecQuery 方法(而不是 InstancesOf)为创建只返回您感兴趣的项提供了灵活性。但我目前尚未获得一个完整的关于WQL查询语言的资料,只知道“WQL查询语言是一种类似于SQL查询语言,是SQL语言的一个子集”,但我在实际使用时,发现并不完全是SQL语言的一个子集,WQL语言还能用于建立事件,控制事件的响应速度等,不知道是否因为本人孤陋寡闻,在SQL中是不是有WITHIN、ISA等关键字?那位如有完整的关于WQL查询语言的资料(最好是电子的)能够提供,本人不胜感谢!
    好,讨论过了获得实例集合的方法,我们是不是还有一个疑问?参数中的"Win32_Process"和"Win32_Service"是什么?这些就是我们称作托管资源的类,大约有数千个不同的类,通常分为系统类、核心与公共类以及扩展类,我们作为应用人员角度,所关心的主要是扩展类,它们通常都在root/cimv2 命名空间中,大部分是一些以Win32_开头命名的类,大约有4、5百个。这些类可以通过微软网站的http://msdn.microsoft.com/librar ... i/wmi_reference.asp查看。关于这方面的问题,我们在后面还将做进一步的讨论。
    4)枚举实例并显示属性
    在上一步中我们获得了所需类的实例集合,接着我们就用For Each xxxx In yyyy 循环语句枚举每一个实例对象,要注意的是yyyy是一个集合,而xxxx是集合中的元素,而这个元素也是一个对象。在例程1和例程2中,objSWbemObjectSet对象就是由所有的单个实例对象组成的集合,在例程1中的单个实例对象是进程对象("Win32_Process"),例程2中的单个实例对象是系统服务对象(" Win32_Service ")。
    在循环体中,我们取出了每个实例对象(objSWbemObject)的一些属性添加到了列表控件(ListView1)中,比较例程1和例程2,它们各自的属性名称基本上都不相同,即使意义完全相同属性其名称也可能不同,例如例程1中属性ExecutablePath和例程2中属性PathName中的内容都是程序的路径,但属性名称却不相同。实际上,每个类都有各自的属性,在这里就不做深入的讨论了,在后面我们还将做进一步的讨论,并提供一个能够罗列出所有CIM命名空间、类、属性、方法的程序。
    在例程1中我们还使用了一个方法去终止进程,因为本节主要是讨论VB中使用WMI编程的基本步骤,所以不对此再进行分析了。大家只要记住VB中使用WMI编程的基本步骤为4步:
    1)创建SwbemLocator指针
    2)连接到WMI服务
    3)获得WMI 托管资源(类)的实例集合
    4)枚举实例并显示属性   
    本讲就到这里,下一讲再来做一个例程,并介绍另一种不完全相同的编程的步骤。由于本人计算机安装的是WIN2000系统,就WMI来说,WINDOWS XP和WIN2003的功能都增强了许多,有些例子在我这里无法实现,所举的例子也仅在WIN2000下做了测试,如执行例程时发生问题请告知。那你们可能会问“你为什么不用WINDOWS XP或WIN2003”?呵呵,因为本人无产阶级也,只能用D版的,而WINDOWS XP或WIN2003需要激活,尽管有解密的,但网上升级的风险很大,不定哪一天升级完后,被告知只能使用XX天必须激活,呜呼哀哉也。下一讲见。


三、VB中使用WMI的另一种方法
    上一讲我们借助例程2(枚举系统所有服务)讲解了VB中使用WMI编程的基本步骤,本节我们将再介绍另一种VB中使用WMI编程的基本方法,微软称做名字(Moniker)方法连接到WMI。我们还是通过一个例子来说明使用此方法的步骤,我们做一个枚举所有的快捷方式文件,并显示各快捷方式的名称、快捷方式文件所在路径和可执行文件所在路径。与例程2一样,首先建立一个新工程具有Form1窗体,在菜单中的【工程】—【部件】下,添加“Microsoft Windows Common Controls 6.0”,在菜单中的【工程】—【引用】下,添加“Microsoft WMI Scripting V1.1 Library”,然后在Form1窗体上添加1个ListView1,在代码窗口添加如下代码(例程3):

Option Explicit
Dim objSWbemServices As SWbemServices
Dim objSWbemObjectSet As SWbemObjectSet
Dim objSWbemObject As SWbemObject
Dim strComputer As String, strNameSpace As String, strClass As String

Private Sub Form_Load()
Dim i As Integer
    Me.Caption = "查找所有的快捷方式文件"
    ListView1.ColumnHeaders.Clear
    ListView1.ColumnHeaders.Add , , "快捷名称", 2600
    ListView1.ColumnHeaders.Add , , "快捷方式文件所在路径", 3000
    ListView1.ColumnHeaders.Add , , "可执行文件所在路径", 3000
    ListView1.View = lvwReport
           
    strComputer = "."           '计算机名,.为本机
    strNameSpace = "root/cimv2" '指定命名空间为root/cimv2
    strClass = "Win32_ShortcutFile"   '指定类为Win32_ShortcutFile
   
    Set objSWbemServices = GetObject("winmgmts://" & strComputer & "/" & strNameSpace)  '建立指定计算机、命名空间的WMI的SWbemServices 对象的引用
    Set objSWbemObjectSet = objSWbemServices.ExecQuery("SELECT * FROM " & strClass)     '通过WQL查询,返回指定类的所有
    Me.Caption = Me.Caption & "(共" & objSWbemObjectSet.Count & "个文件)"
    For Each objSWbemObject In objSWbemObjectSet
        ListView1.ListItems.Add , "a" & i, objSWbemObject.FileName '将快捷名称添加到ListView1第一列
        ListView1.ListItems("a" & i).SubItems(1) = objSWbemObject.Description '将快捷方式文件本身路径添加到ListView1第二列
        ListView1.ListItems("a" & i).SubItems(2) = objSWbemObject.Target '将可执行文件所在路径添加到ListView1第三列
        i = i + 1
    Next
    Set objSWbemObject = Nothing
    Set objSWbemObjectSet = Nothing
End Sub

    根据你计算机上文件的多少,程序的运行可能要等一点时间。运行的结果显示了所有快捷方式的名称、快捷方式文件所在路径和可执行文件所在路径。我们先比较一下例程3和例程2的程序定义部分和整个程序的首次执行过程,我们发现例程3的定义部分少了一个SWbemLocator对象的定义,对应例程2的步骤:1)创建SwbemLocator指针、2)连接到WMI服务;例程3用了:Set objSWbemServices = GetObject("winmgmts://" & strComputer & "/" & strNameSpace)这条语句,余下的语句似乎差不多。
    事实上用例程3的Moniker名字法与第二节介绍的利用SwbemLocator指针和ConnectServer方法连接到WMI服务的结果几乎是相同的,因此此后的步骤就完全一样了。那么我们来看一下通过Moniker名字法连接到WMI服务的编程要点:
    首先它调用了一个GetObject函数创建了SWbemServices 对象的引用(注意例程2是用CreateObject函数),调用参数是字符串"winmgmts://./root/cimv2",而这个字符串就是Moniker名字法连接到WMI服务的编程要点。在我们的程序中由三部分组成(其实完整的可以为五个部分)。
    第一部分为前缀:"winmgmts:"是前缀, 表示为WMI服务,必须使用;
    第三部分为计算机名字(是不是错啦?应该为第二部分,呵呵,没错第二部分在下面一段再讲):"//./"是计算机名字,中间的"."表示本机,可以用所连网络上任何的计算机名字,例如还有另一台计算机的名字为ComputerB,则用字符串"//ComputerB/",如果是本机也可以省略;
    第四部分CIM命名空间:"root/cimv2"就是CIM命名空间,如果是缺省的命名空间也可以省略,通常缺省的CIM命名空间为"root/CIMV2"。因此根据以上的说明,下面的写法效果是相同的:
      "winmgmts://./root/cimv2"
      "winmgmts://./"
      "winmgmts:root/cimv2"
      "winmgmts:"
    刚才讲到名字法字符串完整的为五个部分,那还有哪二个部分啊?好现在我们就来讲一下这余下的二个部分。在上面第一部分后应该还有个第二部分。
    第二部分为安全设置:结构为"{......}!",那中间部分是什么呢?我也说不清楚,用几个例子来说明吧。看名字法字符串"winmgmts:{impersonationLevel=impersonate,authenticationLevel=pktPrivacy,authority=ntlmdomain:mydomain,(debug,!security)}[locale=ms_409]!//MyComputerName/root/cimv2",我们可以看出安全设置部分为"{impersonationLevel=impersonate,authenticationLevel=pktPrivacy,authority=ntlmdomain:mydomain,(debug,!security)}[locale=ms_409]!",它表示身份模拟水平(impersonationLevel)为impersonate,验证水平(authenticationLevel)为pktPrivacy,验证权威(authority)为采用ntlmdomain验证方式通过名为mydomain的主域验证,具有debug特权,security前面怎么有个!呢?那表示没有security特权,[locale=ms_409]部分表示本地化代码为ms_409(即美国英语)。
    呵呵,够复杂的,凡是牵涉到安全肯定复杂,谁叫我们计算机界出了那么多的黑客、红客呢?还有那么多的木马、病毒。复杂也没办法,还得继续学啊,好,我们接着来看身份模拟水平(impersonationLevel)能取哪些值(懒得翻译说明部分了):
wbemImpersonationLevelAnonymous 1 Anonymous
说明:Hides the credentials of the caller. Calls to WMI may fail with this impersonation level.

wbemImpersonationLevelIdentify 2 Identify
Allows objects to query the credentials of the caller. Calls to WMI may fail with this impersonation level.

wbemImpersonationLevelImpersonate 3 Impersonate
Allows objects to use the credentials of the caller. This is the recommended impersonation level for Scripting API for WMI calls.

wbemImpersonationLevelDelegate 4 Delegate
Allows objects to permit other objects to use the credentials of the caller. This impersonation will work with Scripting API for WMI calls but may constitute an unnecessary security risk.(Windows NT 4.0 and Windows Me/98/95:  This impersonation level is not available.)

    验证水平(authenticationLevel)的取值:
WbemAuthenticationLevelDefault 0 Default
WMI uses the default Windows Authentication setting. This is the recommended setting that allows WMI to negotiate to the level required by the server returning data. However, if the namespace requires encryption, use WbemAuthenticationLevelPktPrivacy.

WbemAuthenticationLevelNone 1 None
Uses no authentication.

WbemAuthenticationLevelConnect 2 Connect
Authenticates the credentials of the client only when the client establishes a relationship with the server.

WbemAuthenticationLevelCall 3 Call
Authenticates only at the beginning of each call when the server receives the request.

WbemAuthenticationLevelPkt 4 Pkt
Authenticates that all data received is from the expected client.

WbemAuthenticationLevelPktIntegrity 5 PktIntegrity
Authenticates and verifies that none of the data transferred between client and server has been modified.

WbemAuthenticationLevelPktPrivacy 6 PktPrivacy
Authenticates all previous impersonation levels and encrypts the argument value of each remote procedure call. Use this setting if the namespace to which you are connecting requires an encrypted connection.

    特权的取值:
wbemPrivilegeCreateToken 1 CreateToken
wbemPrivilegePrimaryToken 2 AssignPrimaryToken
wbemPrivilegeLockMemory 3
wbemPrivilegeIncreaseQuota 4 IncreaseQuotaPrivilege
wbemPrivilegeMachineAccount 5 MachineAccount
wbemPrivilegeTcb 6 Tcb
wbemPrivilegeSecurity 7 Security
wbemPrivilegeTakeOwnership 8 TakeOwnership
wbemPrivilegeLoadDriver 9 LoadDriver
wbemPrivilegeSystemProfile 10 SystemProfile
wbemPrivilegeSystemtime 11 Systemtime
wbemPrivilegeProfileSingleProcess 12 ProfileSingleProcess
wbemPrivilegeIncreaseBasePriority 13 IncreaseBasePriority
wbemPrivilegeCreatePagefile 14 CreatePagefile
wbemPrivilegeCreatePermanent 15 CreatePermanent
wbemPrivilegeBackup 16 Backup
wbemPrivilegeRestore 17 Restore
wbemPrivilegeShutdown 18 Shutdown
wbemPrivilegeDebug 19 Debug
wbemPrivilegeAudit 20  Audit
wbemPrivilegeSystemEnvironment 21 SystemEnvironment
wbemPrivilegeChangeNotify 22 ChangeNotify
wbemPrivilegeRemoteShutdown 23 RemoteShutdown
wbemPrivilegeUndock 24 Undock
wbemPrivilegeSyncAgent 25 SyncAgent
wbemPrivilegeEnableDelegation 26 EnableDelegation
wbemPrivilegeManageVolume 27 ManageVolume(Windows 2000无效) 

    第五部分为类名部分:我们看一下"WinMgmts:{impersonationLevel=impersonate}!//ComputName/root/cimv2:Win32_LogicalDisk"这个Moniker名字法字符串,类名部分就是":Win32_LogicalDisk",不过要注意这个结果返回的不是一个SWbemObjectSet对象,而是一个SWbemObject对象,这个例子返回的是Win32_LogicalDisk类对象。
   
    本节介绍的Moniker名字法可以用于远程计算机的WMI服务连接,但不能用其它帐号(非当前登陆本机系统的帐号)和密码进行远程计算机的WMI服务连接。而Moniker名字法具有更灵活的安全设置和特权设置,同时也比SwbemLocator指针法更简洁些,许多用VBScripting写的WMI应用程序都采用Moniker名字法。
    呵呵,已经是4月2日0点多了,还要去干些其它的事,累啊,本讲就到这里吧。大家看到这里,是不是觉得似乎已经知道了怎么用VB对WMI编程,可又觉得并不清楚能用WMI的什么东西,不错我们在下几讲就介绍WMI中我们所关心的WMI结构,以及在WMI中我们到底能用些什么类,每个类都有那些属性、方法等。
四、了解WMI的体系结构

    上一讲我们介绍了用Moniker名字法建立WMI服务连接的方法,在这个方法中我们讲了Moniker名字法的连接字符串由5部分组成:前缀:"winmgmts:";安全设置:"{......}!";计算机名字:"//ComputerName/";CIM命名空间:"root/cimv2";WMI类名:":WMIClassesName"。因为时间原因,没有举出相应的例子,本节内容比较少,又比较枯燥,所以我们举两个例子说明一下Moniker名字法的连接字符串使用方法。首先我们来看一个关机的例程(本论坛中已经有很多帖子讨论过这个问题,一般采用API方法和命令方法,这里给出1个WMI方法),与上面的例程一样,首先建立一个新工程具有Form1窗体,在菜单中的【工程】—【引用】下,添加“Microsoft WMI Scripting V1.1 Library”,然后在代码窗口添加如下代码(例程4):

Option Explicit
Dim objSWbemServices As SWbemServices
Dim objSWbemObjectSet As SWbemObjectSet
Dim objSWbemObject As SWbemObject
Private Const WMI_LOGOFF = 0& '退出(注销)
Private Const WMI_SHUTDOWN = 1& '关机
Private Const WMI_REBOOT = 2& '重启动
Private Const WMI_FORCE = 4& '强制退出,即不通知现在活动应用程序让其先自我关闭
Private Const WMI_POWEROFF = 8& '关闭电源
'Dim strComputer As String, strNameSpace As String, strClass As String

Private Sub Form_Load()
    Set objSWbemServices = GetObject("winmgmts:{impersonationLevel=impersonate,(Shutdown)}!")
    Set objSWbemObjectSet = objSWbemServices.ExecQuery("Select * from Win32_OperatingSystem")
    For Each objSWbemObject In objSWbemObjectSet
        objSWbemObject.Win32Shutdown WMI_POWEROFF, 0&   '关闭电源
    Next
End Sub

    例程4通过WMI类Win32_OperatingSystem的Win32Shutdown方法关闭计算机,我们可以看到Moniker名字法的连接字符串只包含了2个部分:前缀:"winmgmts:";安全设置:"{impersonationLevel=impersonate,(Shutdown)}!",即身份模拟水平(impersonationLevel)为impersonate(=3),特权的取值为Shutdown(=18)。省略了:计算机名字、CIM命名空间,也没有直接建立WMI类。实际上"winmgmts:{impersonationLevel=impersonate,(Shutdown)}!"与"winmgmts:{impersonationLevel=impersonate,(Shutdown)}!//./root/cimv2"是完全一样的(当然前提是CIM缺省命名空间为root/cimv2)。对这个程序稍加修改,还可以实现退出(注销)、关机(不关闭电源)、重启动等,如果具有远程计算机的管理员权限,还可以关闭远程计算机。
    下面我们再举1个例子。我们看到前面的例程2(枚举系统所有服务)中是利用WMI类Win32_Service获得系统所有服务对象实例,并且通过读取DisplayName、State、StartMode、PathName和StartName将服务名称、服务的状态、服务的启动方式、服务程序的路径和服务的登录身份显示到列表中,那Win32_Service究竟有那些属性呢?当然我们可以通过微软的资料查阅出每个WMI类的各项属性,其实我们也可以通过编程列出各个WMI类的各项属性,这个例子就是用来列出Win32_Service的所有属性的名字。与上面的例程一样,首先建立一个新工程具有Form1窗体,在菜单中的【工程】—【引用】下,添加“Microsoft WMI Scripting V1.1 Library”,然后在代码窗口添加1个List1控件,代码如下(例程5):

Option Explicit
Dim objSWbemObject As SWbemObject
Dim objSWbemProperty As SWbemProperty
Dim strComputer As String, strNameSpace As String, strClass As String

Private Sub Form_Load()
    strComputer = "."           '计算机名,.为本机
    strNameSpace = "root/cimv2" '指定命名空间为root/cimv2
    strClass = "Win32_Service"  '指定类为Win32_Service
    Me.Caption = strClass & "类的属性名称"
    Set objSWbemObject = GetObject("winmgmts://" & strComputer & "/" & strNameSpace & ":" & strClass)   '建立指定计算机、命名空间、指定类的引用

    For Each objSWbemProperty In objSWbemObject.Properties_
        List1.AddItem objSWbemProperty.Name  '将Win32_Service的属性名称添加到List1
    Next
    Set objSWbemObject = Nothing
    Set objSWbemProperty = Nothing
End Sub

    例程5运行后将列出Win32_Service的所有属性的名字,利用这个例程只要用其他的WMI类(例如前面已经提到过的Win32_Process或Win32_OperatingSystem)替换Win32_Service,就可以得到对应的WMI类的所有属性的名字。这里我们还是来看一下Moniker名字法的连接字符串包含了4个部分,与前面不同的是我们这个例程中出现了第5部分:类名":Win32_Service",因此GetObject返回一个Win32_Service类对象,然后我们枚举这个对象的Properties_属性(实际上是一个集合,包含了SWbemProperty对象)中的objSWbemProperty对象的Name属性(这是1个字符串)。这句话是在有些拗口,意思可能也不容易一下子理解,不过我们后面还会进一步讲述这个问题。例子就讲这些了,接下去我们还是回到我们本节的主题,首先我们看看WMI的体系结构。
 

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/cw198624/archive/2009/02/19/3911759.aspx

原创粉丝点击