VBGood网站全文搜索 Google

搜索VBGood全站网页(全文搜索)
首页 - 经验之谈 - 用Windows管理技术做你的应用程序
发表评论(0)作者:Jeffrey Cooperstein, 平台:VB6.0+Win98, 阅读:10697, 日期:2000-10-15
本文假定读者对COM,Windows,脚本程序较为熟悉。

本文的代码为:WMIOverview.exe (37KB)

摘要  本文为读者提供了关于Windows管理操作系统方面的综述,这是一项通过调用标准API函数,来获得各种系统及设备信息的技术。使用WMI,管理信息通过常规信息模式(CIM)中的面向对象结构得以陈列,这些结构依赖于分级结构的再使用,以及表征系统设备的标准对象类型。本文简单介绍了如何使用查询语言对WMI进行查询,以获取关于存在的系统类型,处理系统事件和WMI的安全性的信息,该查询语言就是类似于SQL的所谓的Windows管理操作查询语言(WQL)

现在,面临企业管理人员的一个挑战是,计算机管理网络日益庞大,复杂。Windows® 管理操作系统(WMI)是一项功能强大的技术,它可以同时管理一台机器,或是上万台机器。不幸的是,WMI或许是近年来Microsoft保密工作做得最好的,不外泄的技术之一。它远没有其他,诸如Active Directory™或Windows DNA等技术受到公众的关注。本文将系统的介绍WMI,以便你能获得一些的背景知识,并且使用WMI编写自己的应用程序。也许用不了多久,你就会觉得WMI应该象COM, Win32®, 或 Microsoft一样成为应用程序开发者的日常词汇了。

概述

WMI是Microsoft用来支持企业管理系统而引进的几项技术之一。 Active Directory提供了企业范围内的地点、政策和组织管理信息。Microsoft® 管理控制台系统(MMC)是编写应用管理程序时的标准框架。Windows Script Host系统(WSH)允许管理人员自动处理复杂的任务。最后,WMI是基于Windows操作系统,及其应用程序的一项远程控制的技术。

WMI允许应用程序的开发者,使用简单的、一致的机制,去查询企业中的任一台计算机上的信息,或是进行系统配置。通过WMI接口可以获得的信息量是惊人的,包括硬件设置,状态信息,驱动器配置,BIOS信息,应用程序的设置,事件记录信息,以及其他。WMI通过一组API来获得信息,但它表征的是一种通过一个简单,工业标准对象管理模式来获取信息的函数。这使得应用程序的开发者不必学习Windows的每一个API的具体细节。

要理解这样做的效果,假定应用程序的开发者想枚举出机器上四种不同类型的对象:服务,指针,过程和CPU。如果没有WMI系统,该开发者只好寻求不同的API来完成不同类型的枚举;但是使用WMI系统,这就变得相当简单了,这是因为每一对象类型都是以同一种方式枚举的。例如图1(Figure 1)显示了,用VBScript代码编写的WSH系统完成此任务是如何的简单。

如果你运行的是Windows 2000,你可以将这段代码拷贝到ENUM.VBS文件中,并且在控制台窗口运行cscript.ENUM.VBS命令。如果遇到错误,最可能的原因是,为了使该程序更短而忽略了对空指针的查错。例如,若系统不存在已安装指针的备注列表,obj.Description文件将返回空指针。

如果,你运行的操作系统是Windows 9x或是Windows NT® 4.0,WMI控件是可选组件。在Microsoft的网站上可得到最新版本的WMI。

显然,图1中的程序也可以用来枚举远程机器上的同样信息。要显示MYSERVER的信息,只需使用WinMgmts://MYSERVER在首行作为GetObject的参数。实际上,不论是从本地或是从远程的机器,WMI得到的信息,都是使用同一个API得到的。虽然某些Win32 API提供辨认远程计算机的功能,其他的API只能访问本地的机器,无法实现从远程计算机上得到信息。

WMI还能使对被管理对象的信息进行设置,以及调用变得简单。可以采用同样的语法结构来进行中止服务,结束进程,或是从远程计算机注销用户,等操作。应用程序的开发者只需知道对象的名称,以及准备进行的操作的名称,而没有必要了解一个新的API。

为完成所有事情,事件的基础结构将对通过WMI可以访问的对象给出修正信息。在本文的稍后部分,我将举一个创建和撤销监视器进程的例子;另一个例子是当插入或取出软盘时产生一个提示信息。WMI对象管理各类型操作采用的是同样的函数。甚至当基本API不提供针对对象的此类信息,WMI体系结构可以模拟监控这些事件的行为。另外,WMI服务自由的提供所有这些功能的远程访问。

除了WMI已经列出的信息,应用程序还可以列出他们自定义的对象和事件。这样,任意WMI管理程序就能管理你的其他应用程序,包括远程配置,修改提示信息,或是接受自定义事件。

WMI技术的起源

虽然WMI是Microsoft开发的技术,但是它是建立在近年来才引入的工业标准的基础上的。要了解WMI,就需要了解它从最初的所谓企业网络管理系统(WBEM)的演化过程。几年以前,为建立企业系统和设备管理系统的标准体系,一些计算机公司创建了WBEM系统。最初的目的是,开发单一的管理企业任意网络组件标准体系。这将简化由诸如SNMP网络设备标准体系,DMI桌面标准体系,等多个独立体系存在而引起的问题。将来,计算机公司开发与WBEM相配套的硬件,软件,以及操作系统,都将能以同样的方式进行管理。这样,单个管理程序就将能轻易地管理企业范围内的所有不同类型的组件了。实际上,WBEM的最初功能设计是针对(Desktop Management Task Force (DMTF)的。DMTF系统是针对维持实现WBEM系统最初目标所需的标准系统而设计的。

CIM类

最初的WBEM系统的第一个标准是,所谓的常规信息模式(CIM)的管理信息描述函数。CIM采用面相对象函数模拟信息,使用分类定义,分级结构,事例,属性,以及函数描述管理信息。CIM类是通过管理对象格式(MOF)在文本文件中定义的。完整的MOF文件格式系统已经超出了本文的范围,但是,如果你熟悉C++,JAVA,或是接口定义语言(IDL),则你将对它的语法结构相当熟悉。以下就是如何定义一个类,及其子类的一个例子:

  
[abstract] class Automobile

{    

[key] string Make;    

[key] string Model;    

void Recall();

};

class Car : Automobile

{    

real32 BlueBookValue;

};

class Truck : Automobile

{    

uint32 Axles = 2;

};

以上的MOF文件定义了Automobile作为一个基本类,及其两个子类:Car和Truck。Automobile具有Make属性,Model属性,以及Recall函数。Car具有BlueBookValue的额外属性。Truck具有Axles属性,默认值为2(对于Axles而言,如果不另外指定,Truck每一次得到的新的值是2)。括号里的文字(abstract和key)引入了CIM分类参数的概念。分类参数可以应用于整个类,属性,函数或是单独的函数参数。分类参数与IDL文件中属性的概念类似。它们提供了与类,属性和函数的使用相关的额外信息。在这个例子里,abstract类的分类参数意味着,你无法看到Automobile的示例。另一方面,由于Car和Truck不是abstract类,你可以列举Car和Truck的例子。Key属性分类参数意味着任意Automobile(或任意派生类)可以被参数Make和Model唯一确定。这是一个从数据库借用的概念。所有支持多种事例的类需要确定Key属性,即每一种事例必须是Key属性值的唯一组合。换句话说,即使Automobile类定义只允许存在唯一的Ford Mustang事例,但是你可以创建Ford Taurus,甚至Gm Mustang的事例。如果类有且只有一种事例,它可以用单独的一个类的分类参数来描述。在这种事例下,类不再需要确定诸如Key之类的其他属性了。MOF文件格式是用来描述事例或类定义的,以下的示例描述了Truck的一种事例:

instance of Truck

{    

Axles = 5;    

Make = "Ford";    

Model = "Big Rig";

};

单独的CIM标准体系还不足以使公司能创建可管理的对象,并且防止出现混乱。例如,一个公司将路由器称为网络路由器,而另一个公司将同一个东西称为路由器。甚至更糟的是,两个公司都可以定义同样的类的名称,但属性不同。为防止出现这种混乱,DMTF体系定义了CIM模式。模式是一个所有公司都统一遵循的一个有明确定义的类的集合。通常,作为CIM模式体系一部分的类,以CIM_为前缀(例如,CIM_Battery或CIM_Process)。CIM模式倚重于对分级结构的使用,允许管理程序以同样的方式处理相似对象的群。例如,CIM_Battery,CIM_Printer,以及CIM_Processor都是从基本类CIM_LogicalDevice派生得到的。这要求管理程序了解,如何通过CIM_LogicalDevice来管理有此类派生得到的任意类型的设备。此外,任意派生类都支持基本类的属性和函数。类CIM_LogicalDevice具有确定是否支持电源管理程序的属性(PowerManagementSupported),以及允许设置电源状态的函数(SetPowerState)。通过从基本类功能列表得到的信息,应用程序可以管理打印机,处理器,以及其他任意从CIM_LogicalDevice派生的设备的电源状态。

分级结构的另一个好处是,如果计算机分销商们能够提供比CIM类定义的更多信息的话,他们能够扩展CIM_schema。例如,Windows操作系统知道已安装的打印机AveragePagesPerMinute参数。类CIM_Printer没有定义此信息,因此Microsoft由CIM_Printer派生定义了类Win32_Pointer,此类具有额外属性,AveragePagesPerMinute。知道类Win32_Proiner的管理程序可以使用这个信息,而只知道如何管理CIM_Pointer的管理程序也能正常工作。

WBEM支持

CIM和MOF标准体系,以及CIM模式是最初的WBEM体系的核心组件。这些标准体系是不依赖于操作平台和实现的应用程序的。为使统一的管理成为现实,硬件和软件的分销商们必须创建支持这些标准体系的系统(指的是实现WBEM的应用程序)。这包括通过由CIM模式派生出来的类的功能列表。Microsoft在Windows操作系统上实现WBEM的应用程序是WMI。

并不只有Microsoft支持WBEM系统,硬件厂商也同意为实现WBEM系统提供网络以及存储设备。Sun公司许诺为实现WBEM提供Solaris操作系统,诸如Tivoli Systems和Computer Associates等管理软件的主要厂商也同意在他们的产品中支持WBEM系统。有了这些软硬件方面的支持,以一种单一的,一致的方式管理企业将成为现实。

WMI体系结构

了解图2所示的WMI组件的体系结构,可以帮助分析WBEM标准体系实现方式的创建。首要的任务是,确定CIM schema中的哪些组件在计算机中运行Windows。第二步是通过将Windows下列出的所有额外信息定义为类,以扩展CIM schema。Microsoft使用的约定类是CIM类派生得到的,类的前缀为Win32_(例如,Win32_NetworkAdapter就是由CIM_NetworkAdapter派生得到的类)。最终,你仍需要编写某些软件,来实际提供管理功能。保留已有的被支持类的列表是必须的,并且当类的不同事例需要时,还需要提供某些实际数据;通过完成所要求的操作,来响应函数对对象的调用也是必须的。在最初的术语中,这被称为CIM对象管理程序(CIMOM)。在Windows NT中,CIMOM处于WinMgmt服务包中,通过WinMgmt.exe文件得到实现。虽然Windows 9x不支持Windows NT服务包,CIMOM仍可通过可执行文件WinMgmt.exe得到实现。

在某些地方,你需要一个API来讨论CIMOM。你需要能够查询schema,枚举事例,以及调用函数。虽然,CIM标准体系定义的内容可以包括在类中,但并不存在这些内容的二元表示法的定义,或是用来访问这些内容的机制。你可以随意的使用动态连接库,COM,sockets,有名管道,等等。Microsoft选择通过一组DCOM接口列出CIMOM的功能。所有这些接口都是以Iwbem为前缀的(例如,IwbemServer或是IwbemClassObject),在SDK平台中都有存档。这些接口使得,编写对WMI管理的类和事例进行操作的管理程序成为可能。

通过选择使用DCOM,Microsoft本质上可以通过远程计算机访问CIMOM的任意接口,而不用编写额外的代码。但实际上,将每一次接口调用传送到远程计算机的额外开销就可能使客户机程序瘫痪。此外,由接口返回的信息中带有大量的冗余信息,是对带宽的一种极大的浪费。例如,每一个WMI对象包含了让计算机知道对象所在的属性。返回对象阵列的接口函数,在每一个元素中都含有同一计算机名;依靠DCOM来排列数据,将导致计算机名被反复的传送。为改善性能,Microsoft开发了高级用户代理和存根程序,来压缩信息,是这种额外开销减小到最小。虽然,开发者们也许并不知道这些代理和存根程序的存在,但这些程序是针对远程连接的不良性能而编写的。

一旦,你定义了允许访问CIMOM的DCOM接口,下一步就是创建一个包括所有分类定义的数据库。此外,你需要存储大量的,诸如WMI自身的安全性能设置的静态信息,这些静态信息是不允许被改变的数据,除非是CIMOM对它们的直接操作,或是间接的通过对DCOM接口的调用而进行的。这包括静态的事例数据,以及分类定义,并且允许系统配置信息以静态信息在注册表内的同样方式进行存储。包含这些静态信息的数据库被称为CIM对象库,一个\winnt\system32\wbem\respository目录下的文件。

为提供动态对象的信息,WinMgmt服务包支持WMI信息提供程序的记录。WMI信息提供程序实际上就是,支持在SDK平台上描述的一组定义明确的接口的COM服务器。对于动态信息,WMI信息提供程序可以用CIMOM命令进行查询,或是直接调用CIMOM以告知数据的改变。WMI信息提供程序最常用的功能是用来提供动态事例信息,激活进程的列表;它也可以用来在模式中动态描述类的信息提供程序程序,或是创建事件信息的信息提供程序程序(事件体系结构将在稍后的内容中讨论)。WMI装载了在模式中为大量类提供信息的信息提供程序(如图3所示)。开发者可以通过定义自定义类,以及编写提供动态事例信息的信息提供程序程序,来拓展由WMI所提供的信息。

由WinMgmt服务包提供的DCOM接口足以使C++应用程序开发者编写应用管理程序,但不幸的是,不是所有的开发环境都支持访问任意COM接口。为从支持自动对象的环境中访问WMI,Microsoft提供了直接与DCOM接口转换的应用程序对象。在DCOM接口与应用程序对象之间存在着直接的映射关系。例如,SwbemObject转换IwbemClassObject,以及SwbemServices转换IwbemServices。虽然命名系统不是唯一的,但是熟悉一个接口的人很容易辨认和了解其他接口事例。应用程序对象允许通过Visual Basic®,Visual Basic的应用程序(VBA),WSH, VBScript, JScript®,ASP,或是支持自动对象的其他环境,对WMI进行完全访问。在参考查询体系中加入WMI Scripting V1.1 Library ,则Visual Basic或是VBA方案就可以访问这些对象了。支持ActiveX®程序的操作平台可以通过对象类的代号,或是类的名称创建这些对象,这些对象的前缀是WbemScripting,如WbemScripting.SwbemLocator。

Microsoft提供的另一个组件返回访问DCOM接口,是创建类,属性,类似于数据库表格,行或列的事例列表的ODBC适配器。若将WMI数据写入Microsoft Excel电子表格,ActiveX数据对象(ADO)程序,或是其他数据库驱动程序,该适配器非常有用。

本文的剩余部分将讨论的是,通过DCOM接口,或是应用程序对象,对WMI的使用问题。这两类函数都提供对WMI服务包的完全访问,而ODBC适配器当前只支持WMI功能的一部分。虽然并没有涉及到XML使用方面的内容,但它却是需要关注到重要特征之一。正如前面所提到的,初始的WEBM没有指定使用某项特定的技术来访问CIM对象。Microsoft选择使用DCOM接口,而其他的操作系统或硬件设备似乎无法使用此接口;然而,XML提供HTTP连接协议可以被用作接入WBEM系统的标准方式。实际上,DMTF系统已经起到了为实现WEBM系统创建XML/HTTP协议的作用。在今后推出的硬件设备和操作系统都将支持通过XML和HTTP。

名称空间

WMI系统支持将类的集合分为所谓的名称空间的逻辑单元。名称空间类似于驱动器中的文件夹,而分类定义就类似于文件夹中的文件。图4 显示了在SDK平台上如何使用WMI工具表示名称空间。每一台计算机在分级结构的顶层都有一个有明确定义的名称空间,即所谓的根名称空间。名称空间的所在用路径来描述,这类似于子目录(如root\cimv2或root\directory\LDAP)。每一个名称空间都包括自己的类和事例的集合。为唯一确定某一个类,名称空间也必须是唯一确定的(如,root\cimv2:Win32_Processor)。在一个名称空间之中,所有的类必须是唯一的(正如文件在目录中的情况类似),但是在不同的名称空间中,不相关的类可以有相同的名称。分级结构是CIM中的一个关键组成部分,并且存在一个限制,所有希哦那个一个基本类派生出来的类,必须在同一个名称空间中定义。换句话说就是,如果你想从Automobile派生出一个类Car,类Automobile必须是在同一个名称空间中定义的。



Figure 4Exploring namespace

如文章的前段已经提及过的,初始的WBEM系统的一个核心组件是支持在CIM模式中定义的类。在WMI系统中,这些类可以在root\cimv2 名称空间中被找到。CIM模式在不断的更新,每次发布的都用一个版本数来描述。rootv2\cimv2 名称空间 的V2指的是CIM第二版模式。这些模式储存在其他名称空间中,诸如与WDM提供的信息相关的root\WDM,或是与Active Directory信息提供程序提供的信息相关的root\directory\LDAP。实际上,除非你通过派生已有的类来拓展一个模式,否则最好在自己的名称空间中创建新的类。

系统类和系统属性

每一个名称空间都包含有一组公用类,即所谓的系统类。所有的系统类都以双下划线为前缀(例如,__Provider)。在每一个名称空间中都有一个有趣的类,__namespace类;这个类有一个独立的属性,即名称属性。枚举这个类的事例,为查询子名称空间提供了一种机制。例如,根名称空间在名称属性中有一个为cimv2设置的事例,__namespace。其他的系统类用于相对复杂的WMI编程,诸如自定义信息提供程序注册程序等,在本文中没有提及。

除了系统类之外,每一个类也有一组标准系统属性;并且它们也是以双下划线为前缀,很容易确认。这些属性是由WMI系统自动生成的,并且是只读的。他们存在的主要目的是为了让用户程序动态的确定对象的标识。在MOF文件中描述类或事例的定义时,这些属性被忽略了。图5 显示了系统属性的一个列表。

对象路径

在文章的前面部分提到的描述名称空间和类的语法结构,引入了对象路径的概念。对象路径就是用来确认计算机,名称空间,类,以及事例的函数。对象路径的绝对形式确定了计算机,和名称空间。例如,\\jeffc\root\cimv2:Win32_Process指的是,分类定义Win_32Process在当前计算机上的root\cimv2名称空间中。路径Win32_Process指的是分类定义在当前计算机,以及当前名称空间中。

对象路径也可以用来确定事例和分类定义。要确定一个事例,就要确定类的名称后的关键属性,以及属性的值。相对对象路径Win32_Environment.Name="TEMP",UserName="JEFFC\\Administrator"指的是事例Win32_Environment的Name属性为TEMP,UserName属性为JEFFC\Administrator。需要注意的是当引入对象路径时,“\”表示为“\\”,(正如C++或是Java语言)。在单一类(没有关键属性,有且只有一个事例)中,含有类名称的对象路径以=@后缀,如\\jeffc\root\cimv2:SingletonTest=@。

理想情况下,对象路径是完全与具体的访问函数无关的;这即意味着计算机名,名称空间名,类名,属性名,以及属性值都是与具体的访问函数无关的。但实际上,某些编写得不完善的应用程序,对访问属性名和属性值的函数有要求。若遇到这种情况,则信息提供程序程序将作为一个错误来处理。除了在编写信息提供程序程序时需要当心以外,开发者们还必须注意,对对象路径进行的任何操作都是与具体访问函数无关的。

WMI控件编程

现在到了该讨论用WMI控件编写管理应用程序的具体细节的时候了。用WMI控件编写任何应用程序的第一步是,使用CoCreateInstance和CLSID_WbemLocator得到一个IwbemLocator指针。如果你使用的是脚本接口,则你创建的是WbemScripting.SwbemLocator对象。从现在开始,我将举出一些使用JScript和脚本接口创建对象的具体示例,这将对这些示例阅读和理解变得更加简单。在适当的时候,我将提到等价DCOM接口,因此,它也应该能很容易的转换成C++代码。

输入定位器程序允许你通过ConnectServer函数,请求连接到WMI控件服务上。ConnectServer会要求你确认计算机和名称空间名,若不填的话,ConnectServer将连接到本地计算机的默认名称空间上。ConnectServer返回一个SwbemServices对象(或是WbemServices接口)。WbemServices提供与CIMOM控件接口之间的通讯,但它只能访问由ConnectServer确定的名称空间内的对象。若要访问另一个名称空间内的对象(即使是存在于同一台计算机上的对象),你也需要用从ConnectServer的另一次调用而得到的不同的SwbemServices对象。SwbemServices提供了许多访问WMI控件对象的函数,但最简单的是Get(或是IwbemServices:: GetObject)。这允许你通过使用对象路径得到一个分类定义,或是特定的事例。图6 显示了一个这样做的示例。

在图6 中,你会注意到Get函数是用来恢复Win32_LogicalDisk分类定义,或是C:驱动器的Win32_LogicalDisk事例的。在这两种情况中,你可以返回一个SwbemObject(或是使用IWbemServices::GetObject返回一个IwbemClassObject接口)。SwbemObject可以使你能访问对象的完整定义,包括属性,值,函数,以及限定词。脚本对象将CIM对象属性动态的加入由SwbemObject支持的标准属性中。如果你实现知道你在查找的属性,这将使你的变得更容易。以下的代码显示如何访问,你先前得到的C:驱动器事例的FreeSpace属性:

//显示C:盘的自由空间
WScript.Echo(diskinstance.FreeSpace);

属性名称和值也可通过Properties_ collection来访问。

//用一个“Enumerator”函数来显示的所有的属性和值//在VBScript中,这可以通过定义“FOR EACH”来完成var f=new Enumerator(diskinstance.Properties_)for (;!f.atEnd();f.moveNext ()){  var prop = f.item();  WScript.Echo(prop.Name + " = " + prop.Value);
}

函数也是用同样的方式来访问,可以通过Methods_ collection函数来访问函数,并且它们是直接附加在SwbemObject自身之上的。

如前面所提到的,每一个对象都有一组系统属性,允许对象的标识能被动态的确定;通过SwbemObject的Path_property函数可以访问这些系统属性。Path_property函数是一个SwbemObjectPath对象,具有同每一个系统属性相对应的一组属性。命名协议对于系统属性的名称而言不是唯一的,但两者之间的映射关系是显而易见的。下面的函数显示了如何动态的确定一个对象是类还是事例:

function IsObjectAClass(obj){  //IsClass函数在内部使用了系统属性__Gunus  //确定对象是类还是事例  return obj.Path_.IsClass;
}

直到现在为止,这些示例都假定你知道你想要校验的类或事例的对象路径。SwbemServices对象也允许对类或事例进行动态查询。类的列表可由SubclassesOf函数来确定,这种函数返回给定类的子类,若没有指定类作为参数,则返回一个基本类的列表。为事例能够进行动态恢复,WMI控件借用了一项数据库的技术;它使用了SQL的一个变形,即所谓的WMI查询语言(WQL)。下面就是一个简单的WQL查询,恢复所有逻辑驱动器的列表。

SELECT * FROM Win32_LogicalDisk

如同SQL一样,可以通过返回一组特定属性,或是满足某些条件的事例的子组,改进WQL查询。在SDK平台中有WQL查询的完整语法结构存档,但是,某些示例有助于显示WQL功能。下面的示例返回所有停止的服务列表:

SELECT * from Win32_Service WHERE State="Stopped"

下一个查询只返回所有逻辑磁盘的Name和FreeSpace属性:

SELECT Name,FreeSpace FROM Win32_LogicalDisk

可以使用SwbemServices的ExecQuery函数来执行WQL查询。ExecQuery函数返回的是,一个符合查询标准的对象的集合。下面的示例将输出每一个逻辑驱动器的名称,自由空间,以及总空间的大小:

// Sample2.jsvar locator = new ActiveXObject("WbemScripting.SWbemLocator");var service = locator.ConnectServer();var props = service.ExecQuery("SELECT Name,FreeSpace                              FROM Win32_LogicalDisk");var f = new Enumerator (props);for (;!f.atEnd();f.moveNext ()){  var p = f.item ();  WScript.Echo(p.Name + " - " + p.FreeSpace + " - " + p.Size);
}

如果你在控制台窗口运行以上的脚本程序(使用的命令是cscript SAMPLE2.JS),输出将如下:

A: - 18432 - undefinedC: - 1079707648 - undefinedD: - 1006243840 - undefinedE: - 553623552 - undefined
Z: - 4573265920 - undefined

每一行将显示驱动器的名称,自由空间,以及驱动器的大小,但是你会注意到驱动器的大小被略去了。如果你仔细的查看以上的WQL查询,你会注意到我只查询了Name和FreeSpace属性。由于我没有查询Size属性,我不保证返回对象会包含这个属性。对WQL查询限定某些属性能大大提高执行效率,这是因为信息提供程序可以略过某些难以查询的属性,但是它有可能返回只包含部分信息的SwbemObject对象。如果不当心,这有可能成为棘手错误的一个来源。

SwbemObject对象还提供了返回对象集合的集中函数(如InstancesOf,ReferencesTo,以及AssociatorsOf)。如果你使用的是IwbemServices接口,你可能注意到了这些函数并不存在。这是因为另外的SwbemServices函数在幕后,实际生成WQL查询;这个函数存在于脚本对象之中,作为使脚本程序更合理的一个便利函数。调用InstancesOf(SomeClass)或是ExecQuery(SELECT * FROM SomeClass)不存在运行上的差异。

你也许已经注意到了,第一个示例使用GetObject来恢复SwbemServices对象。其他示例使用了两个步骤的过程,先调用ConnectServer,然后再创建SwbemLocator。GetObject使用了类似于COM空间开发者的概念,即所谓的monikers。WMI系统支持大量的monikers,使直接访问SwbemServices和SwbemObject对象的类或事例成为可能。下面的Jscript显示了使用monikers访问Alerter服务的一个示例:

var alerter = GetObject("WinMgmts:Win32_Service=\"ALERTER\"");

没有monikers的等价函数要求三个步骤:

var locator = new ActiveXObject("WbemScripting.SWbemLocator");var service = locator.ConnectServer();
var alerter = service.Get("Win32_Service=\"ALERTER\"");

使用带有标记的GetObject,对于大量简单任务而言非常便利。然而,对统一名称空间中的对象重复使用标记的效率,比重复使用单个的SwbemServices对象要低。WMI monikers支持的所有选项的完整列表,处于SDK平台中的“Object Creation and Monikers”之中。现在,该讨论某些能浏览从WMI控件得到的信息的工具了。WMI控件装载了所谓的WBEMTest.exe的测试程序。这是一个功能强大的应用程序,因为这个程序能提供对WMI系统中的几乎每一个DCOM接口的访问,但是它不容易被新手所理解。WMI SDK装载了一套使浏览WMI控件更容易的工具。WMI SDK可从http://msdn.microsoft.com/downloads/sdks/wmi/default.asp得到。WMI SDK的完整内容也可从SDK平台上得到,但是某些工具不是默认安装的。你必须仔细查看安装选项,安装那些WMI控件工具。



Figure 7CIM Studio

CIM Studio是WMI SDK中查看模式的最有用的工具之一(如图7)。左边的窗格显示了名称空间中类的分级结构树;右边的窗格显示了类或事例信息。CIM Studio还允许你进入WQL查询系统,并进行结果校验。

安全性

由于WMI系统是一个服务程序,在计算机上,它可以作为无限控制的本地系统运行。当用户提出请求,实际上是这个服务程序调用Win32 API来得到信息,或是完成某项操作。这是否意味着你可以通过WMI系统,并精心绕过你的用户帐号上的常规Windows NT安全系统,来得到信息?这绝对是不可能的。

无论何时建立一个连接,你的用户凭证将通过优先的DCOM基础结构输入WMI系统。在处理任何请求之前,WMI系统将处理用户凭证。这保证了,如果你在其他途径下无法进行的操作,在WMI系统中也无法执行。考虑调用Win32 DeleteFile函数,以及使用Win32_DataFile的DeleteFile函数的情况:如果你调用Win32 DeleteFile函数,Windows NT就会对确定你拥有适当的许可负责;如果你使用的是Win32_DataFile的DeleteFile函数,即你想WMI系统请求删除文件,最终,WMI服务程序将调用Win32 DeleteFile函数。WMI系统不是自己尝试得出你是否该被允许这样做,而是模拟你的帐号来执行这个调用。Windows NT将做出适当的安全性检查,如果你没有许可,将会返回错误到WMI系统中。WMI系统将把这个错误传递给客户程序。

当连接远程计算机时,有可能将另一个用户的凭证作为参数指定给ConnectServer。如果略去这些,则将使用当前用户的凭证。当连接到本地计算机时,只能使用当前用户的凭证。

前面的解释对于实际的过程而言有些过于简单了,如果你在使用脚本对象编写客户应用程序,这就足够了。当使用DCOM接口时,还需要应用其他的DCOM规则。特别是,任何支持远程使用的接口都需要使用IclientSecurity,配置适当的安全过滤层。对于本地连接,需要启用执行某项操作所要求的客户线程特权。WMI SDK更为详细的涉及了这些问题,但买几本关于DCOM的书籍是值得的。幸运的是,脚本对象自动的处理绝大多数的此类复杂问题,这使得DCOM系统新手有了一个极佳的选择。



Figure 8Securing WMI Access

为提供额外的保护,WMI系统在每一个名称空间上支持传统的Windows NT安全设置。这对于指定限制非常有用,例如谁可以通过通过远程计算机连接到WMI系统中。这可以通过Windows 2000中的计算机管理的扩展来进行配置(如图8)。要实现这个功能,右键点击桌面上的我的电脑图标,然后选择管理选项;在服务和应用下,选择WMI控制,然后显示属性。在安全性能标签上,你可以设置对每一个名称空间的访问许可,就同你设置硬盘驱动器的文件夹访问许可一样。如果你运行的是Windows 9x 或是Windows NT,运行WBEMCNTL.EXE可以实现同样的功能。当连接到一台运行Windows 9x的计算机时,通过WMI系统管理的对象不支持Windows NT安全设置;但是,WMI系统允许名称空间通过模拟Windows NT许可进行配置。这可以让管理员控制允许谁连接到名称空间;但是,一旦建立连接,客户将对陈列的对象拥有完全控制。

事件

现在你已经了解了应用程序如何查询WMI系统,得到关于类,以及事例的信息。WMI系统还允许应用程序从系统接收事件。在标记中定义的事件也就是其他的类。例如,一个现代的厂商可以编写一个陈列MyModem_Ring事件的信息提供程序。MyModem_Ring类具有,诸如CallerIDName或是CallerIDNumber等属性。为记录一个事件,应用程序要使用类似于数据查询的WQL语法结构。以下的事件查询指定的是与Stephanie的来电有关的应用:

SELECT * FROM MyModem_Ring WHERE CallerIDName="Stephanie"

在信息提供程序定义的事件之外,应用程序还可以记录监测类或事例改变的事件。由于允许应用程在信息提供程序定义的事件之外,应用程序还可以记录监测类或事例改变的事件。由于允许应用程序对WMI列出的状态的任何改变作出反应,这项功能是非常有用的。图9中的代码显示了监测任意对象的创建是多么的容易。HTML程序(HTA)监测任何新过程的创建。

将图9中的代码复制到一个叫作NewProcs.HTA的文件中,并通过浏览器程序启动这个文件。HTA文件类非常似于HTM文件,但是运行的安全性能较差。由于WMI系统列出了操作系统的低层功能,脚本对象没有被脚本程序标记为是安全的,这并不意味着你不应该改编这些对象。但是你不应该允许没有确认安全的HTM文件来改编WMI系统。如果你在HTM文件中使用WMI改编对象,你将收到关于使用对于改编,没有标为安全的对象。当你启动HTA文件时,系统给予它作为可执行文件同样的访问。这允许使用WMI脚本对象,不会产生新的警告信息。

当你启动新的进程时,你可以在一个简单的应用程序中看到每一个进程的名称。这个示例引入了两个新的概念:首先,你用ExecNotificationQueryAsync函数记录使用WQL事件查询的事件。在SDK平台中有WQL事件查询的完整语法结构,但图9中的代码可以作为监测任何类的创建的模板来使用。

当新的事例创建时,WMI信息提供程序对通知WMI系统作应答,但这只是一个可选的要求。如果信息提供程序对一个特定的类不提供通知,应用程序仍能记录事例创建通知,并且WMI系统将通过用事例列表来调查信息提供程序来模拟事件。从句WITHIN 1告诉WMI系统,它应该每秒调查Win32_Process类的新的事例。不幸的是,这种调查函数并不安全。如果在一秒中之内创建一个进程,并且注销它,也就是在两次WMI系统查询之间,应用程序将不能收到事件通知。即使有了这个限制,事件的功能仍是强大的。

在查询事例创建之外,它还能购监测事例的删除,或事例的指定助兴的修改。图10 显示了应用程序如何监测软盘驱动器中的磁盘插入的,通过监测A:逻辑磁盘的改变来实现的。当Size属性改变时,新的Size大于0,你可以假定磁盘已经插入。这并不能算是很好的例子,但是你显示了WQL查询系统是如何对系统中几乎所有的情况能够产生事件来作应答的。

图10 引入的第二个新概念是SwbemSink的使用。收信点(sink)是在脚本接口中用来执行异步操作的,通过SwbemSink的类的ID创建变量mysink。收信点可以用在任何返回异步信息SwbemServices函数中。当得到异步数据时,收信点将启动事件并将数据传输到应用程序中。在前面的示例中,在WQL事件查询系统中,使用ExecNotificationQueryAsync函数来注册mysink对象。当指定事件发生时,mysink对象启动OnObjectReady事件。OnObjectReady事件句柄在HTML文档的底部加入文本。

我在下载的代码中还包括了另外三个示例,展示了本文所涉及的绝大多数概念(这些代码可以从本文的起始出的链接得到)。KillCalc.vbs是一个中止所有CALC.EXE运行事例的文件。ProcTree.HTA使用Win32_Process的ParentProcessId属性对系统中的所有进程创建母/子树。这个事例没有对操作做优化,但是它展示了你可以从WMI系统中得到的有用信息,这些信息也许没有在Win32 API中列出。最后,PrcWatch.HTA展示了sink的高级用法。这个示例显示EXPLORER.EXE启动的所有进程的列表。它在一个表格中列出这些进程,并附有一个删除键来中止每个进程(需确定x.gif文件在同一个路径下)。当浏览器启动新的进程时,它们被加入这个表格;中止进程时,则从表格中删除。这个示例也显示了如何使用一个sink来处理多于一个的异步请求。

结论

正如你所看到的,WMI系统是一个复杂而功能强大的服务协议。本文只涉及到了WMI系统的一些粗浅的功能。由于其他公司采用WBEM标准,WMI系统的功能会更强大。我希望这能鼓励你对自己的应用程序查看WMI系统,我想你将会发现它是一个不可缺少的工具的。