Windows2000 服务器端应用程序开发设计指南-Windows Management Instrumentation

Windows Management Instrumentation(WMI)是以一致的方式来管理电脑系统。

WMI是Microsoft实作的一个Web-Based Enterprise Management(WBEM)的执行方式,支援Distributed Management Task Force(DMTF)。

WMI提供您管理服务的能力。使用WMI,您可能启动一个服务、停止它以及改变它的许多参数如启动模式、启动名称、路径和服务类型。WMI也结合了事件处理的特色,以允许您提供可以让使用者改变之简单指令码与服务,例如,当服务应该执行却没有执行。WMI的所有管理特征即是它可以远端处理,所以只要给定一个适合的授权,您就可以从网路上的任何地方存取它们。

WMI的架构
 

WMI是一个电脑系统之可延伸资料模组,图8-1会说明其架构。本节将讨论WMI架构的元件。

Windows Management Service
 

Windows Management Service(WinMgmt.exe)是WMI的主要元件。WinMgmt担任了WMI消费者(管理应用程序)和提供者之间的代理人。其资料被储存在一个物件导向的结构描述中。这个结构描述由DMTF设计并为所有显示WMI资料的元件提供一个单一的资料描述机制。透过提供支援继承的标准结构描述,WMI资料提供者可以提供标准的资料类别和内容,同时也允许取得其他特定厂商的专用扩充程序以区别特定的产品。本章即在说明如何正确地完成本章的大部份内容。


 

 图8-1 WMI的架构

CIM物件管理者(CIM Object Manager)
 

Common Information Model(CIM)物件管理者提供客户端支援多种存取技术的介面,如COM、Scripting、XML、ODBC和ADO。物件管理者支援建立、删除、修改和取回类别和实例的API。类别实例不是由CIM储存机制所提供,此情形称为静态实例,就是由某些种类的提供者(被实作成如COM服务器一样)所提供,此情形称为动态实例。

CIM储存机制(CIM Repository)
 

CIM储存机制通常只被用来储存WMI支援类别之相关资讯。它基本上是一个符号表,而且不该被用来储存容量大的资料。CIM储存机制不是被存放在允许宣告类别和实例的物件管理者API中,就是直接存放在一个已知的MOF(Managed Object Format)文字中。

WMI提供者
 

提供者为COM元件,负责在CIM物件管理者与被管理之物件间的完整通讯。提供者可以为内建或特定之应用程序。

提供者可以为各种形式,一般的例子是一个为被管理之物件回传特定资讯的提供者。类别提供者是可以回传类别和实例之定义的一个更复杂之提供者类型。例如,如果您曾尝试从某些可能有新增表格的资料库中回传资讯,它可能会回传在提供者开启资料库时定义的每个类别之表格。这个提供者为进阶的类型,而且服务通常没有实作它。Active Directory是一个服务的罕见范例,它实作了一个动态的类别提供者。Property提供者为一个较简单的类型,它允许动态的新增属性到静态实例。

WMI架构之一般意图是使编写提供者的方法尽可能简单—例如,编写一个实例提供者可能只是产生几行程序代码的问题而已(也就是在WMI SDK中经由提供者产生之工具和范例的说明)。提供者的建构也会利用到继承的观念。如果您在一个类别中新增一个已经拥有提供者的子类别,则您只需为您的新属性或为您所覆写(Overriding)之类别提供属性值即可。

被管理之物件
 

一个被管理之物件可以是任何的企业元件(Enterprise Component)—例如:一个Win32物件或服务。一个服务允许客户端使用允许自己在一个WMI物件实例中显示管道之匿名管道(Name Pipe)与它连结。

管理应用程序
 

管理应用程序是一个与本地机器或远端机器通讯之WinMgmt服务应用程序。这些应用程序可以询问WinMgmt服务以决定所有存在一个机器中的逻辑与实体元件。然后这个资讯会被显示并修改,让使用者可以确实地理解并改变机器的设定。另外,当特定的事件发生时,管理应用程序可以命令WinMgmt服务传送一个通知讯息。例如,当机器的硬盘空间少于20% 或一个新的处理程序开始在被管理之机器上执行时,管理应用程序可以接收一个通知讯息。

结构描述(Schema)
 

WMI隐藏了管理环境之所有可怕及复杂的事物。它采用了Win32 APIs、登录设定、Simple Network Management Protocol(SNMP)Management Information Bases(MIBs)和Traps以及所有其他混杂的管理介面,并将它们包装在一个定义严谨的结构描述中。

结构描述并非是从WMI分离的一个元件,却是WMI的一个重要部份。结构描述是一个描述被管理之物件类别的集合。所有在WMI架构中的元件皆支援结构描述。

结构描述由物件管理者提供,能处理大批的资讯,包括系统、网路、应用程序、设备和实体元件,以及使服务的安装、设定与管理发生作用。结构描述由被安排在拥有属性、关联和方法之类别中的实例所组成。

WMI名称的演化

Common Information Model(CIM)是由Distributed Management Task Force(DMTF)定义的规格,描述以物件导向的方法来管理系统和网路之内容。Microsoft起初将CIM之实作命名为「Web-Based Enterprise Management(WBEM)」,而核心模式部份则命名为「Windows Management Information(WMI)」。DMTF开始在市场销售时,使用「WBEM」来描述一组以CIM之网际网路技术为基础的管理。然后Microsoft将他们所实作的CIM名称改为「WMI」,而核心模式部份的名称则改为「WMI extensions for the Windows Driver Model(WDM)」。由此名称的演化过程,您将可以发现Windows使用了「WMI」、「WBEM」以及「CIM」来表示其函数、介面、类别以及其他元件的管理架构。

WMI工具
 

WMI有许多可用的工具。您可能会时常使用的二个工具是CIM Studio和MOF编译器。在本章稍后之〈TimeServiceProvider范例〉一节中,将会讨论另外一个WMI Provider Code Generator Wizard工具。

WMI CIM Studio
 

WMI CIM Studio是一个察看和编辑CIM储存机制之类别与实例资讯的工具。类别和实例被分配到一个名称为命名空间(namespace)的单元群组中。WMI提供一个名称为root/CIM2的命名空间,它包含了所有代表系统之类别和实例。为了对WMI进行试验,请安装WMI SDK(特别是WMI工具,存放在附赠光碟上的Platform SDK中)。在安装了SDK后,您便可以使用CIM Studio工具来察看root/CIMV2命名空间的内容。本章中将使用CIM Studio以说明WMI的许多能力。

要开启CIM Studio时,请从公用程序集中选择WMI CIM Studio,它会被列在Microsoft Platform SDK群组下(或者是WMI SDK群组)。为了察看被安装在机器上的服务清单,请移至Win32_Service类别,并浏览CIM_ManagedSystemElement、CIM_LogicalElement、CIM_Service、Win32_BaseService以及Win32_Service的内容。按下右窗格上面的Instance工具列按钮,您应该会看到如图8-2所示的内容。


 

 图8-2 显示已安装服务的WMI CIM Studio

MOF Compiler
 

MOF compiler为WMI提供的一部份,可被用来引入新的类别与实例至系统中。以下的MOF范例说明了一些WMI类别的特征:

[static] class Msft__Joke {	[key]	string JokeName;;		string JokeText;};instance of Msft_Joke {	JokeName = "KnockKnock1";	JokeText = "Knock,knock. :Who's there? Dwayne: Dwayne, who?: "		"Dwayne the bathtub, I'm dwowning!";};

透过关键字class可以确定类别,而它的前面可以放置包含在中括弧([ ])内的限定词。该限定词用来提供与类别处理有关的资讯,例如,该如何实作它或者如何将资讯本土化的方法。类别属性由放置在前面中括号内的限定词定义。

在Msft_Joke范例中,类别被定义为[static],表示该类别的实例被储存在WMI储存机制内。JokeName的为字串属性,它是该类别的一个重要部份,表示类别的每一个实例皆必须有一个属性值,而且这个值必须与已存在的其他实例不同。

WMI的资料组织
 

WMI提供了一组安排资料应如何描述给使用者的基础类别。经由这些类别的使用,您可以避免大量结构描述设计的负担—所有您必须在已存在设计中扩充的事情。但是在扩充WMI以展示服务之特征时,您需要了解WMI免费提供的内容。

WMI将管理资讯分成几个类别,这些类别经由一些高阶类别而表示在WMI结构描述中。

操作上的资料(Operational Data)
 

操作上的资料与目前状态有关,并处理像「该服务需要多少内存?」这类的问题。它描述了系统真正做的事以及它的能力。例如,在服务的内容中,系统可能指示了服务可以与桌面互动的能力、服务的显示名称等等。

设定资料(Setting Data)
 

设定资料描述了被要求的状态以及结构的资讯,并处理如「服务应该使用多少内存?」之类的问题。当您在设定资料时,可以问自己一些问题,例如:「这个属性有其他可能的设定值而某些值在不同使用者或不同时间的情形下皆可正确执行吗?」这些种类的问题可以帮助您组织资料。例如,不管之前的问题,如果有多于一个属性的设定值存在,您应该将资料分开至一个不同的设定类别中,以提供这个结构资讯。在很多情况下,您可能会在设定和操作上的资料中发现类似的值。

统计的资料(Statistical Data)
 

统计的资料回答了像「服务现在使用了多少内存?」这类的问题。除了描述一些偶尔才被要求的统计资料外,它与操作上的资料类似,对效能的了解很有帮助,但是对了解被管理物件的状态部份则不是必需的。如果没有其他比必须避免在固定对操作上的资料存取时评估其统计资料的工作还重要的话,则必须从操作的资料中提取统计的资料。

历史的资料(Historical Data)
 

历史的资料回答了如「有少内存已被服务使用?」的问题并描述之前的状态。WMI没有提供一个预期捕捉大量资料的记录,但是它提供了一个资料传递机制和一个描述历史资料状态的表现形式,当您阅读到本章稍后之事件部份时便可看到这个内容。

考虑是否扩充WMI时,第一个要决定的是您想要引入的资料是否已被WMI支援。如果已支援,您便不需做任何事。如果没有的话,您就要描述您想处理之操作上的、设定、统计的或历史的资料;找出一个合适的类别;然后察看您有兴趣的资料内容是否已经存在该资料中。例如,如果您新增了一个新的服务,您将发现已经有一个Win32_Service的实例出现。若您需要加入新的操作上资料,则应该使用基础的Win32_Service来取得一个新的类别,然后,透过一个适当的方法使它存放在这个被取得的类别中(稍后会有更多的相关内容)。

WMI提供之与服务相关的核心类别
 

让我们简短的看一下WMI提供之与服务相关的核心类别。这些类别如图8-3所示。CIM_ManagedSystemElement、CIM_Setting以及CIM_StatisticalInformation类别分别为作业上的、设定和统计的资料提供根目录类别。如您从Win32_BaseService类别中看到的,WMI对关于服务以及和服务之定义、修改、启动、停止和删除有关之基本方法提供了各式各样的资讯。


 

 图8-3 WMI提供之与服务相关的核心类别

说明

类别名称前面加上「CIM_」以指示被DMTF定义的类别,而前面加上「Win32_」则表示被Microsoft定义。如果您打算将新的类别加至CIMV2命名空间,您就应该为您所熟悉的结构描述决定一个名称,例如商标,以确保没有其他相同的名称被使用。

CIM_ManagedSystemElement
 

CIM_ManagedSystemElement类别是所有储存在CIMV2命名空间之操作上资料的基础类别。它描述了所有系统的实体和逻辑元件。CIM_Managed SystemElement是为了正确取得二个类别的基础类别:CIM_LogicalElement和CIM_PhysicalElement。

说明

您应该永远不会用CIM_ManagedSystemElement来取得一个类别。如果您这么做的话,基本上是表示您定义的资料并非实体或逻辑的资料;然而,CIM之结构描述要求每一个系统元件为实体或逻辑二者之一,而且不能二者皆是。

CIM_ManagedSystemElement为每一个预期要提供数值的系统元件定义如下所示的一组基本属性:

  •  Caption 物件的短文字说明(一个单行字串)。
     
  •  Description 不限制大小的物件文字说明。
     
  •  InstallDate 当物件被安装时,指示日期与时间值。缺少这个值并不表示没有安装此物件。
     
  •  Name 已知物件的标签。可以覆写(override)取得类别的名称以建立一个属性机码,但这并非是必需的。
     
  •  Status 一个指示物件之目前状能的列举。
     

说明

在这个部份中描述的这些属性以及那些为了其他类别而定义的内容皆被描述在WMI SDK与CIM Studio里面。选择一个包含属性的类别并按下Help For Class按钮即可察看CIM Studio的一些属性描述。

Win32_BaseService
 

Win32_BaseService类别描述了被安装在服务控制管理员登录资料库的可执行程序。此类别的实例会识别服务或设备驱动程序。您的服务应该使用Win32_Service为它的基础类别,以取得一个类别(于下一节讨论)。一个设备驱动程序可以用Win32_SystemDriver为它的基础类别,以取得一个类别。

说明

您应该永远不会从Win32_BaseService中取得一个类别,那么做表示告诉系统您已经改变了作业系统的架构。

Win32_BaseService类别支援的属性包括:

  • AcceptPause
     
  • PathName
     
  • AcceptStop
     
  • ServiceSpecificExitCode
     
  • DesktopInteract
     
  • ServiceType
     
  • DisplayName
     
  • StartName
     
  • ErrorControl
     
  • State
     
  • ExitCode
     
  • TagId
     

说明

在CIM Studio中有一些属性拥有一个黄色箭头图示,这个黄色箭头图示指示该属性是从一个基础类别继承而来的。

Win32_Service
 

Win32_Service类别描述了在Microsoft Win32电脑系统上的一个服务。一个服务应用程序与本书第叁章与第四章所讨论的服务控制管理员介面相符。

在WMI中,服务类别应该是为了管理服务而使用,而不是提供存取服务的工作。例如,它可能会不适当的宣告一个来自Win32_Service的DHCPService类别并在类别中实作了一个取得新的DHCP位址之方法。然而,它也可能在被取得的类别中适当的实作一个方法,以允许您限制可被服务回传的位址范围。

Win32_Service类别支援了以下的属性:

  • CheckPoint
     
  • ProcessId
     
  • WaitHint
     

CIM_ServiceAccessPoint
 

CIM_ServiceAccessPoint类别描述了您的服务之存取位置,如同在通讯协定服务的环境条件下由应用程序和通讯埠80接收的命令列参数。使用这个类别管理存取位置。如果您需要控制例如主机的存取位置,则要提供此类别的实例。

CIM_Setting
 

您知道设定资料是分别被操作上的资料所描述。CIM_Setting类别为所有的设定资料提供一个基础类别。在任何您想要为服务加入与设定相关的资料时,应该透过从CIM_Setting类别取回而使它可以存取。

定义一个应用于CIM_ManagedSystemElement的特殊设定时,请注意CIM_ElementSetting在CIM_Setting与CIM_ManagedSystemElement之间的关系。您应该永远从相关的类别中取得一个类别,以建立您的CIM_ManagedSystemElement类别(通常是一个Win32_ServiceCIM_Service或AccessPoint_class)与设定它的CIM_Setting-derived类别之间的特殊关系。您可以在CIM储存机制中储存设定资料(如您在本章前面所见的joke类别范例),但是我不建议您如此做,因为CIM储存机制不打算用于一般目的之储存,您应该在别处储存资讯,例如在登录中,并使用WMI登录提供者取回资讯。

CIM_StatisticalInformation
 

CIM_StatisticalInformation类别在CIMV2命名空间内的统计资料提供了一个位置。您可以选择此类别下的任何类别,并在的实例中察看它目前的统计资料。例如,看到CIM_StatisticalInformation/Win32_Perf/Win32_Perf RawData类别下的Win32_Perf RawData_PerfDisk_PhysicalDisk类别。

说明

许多类别名称包括了一些底线。严格来说,它并不被允许—这些是Microsoft没符合规则的例子。在每个您的类别名称中最好只包括一个底线;而这个底线应该要使类别名称与取回的类别名称标记区分。

关联类别
 

我只提及一个重要特色即是关联类别,它将一些类别连接在一起。一个关联类别的范例即是您曾见过的CIM_ElementSetting,它将CIM_ManagedSystemElement连接至描述它们的结构参数设定。

您必须了解更多重要的关联类别以正确地使用WMI。以下几个部份描述了您应该了解的关联类别。

CIM_Dependency
 

大部份连接了二个CIM_ManagedSystemElement类别的关联类别为二种类型之一—即一个CIM_Dependency关联类别或一个CIM_Component关联类别。CIM_Dependency关联类别描述了二个物件间的函数依存关系,依存关系说明了哪一些元件隶属于另一个元件,例如,Win32_DependentService关联类别(从CIM_ServiceServiceDependency取回,而它是从CIM_Dependency取回的)被用来指示服务间的依存关系。如果服务因为一个它依赖的服务而启动失败,它可能会采取从属的连锁关系,直到您确定了问题的原因为止。

如果您开启了CIM Studio并展开CIM_Dependency下的树状结构,您将会看到超过40个的关联类别,每一个皆展示不同类型的依存关系,以使它可以在WMI模型中描述。他们之中有些是特定的服务以及与他们相关的物件。CIM_Dependency时常会被当成基础类别使用。

CIM_Component
 

CIM_Component关联类别被用来确认一个元件为另一个元件一部份之事实。从CIM_Component取回的类别描述一个系统元件和许多组成系统之CIM_ManagedSystemElement类别间的关系。CIM_Component通常被当成一个基础类别使用。

CIM_ElementSetting
 

CIM_ElementSetting与一个提供结构参数之CIM_ManagedSystemElement类别的设定相关。您必须透过CIM_ElementSetting关联类别取回一个类别,以与任何您用适用于CIM_ManagedSystemElementderived类别设定而加至CIMV2命名空间的设定取得关联。

CIM_Statistics
 

统计资料的关联类别将一个CIM_ManagedSystemElementderived类别连接至许多适用于它的统计资料。您必须经由CIM_Statistics关联类别取回一个类别,为使用者提供一个取得CIM_ManagedSystemElementderived类别物件之统计资料的方法。

CIM_ServiceServiceDependency
 

此类别从CIM_Dependency关联类别取回。使用它去定义任何存在服务间的依存关系。它实际上不常被用来察看一个在CIMV2命名空间内的CIM_Service ServiceDependency类别,因为服务的依存关系通常被定义为服务本身的属性。

CIM_ServiceAccessBySAP
 

这个类别也是从CIM_Dependency关联类别取回,而且应该被用来定义服务和存取位置间的关系,以用来存取服务。

Win32 Service-Related Classes
 

以下几个部份描述了一些您应该了解之与Win32服务相关的类别。包括了与系统相关的类别、与使用者帐户相关的类别以及为服务群组指定执行之依存关系的类别。图8-4显示了一些类别。

Win32_ComputerSystem
 

Win32_ComputerSystem类别描述一个在Win32环境下的电脑,包括许多与开机、电源、硬体、拥有者以及其他资讯相关的属性。Win32_ComputerSystem类别最后会从CIM_System取回。通常您不会新增任何东西到CIM_System或它的任何后裔中。虽然系统制造商也许会从Win32_ComputerSystem中取回一个类别,以定义Win32系统的特性,但它非常的不可靠。

Win32_Process
 

Win32_Process类别被严格地限制在Win32系统知道的处理程序上。您不应该增加实例或属性至此类别中,但是您可以建立与处理程序实例的关联。例如,您可以使用关联类别来说明您的服务对应至一组处理程序和线程的细节。


 

 图8-4 一些与Win32服务相关的类别

Win32_Account
 

Win32_Account类别包含了Win32系统与使用者帐户和群组帐户相关的资讯。由Windows网域识别的使用者或群组的名称是这个类别的后代(或成员)。

Win32_SystemAccount
 

Win32_SystemAccount类别描述了本机帐户。

Win32_LoadOrderGroup
 

Win32_LoadOrderGroup类别确认可取决于服务的载入顺序群组。Win32_LoadOrderGroup的二个重要的关联类别是描述基础类别服务与一个载入顺序群组关系的Win32_LoadOrderGroupServiceMembers类别,以及描述基础服务和取决于开始执行之载入顺序群组间依存关系的Win32_LoadOrderGroup ServiceDependencies类别。以下的方法可以表示这二个类别之间的差异:元件关联类别说明「服务或驱动程序为此群组的一个成员」,然而依存关系之相关类别则说明「此服务或驱动程序隶属于此群组」。

软件安装类别
 

图8-5说明了有关软件安装的主要类别。这些类别直接反映了用来控制Microsoft Windows Installer技术的资讯。如果您使用Windows Installer安装您的服务,这些安装类别会被自动地加入,而您就可以使用WMI来安装、移除以及修复您的安装资讯。


 

 图8-5 与软件安装有关的类别

Win32_Product是具体的类别,是经由一个消费者单元取得的实体元件、软件特性以及其他产品的集合。这个类别的实例描述了被Windows Installer安装的产品。一个产品通常与单一的安装套件有关。

CIM_SoftwareElement类别为特定平台而将CIM_SoftwareFeature物件个别分解到一组可管理或可部署的元件中。由它的硬体架构以及作业系统可以唯一的确定软件元件平台(例如,Microsoft Win98或Windows 2000)。

事件
 

每个有用的管理应用程序必然是经由事件驱动。这是为了一个回应管理应用程序(在它们中断后,元件是固定不变的)以及预防管理应用程序(在它们中断前,元件是固定不变的)。WMI有一个要求尽量使您使用的部份最小化的事件相关特性的一个广泛设备。在本节中,将会看到WMI提供的部份,接着会描述如何使您的服务从一个大且复杂的环境下转移至一个可管理的企业类别应用程序中,或者能正常的使用于远端服务的情形。

每个事件结构至少必须解决叁个问题:

  •  发行(Publication) 我该如何查明有什么事件?
     
  •  订阅(Subscription) 我该如何将想知道事件告诉系统?
     
  •  传送(Delivery) 事件如何传送到我这里?
     

事件的发行
 

WMI中的事件透过一个事件类别被发行。一个事件的产生被类别的实例所描述。所有的事件类别皆从 __Event系统类别取得。

说明

您可以在CIM Studio的 __SystemClass/__IndicationRelated下找到 __Event类别。用于系统类别前面的双底线是用来区分其他类别名称与保留的系统类别名称。WMI不允许您定义一个前面加底线的类别名称。

WMI定义的最重要事件类别为 __InstanceOperationEvent以及取得它的类别。实际上它们是非常简单的,如同您可在下列之MOF程序代码中看到的:

class __InstanceOperationEvent :__Event {	object TargetInstance;};class __InstanceCreationEvent :__InstanceOperationEvent {};class __InstanceDeletionEvent :__InstanceOperationEvent {};class __InstanceModificationEvent :__InstanceOperationEvent {	object PreviousInstance;};

其中二个object定义了嵌入的物件,这些物件会触发事件。例如,当一个新的处理程序成立时,如透过新的Win32_Process类别描述的一样,以它包含嵌入物件之新实例的TargetInstance属性产生一个 __InstanceCreat类别的新实例。

透过这个方法,WMI提供一个描述每个命名空间之建立、修改或删除动作的事件。当然,这些事件只有在要求时才会被接收—在最简单的电脑系统中,如果您询问所有可能的__InstanceOperationEvents时,则您每秒钟就会接收到成千的事件。基本上您可以监控每一个线程、处理程序、文件、服务或其他改变系统状态之元件的实例。

事件的订阅
 

在WMI中,事件的订阅透过一个客户端应用程序完成。询问是与一个类别相互作用的一个自然方法。您可以呼叫一个API并传递一个物件定义的询问,将您有兴趣的物件告诉WMI。例如,当一个新的处理程序被启动时,以下的Microsoft Visual Basic程序代码会侦测到它并显示处理程序的名称。

Dim wbemService As SWbemServicesDim events As SWbemEventSourceDim singleEvent As SWbemObjectDim sQuery As StringSet wbemServices = GetObject("winmgmts:{impersonationLevel=impersonate}")sQuery = "select * from __instancecreationevent within 10" & _	"where targetinstance isa 'Win32_Process'"Set events = wbemServices.ExecNotificationQuery(sQuery)Do	Set singleEvent = events.NextEvent	MsgBox singleEvent.TargetInstance.NameLoop

程序代码的第一个陈述式透过WinMgmt服务取得一个handle,以开启WMI。第二个陈述式则提供订阅的请求。第叁个陈述式透过服务而引用了ExecNotificationQuery函数,以执行该请求。经由ExecNotificationQuery呼叫而回传的event物件有一个如事件发生时,允许您等待事件的NextEvent方法。当我们讨论到事件的传递时,您将会看到这个请求事件的范例程序。WMI支援非常复杂的事件订阅和传递机制,以适用各式各样的管理应用程序。

说明

您应该也要注意一些与先前所列之范例程序代码有关的特性。它利用WMI指令码的惯例,假设root/CIMV2为预设的命名空间。同样的,它没有包括任何错误处理或终止程序代码。

事件的传递
 

现在我们将注意力转移到事件的传递上。您曾经看过一个透过允许您编写捕捉与处理事件的简单指令码介面支援的基本事件传递机制。事实上,如果将事件当成管理应用程序的一部份使用,您将会需要以一个非同步的方式处理它们。您也需要过滤事件以使您只察看有兴趣的事件。

WMI允许您控制事件被传递的二个方法,即哪个事件已被传递以及取得与事件一起传递的资料。利用要求来陈述订阅请求的事实,以负担这个控制。我们已经看过每次得到一个处理程序已启动事件之简单的订阅请求。

考虑一个稍微复杂的范例。假设您只想取得已停止之以手动方式启动的服务名称,您的第一个步骤是找出一个定义服务启动方式的属性。在CIM Studio中察看Win32_Service类别的定义,您应该会看到一个名称为StartMode的属性。在属性上按下滑鼠右键,选择Property Qualifiers以察看它的修饰语。ValueMap修饰语包含了有关启动模式的资讯,而且可以为以下之一的值:Boot、System、Auto、Manual以及Disabled。如果它为「Manual」,则我们可以检查并察看StartMode的属性。服务的名称是经由Name属性而给定的,所以您的订阅要求可能会是以下的内容:

Select TargetInstance.NameFrom __InstanceModificationEventWithin 10Where TargetInstance isa 'Win32_Service' and	TargetInstance.StartMode = 'Manual' and	TargetInstance.Started = FALSE and	PreviousInstance.Started = TRUE

注意到这个要求使用了PreviousInstance值,以确定服务的启动状态是否已经从True被改变到False。服务有许多可以改变的外观,但是在这个情况下我们只对服务是否已停止的事实感到兴趣。使用Within类别的请求定义一个透过CIM Object Manager在侦测事件发生时使用的轮询间隔。基本上物件管理员每十秒就会重新评估该物件。

您可以编写一个用WMI登录之固定的事件订阅者,其方法是每个事件发生时,这个订阅者被要求采取一些动作。我们刚才已在范例中看过,您可以确定每次服务被停止时,操作员会被通知。

WMI有许多标准的订阅者,您可以用它来建构复杂的事件控制者。例如,WMI可以利用Microsoft Message Queue(MSMQ)来保证事件的传递。WMI有一个可以发布命令列呼叫的标准订阅者,例如,当一个物件发生时,允许您传送一个e-mail讯息。

TimeServiceProvider的WMI提供者范例
 

TimeServiceProvider的WMI提供者范例(「08 TimeServiceProvider.dll」)如 

选择提供的资讯
 

建立提供者的第一个步骤是决定服务之可用资讯。TimeService服务使用一个匿名管道给Client/Server连结—这个匿名管道的名称即是一个可利用的资讯。在TimeService.cpp文件中(第叁章),可以看到管道名称为「//./pipe/TimeService」。我们将会建立一个提供管理员或客户端使用WMI来要求匿名管道的动态提供者。您可以考虑修订这个WMI提供者,因此它也允许管理员透过使用WMI而改变匿名管道的名称。

使用MOF取回一个类别
 

下一个步骤是用Win32_Service取回一个类别,并且增加一个匿名管道的属性至取回的类别中。以下的程序代码说明被用来建立此类别的MOF程序代码,存放在附赠光碟上的TimeServiceStart.mof文件中:

#pragma namespace("////.//root//cimv2") class Richter_TimeServiceProvider :Win32_Service {	string PipeName; };

使用MOF编译器
 

一旦建立了 .mof档后,您需要使用MOF编译器去编译它(MOFComp.exe),它是WMI的一部份。MOF编译器将会增加新的Richter_TimeServiceProvider类别至系统中。以下说明了您可以使用的编译命令:

C:/WINNT/system32/wbem/mofcomp.exe -class:forceupdate	"<filepath>/TimeServiceStart.mof"

请注意「-class:forceupdate」选项以及标记在MOF文件路径前后引号的使用。当类别发生冲突时,「-class:forceupdate」选项可以强迫更新类别,而引号则允许您使用包含空白字元的名称。请察看《WMI SDK以及Platform SDK》文件的「MOF Compiler」主题,以取得更多有关MOF编译器之可用选项资讯。

结合MOF编译器至Visual Studio
 

如果您时常使用MOF编译器,您会发现可以很方便的将它新增至Microsoft Visual Studio之Tools功能表,开启Visual Studio并从Tools功能表中选择Customize选项即可完成这件事。在Customize对话方块中,选择Tools页签,卷动清单至底部,增加一个新的工具,名称为MOF Compiler,如图8-6所示。

因为您已经将MOF编译器设定为Visual Studio的一个工具,所以您可以开启任何MOF文件并编译它。由于已经核取了Use Output Window核取方块,所以任何的编译错误皆会显示在Output视窗中。

您可以在Output视窗中双按显示错讯息的内容,Visual Studio会自动地移到原始码中产生错误的那一行。


 

 图8-6 将MOF Compiler加至Visual Studio Tools功能表之Customize对话方块的Tools页签

在您编译TimeServiceStart.mof文件后,请执行CIM Studio并开启CIMV2命名空间。此时您应该会在以下的节点中看到新的类别。如果CIM Studio已经开启,您就必须更新显示内容。

CIM_ManagedSystemElement/CIM_LogicalElement/CIM_Service/	Win32_BaseService/Win32_Service/Richter_TimeServiceProvider

图8-7显示了CIM Studio应该显示的内容。反白的部份为新的PipeName属性,您可以容易地察看这个类别的部份。

为了存放这个新的Richter_TimeServiceProvider类别,提供者必须被定义并且与类别定义关联。这就是Windows Management Service(WinMgmt.exe)呼叫提供者以取得实例资料的方法。建立提供者的最简单方法即是让CIM Studio工具为您产生DLL的原始码。

使用WMI Provider Code Generator精灵
 

想要为Richter_TimeServiceProvider类别产生提供者之DLL,您必须先在左窗格中透过左边的核取方块选择类别名称(如图8-7所示),然后在CIM Studio上面双按Provider Code Generator按钮,以呼叫WMI Provider Code Generator精灵。


 

 图8-7 编译TimeServiceStart.mof档后,显示在CIM Studio中的Richter_TimeServiceProvider类别

在Welcome对话方块中,按下Next开始执行精灵。在下一个对话方块新增适当的文字到Description文字方块中。然后检查Override Inherited Properties核取方块,并在属性清单中核取名称属性,以使提供者从基础类别覆写。您必须覆写名称属性,因为它用物件管理者决定是否经由被取得之提供者供应您提供之符合实例的属性。对话方块看起来应该像图8-8的内容(请注意您可能无法看到核取的名称属性)。


 

 图8-8 用来指定名称与属性的WMI Provider Code Generator精灵对话方块

按下Next按钮以显示如图8-9所示之对话方块。您可以用这个对话方块给予提供者DLL一个文件名称和说明,以及指定您想存放产生程序代码的目录。按下Finish按钮即可产生提供者的原始程序代码。精灵会产生六个文件:MAINDLL.cpp、Richter_TimeServiceProvider.cpp、Richter_TimeServiceProvider.h、TimeServiceProvider.def、TimeServiceProvider.mak以及TimeServiceProvider.mof。

修改精灵产生的程序代码
 

您还需要对已产生的程序代码做些修正,该程序代码才可以正确执行。在预设的情形下,TimeServiceProvider类别会被放置在root/default命名空间内。您必须在TimeServiceProvider.mof的上方加入以下的Pragma,以使它成为root/CIMV2命名空间的一部份。

#pragma namespace("////.//root//cimv2")


 

 图8-9 用来指定提供者之文件名称、说明以及输出目录的WMI Provider Code Generator精灵

将TimeServiceProvider.mof与TimeServiceStart.mof文件结合会使提供者的部署工作更方便。我附加了TimeServiceStart.mof的内容至被精灵产生之TimeServiceProvider.mof文件的底部,还新增了以下几行程序,以使WinMgmt服务察觉这个动态地产生的类别属性值。列表8-2显示经过修改后的TimeServiceProvider.mof文件内容。

[provider("TimeServiceProvider"), dynamic]

现在让我们集中在Richter_TimeServiceProvider.cpp文件上,在它可以使用前必须做些修改。首先寻找这一行程序:

CRichter_TimeServiceProvider MyRichter_TimeServiceProviderSet	(PROVIDER_NAME_RICHTER_TIMESERVICEPROVIDER, L"NameSpace");

以适当的命名空间取代NameSpace:

CRichter_TimeServiceProvider MyRichter_TimeServiceProviderSet	(PROVIDER_NAME_RICHTER_TIMESERVICEPROVIDER, L"root//cimv2");

第二,修改EnumerateInstances函数,使它回传正确的属性值。对TimeService服务来说,这是非当容易的,因为它只有一个属性,即匿名管道的名称。以下的程序代码说明了EnumerateInstances函数修改后看起来的样子。

HRESULT CRichter_TimeServiceProvider::EnumerateInstances (	MethodContext* pMethodContext,long lFlags ){	HRESULT hRes = WBEM_S_NO_ERROR;	// 基于被传递进来的MethodContext建立一个新的实例	// 注意到CreateNewInstance可能会被丢出,但是它永远不会回传NULL	CInstance* pInstance = CreateNewInstance(pMethodContext);	// 类别名称必须符合服务的程序设计名称	pInstance->SetCHString(pName,		"Programming Server-Side Applications Time");	pInstance->SetCHString(pPipeName, "TimeService");	hRes = pInstance->Commit();	pInstance->Release();	return(hRes);}

第叁,您必须修改GetObject函数,让它回传一个给定的资料。对一个服务来说,修改这个函数是很容易的,因为在每个被给定时间的机器上,它是唯一的一个服务实例。以下的程序代码说明GetObject函数修改后的样子。列表8-1显示了Richter_TimeServiceProvider.cpp文件内容。

HRESULT CRichter_TimeServiceProvider::GetObject (CInstance* pInstance, 	long lFlags){	HRESULT hr = WBEM_E_NOT_FOUND;	// 类别名称必须与服务的程序设计名称相符	pInstance->SetCHString(pName,		"Programming Server-Side Applications Time");	pInstance->SetCHString(pPipeName, "TimeService");	hr = WBEM_S_NO_ERROR;	return(hr);}

建构TimeServiceProvider范例
 

为了建构提供者DLL,我用Visual Studio建立了一个空的Win32 Dynamic-Link Library专案,接着加入MAINDLL.cpp、Richter_TimeServiceProvider.cpp、Richter_TimeServiceProvider.h以及TimeServiceProvider.def文件至专案中。您必须记得连结FrameDyn.lib程序库(由WMI SDK提供)。透过以下这行放置在Richter_TimeServiceProvider.cpp文件中的程序代码来确定对程序库的连结:

#pragma comment(lib, "FrameDyn.lib")

最后,我为此专案的Debug Build和Release Build加入了USE_POLARITY符号。在Project Settings对话方块的C/C++ 页签上可以完成这个动作,如图8-10所示。您可能想用Warning level 3的设定来编译文件,因为若使用Warning level 4设定来编译文件时,标头文件架构会产生很多警告讯息。


 

 图8-10 加入符号以及设定文件警告层的C/C++ 页签

部署TimeServiceProvider范例
 

您必须执行MOF Compiler,传递TimeServiceProvider.mof文件,以部署提供者。这会使WinMgmt服务察觉WMI类别提供者的资讯。接下来您要执行RegSvr32,传递「08 Time-ServiceProvider.dll」文件给它。最后,您必须确定TimeService服务(显示名称为「Programming Server-Side Applications Time」)已经被安装在机器上。请注意此服务不一定为执行状态,但是必须已经安装。您可以用安装选项("03 TimeService.exe" -install)来安装服务。

为了察看这一切努力的成果,请开启CIM Studio并浏览至以下的节点:

CIM_ManagedSystemElement/CIM_LogicalElement/CIM_Service/	Win32_BaseService/Win32_Service/Richter_TimeServiceProvider

在右窗格上方按下Instances按钮,选到Win32_Service类别的新PipeName属性,您将会看到从TimeServiceProvider DLL回传的资讯,如图8-11所示。


 

 图8-11 在CIM Stud中显示TimeService服务之PipeName属性的TimeServiceProvider DLL

说明

当需要时,提供者DLL会被载入到WinMgmt.exe的位址空间。如果您想要修改您的提供者原始程序代码并重新建构它的话,必须先停止WinMgmt服务,当您准备好载入新建构的DLL时,再重新启动它。

如果您的提供者无法执行,可以检查C:/WINNT/System32/WBEM/Logs目录,它包含了一些记录文字档。这些文件追踪了许多错误情形,例如配置或载入提供者失败的情形。同样的,为了帮助您对提供者侦错,可以执行WinMgmt.exe,以正常的处理程序取代服务。这么做之前您必须先停止执行WinMgmt服务并从命令列中使用 /exe选项执行WinMgmt.exe。

Richter_TimeServiceProvider.cpp/******************************************************************Richter_TimeServiceProvider.CPP — 实作WMI提供者类别由Microsoft WMI Code Generation Engine产生目标:	-察看个别的函数标头			-当连结时,确定您已经连结了framedyd.lib和msvcrtd.lib(侦错版)或 			framedyn.lib和msvcrt.lib(零售[M3]版)说明:为Microsoft Windows TimeService WMI提供者设计服务器端应用程序******************************************************************/#pragma message("This project requires that the Platform SDK 's WMI components")#pragma message("be installed.You may install these componenets from the ")#pragma message("book 's CD-ROM or from msdn.microsoft.com")// 在侦错资讯中,识别项会被缩短为「255」个字元#pragma warning(disable:4786)//********************************************************************#pragma warning(push,3)#include <fwcommon.h>	// 必须先被含入#pragma comment(lib,"FrameDyn.lib")#include "Richter_TimeServiceProvider.h"// 目标:为您的提供者实例使适当的命名空间取代「NameSpace」内容// 例如:「root //default or "root //cimv2」//============================================================CRichter_TimeServiceProvider MyRichter_TimeServiceProviderSet (PROVIDER_NAME_RICHTER_TIMESERVICEPROVIDER,L"root //cimv2");// 属性名称//===============const static WCHAR* pName = L"Name";const static WCHAR* pPipeName = L"PipeName";/**********************************************************************函数:CRichter_TimeServiceProvider::CRichter_TimeServiceProvider**说明:建构子(Constructor)**输入:无**回传:无**注解:呼叫提供者的建构子*********************************************************************/CRichter_TimeServiceProvider::CRichter_TimeServiceProvider (LPCWSTR lpwszName,LPCWSTR lpwszNameSpace ):	Provider(lpwszName, lpwszNameSpace){}/********************************************************************** 函数	:  CRichter_TimeServiceProvider::~CRichter_TimeServiceProvider** 说明	:  解构子(Destructor)** 输入	:  无** 回传	:  无** 注解	:*********************************************************************/CRichter_TimeServiceProvider::~CRichter_TimeServiceProvider (){}/********************************************************************* 函数	:  CRichter_TimeServiceProvider::EnumerateInstances** 说明	:  回传此类别的所有实列** 输入	:  一个为了与WinMgmt沟通而指向MethodContext的指标*		在IWbemServices::CreateInstanceEnumAsync描述之一个包含long型别的标记。		请注意以下这些被WinMgmt处理(以及过滤)的标记:*			WBEM_FLAG_DEEP*			WBEM_FLAG_SHALLOW*			WBEM_FLAG_RETURN_IMMEDIATELY*			WBEM_FLAG_FORWARD_ONLY*			WBEM_FLAG_BIDIRECTIONAL** 回传	:  如果执行成功则回传WBEM_S_NO_ERROR** 注解	:  目标:在机器上的所有实例应该在此被回传,此类别知道如何存放己被填入的所有属性。如果没有									            实例的话,则回传WBEM_S_NO_ERROR。没有一个错误是没有实例。如果您实作一个「只									            有方法(method only)」的提供者,您应该移除此方法。*********************************************************************/HRESULT CRichter_TimeServiceProvider::EnumerateInstances (MethodContext*pMethodContext, long lFlags){	HRESULT hRes = WBEM_S_NO_ERROR;// 目标:	以下的注解包含了为此类别而输入属性的「set」方法。因为它们不会在目前的格式下编译,所以						注解它们。每一个 <Property Value> 应该被取代为适当的值。同样的,考虑建立一个新的方						法并移除这些set陈述式以及回圈里面的GetObject。察看架构范例(ReindeerProv.cpp)						可以取得完成这件事的范例。////  			 如果机器上超过一个实例产生例外的情形,则应该透过实例以相对应的填满动作执行 						EnumerateInstances。////      您必须永远设定为所有主要的属性。请察看文件以取得进一步的细节///////////////////////////////////////////////////////////////////////////////	// 依据传递进来的MethodContext建立一个新的实例。	// 注意CreateNewInstance可能会被丢出,但是永远不会回传NULL	CInstance* pInstance = CreateNewInstance(pMethodContext);	// 类别名称必须符合服务的程序设计名称	pInstance->SetCHString(pName, "Programming Server-Side Applications Time");	pInstance->SetCHString(pPipeName, "TimeService");	hRes = pInstance->Commit();	pInstance->Release();	return(hRes);}/********************************************************************** 函数:	CRichter_TimeServiceProvider::GetObject** 说明:	依据主要的属性为类别寻找一个单一的实例** 输入:	一个指向包含主要属性之Cinstance物件指标*				描述在IWbemServices::GetObjectAsync中的一个long型别标记** 回传:	如果可以找到实例,则回传WBEM_S_NO_ERROR*				如果无法找到被主要属性描述的实例,回传WBEM_E_NOT_FOUND*				如果找到实例但是有另一个错误发生时,回传WBEM_E_FAILED** 注解:	如果您实作了一个只提供方法的提供者,应该移除这个方法*********************************************************************/HRESULT CRichter_TimeServiceProvider::GetObject (CInstance* pInstance,long lFlags){	// 目标:	GetObject函数用来依据主要的属性,在机器上搜寻此类别的实例。不像那些在机器上寻找所								有实例的EnumerateInstances一样,GetObject使用主要的属性寻找符合的单一实例 并回传该实例	//	// 使用Cinstance之Get函数(例如,呼叫GetCHString(L"Name", sTemp))相对于用pInstance 			 	察看客户端要求的主要值HRESULT hr = WBEM_E_NOT_FOUND;	// 类别名称必须与服务的程序设计名称相符	pInstance->SetCHString(pName, "Programming Server-Side Applications Time");	pInstance->SetCHString(pPipeName, "TimeService");	hr = WBEM_S_NO_ERROR;	return(hr);}/********************************************************************** 函数:	CRichter_TimeServiceProvider::ExecQuery** 说明:	您传递一个方法的内容使用在满足要求实例的建立中,并将放置所有的实例。您可能会回传比被要求					                以及WinMgmt将要公布之过滤后无法应用还要多的实例或属性。** 输入:	为了与WinMgmt沟通的一个指向MethodContext指标*				一个描述满足要求条件的要求物件*				描述在IWbemServices::CreateInstanceEnumAsync中的一个long型别标记,注意以下被                 WinMgmt处理(以及过滤)的标记:*			WBEM_FLAG_FORWARD_ONLY*			WBEM_FLAG_BIDIRECTIONAL*			WBEM_FLAG_ENSURE_LOCATABLE** 回传:	如果此类别不支援该要求或者此类别的要求太复杂时,回传WBEM_E_PROVIDER_NOT_CAPABLE。					                架构将会呼叫EnumerateInstances函数,并让Winmgmt公布过滤器。*		如果要求执行失败,回传WBEM_E_FAILED*		如果要求执行成功,回传WBEM_S_NO_ERROR** 注解:	目标:大部份的提供者不需要实作此方法。如果您没有做的话,WinMgmt将会呼叫您的列举函数以					                取得所有实例并执行过滤程序。除非您期待从要求中实作重要的储存动作,否则您应该移除这个方法。					                如果您实作了一个只有方法[M6]的提供者,也应该移除此方法。*********************************************************************/HRESULT CRichter_TimeServiceProvider::ExecQuery (MethodContext *pMethodContext,CFrameworkQuery&Query,long lFlags){	return (WBEM_E_PROVIDER_NOT_CAPABLE);}/********************************************************************** 函数	:  CRichter_TimeServiceProvider::PutInstance** 说明:	PutInstance应该被用在可以将实例资讯写回硬体或软件的提供者类别中。例如, Win32_Environment                允许一个PutInstance建立或更新一个环境变数。然而,一个像 MotherboardDevice的类别不允许对一                些套接字做编辑,因为提供者会影响那些数字,所以很困难。** 输入:	指向一个包含主要属性的Cinstance物件指标*				描述在IWbemServices::PutInstanceAsync内的一个long型别标记** 回传:	如果PutInstance为不可用时,回传WBEM_E_PROVIDER_NOT_CAPABLE*				如果发生一个实例之错误传递情形,回传WBEM_E_FAILED*				如果任何一个实例属性不正确,回传WBEM_E_INVALID_PARAMETER*				如果实例被适当地传递,回传WBEM_S_NO_ERROR** 注解:	目的:如果您不打算编写支援您的提供者之程序,或者建立了一个只有方法的提供者,请移除此方法*********************************************************************/HRESULT CRichter_TimeServiceProvider::PutInstance (const CInstance &Instance,long lFlags){	// 使用Cinstance之Get函数(例如,呼叫GetCHString(L"Name", sTemp))相对于察看被客			户端要求的主要值	return (WBEM_E_PROVIDER_NOT_CAPABLE);}/********************************************************************** 函数:	CRichter_TimeServiceProvider::DeleteInstance** 说明:	像PutInstance的DeleteInstance,实际将资讯写到软件或硬体中。对大部份的硬体来说, DeleteInstance                应该不要被实作,但是为软件结构实作DeleteInstance则似乎可行** 输入:	一个指向包含主要属性的Cinstance物件*				描述在IWbemServices::DeleteInstanceAsync里面的一个long型别标记** 回传:	如果DeleteInstance不可用时,回传WBEM_E_PROVIDER_NOT_CAPABLE*				如果在删除实例时发生错误,则回传WBEM_E_FAILED*				如果任何一个实例的属性不正确,回传WBEM_E_INVALID_PARAMETER*				如果实例被正确地删除,回传WBEM_S_NO_ERROR** 注解:	目标:如果不打算支援删除实例的动作或实作了一个只有方法的提供者,请移除此方法*********************************************************************/HRESULT CRichter_TimeServiceProvider::DeleteInstance (const CInstance &Instance, long lFlags ){	// 使用Cinstance之Get函数(例如,呼叫GetCHString(L"Name", sTemp))相对于察看被客			户端要求的实例主要值	return (WBEM_E_PROVIDER_NOT_CAPABLE);}/********************************************************************** 函数:	CRichter_TimeServiceProvider::ExecMethod** 说明:	覆写此函数以提供对此方法的支援*				一个方法是为您的提供者要求您的类别并在上面执行某些函数,以及更进一                步改变状态的进入点(状态的改变应该被PutInstance() 处理)** 输入:	一个指向包含相对于被执行之实例的Cinstance指标*				一个A包含方法名称的字串*				一个指向包含IN参数的Cinstance指标*				一个指向包含OUT参数的Cinstance指标*				一组被方法指定的标记** 回传:	如果没有为此类别实作,则回传WBEM_E_PROVIDER_NOT_CAPABLE*				如果方法执行成功,回传WBEM_S_NO_ERROR*				如果执行方法时产生错误,则回传WBEM_E_FAILED** 注解:	目标:如果您不打算支援Methods,请移除此方法*********************************************************************/HRESULT CRichter_TimeServiceProvider::ExecMethod (const CInstance&Instance,						const BSTR bstrMethodName,						CInstance *pInParams,						CInstance *pOutParams,						long lFlags){	// 为非静态方法,使用Cinstance之Get函数(例如,呼叫GetCHString(L"Name", sTemp))	相对于察看客户端要求的主要实例值	return (WBEM_E_PROVIDER_NOT_CAPABLE);}
 列表8-1 TimeServiceProvider sample范例
TimeServiceProvider.mof// TimeServiceProvider.MOF//// 经由Microsoft WBEM Code Generation Engine产生//// 目标:如果这个类别预期会在一个命名空间中被建立,而不是另一个预设值//(root/default)时,您应该在这里新增 #pragma namespace命令。如果这些类别将进入您所拥有		   的命名空间,考虑在这里建立命名空间。请察看CIMWIN32.MOF以取得如何建立命名空间的范例。   同样的,考虑结合此mof和定义提供者所供应之类别的mof档		   ////============================================================// 注意:以下这行必须自行加入:#pragma namespace("////.//root//cimv2")//*************************************************************//***Registers Framework Provider ***//*************************************************************instance of __Win32Provider as $P{	Name = "TimeServiceProvider";	ClsId = "{ccb338cc-f3af-4d80-8bba-d8ba0f8c325d}";};instance of __InstanceProviderRegistration{	Provider = $P;	SupportsGet = TRUE;	SupportsPut = TRUE;	SupportsDelete = TRUE;	SupportsEnumeration = TRUE;	QuerySupportLevels = {"WQL:UnarySelect"};};instance of __MethodProviderRegistration{	Provider = $P;};// 注意:以下这行程序是自行加入的(从TimeServiceStart.mof):[provider("TimeServiceProvider"), dynamic]class Richter_TimeServiceProvider : Win32_Service {	string PipeName;};
 列表8-2 TimeServiceProvider范例之WMI提供者的MOF文件

固定结构的设定
 

许多服务要求保存固定结构的设定(我们曾在第五章中讨论如何为此目的而使用登录)。一个选择是,您可以在经由Win32_Service取回的类别中建立属性,或者使用内附在WMI中的一个标准提供者。为了使用这个提供者,您只需编写一个合适的MOF文件即可。

WMI在以下的登录机码下保留了它的所有结构资讯,例如,自动取回MOF文件的目录位置以及启动堆积(Heap)分配的大小:

HKEY_LOCAL_MACHINE/Software/Microsoft/WBEM

以下的MOF文件定义了从CIM_Setting类别取回的一个类别。取回的类别允许您使用WMI察看那些WBEM的登录设定:

#pragma namespace("////.//root//cimv2") // 实例提供者instance of __Win32Provider as $InstProv{	Name = "RegProv";	ClsId = "{fe9af5c0-d3b6-11ce-a5b6-00aa00680c3f}";};instance of __InstanceProviderRegistration{	Provider = $InstProv;	SupportsPut = TRUE;	SupportsGet = TRUE;	SupportsDelete = FALSE;	SupportsEnumeration = TRUE;};[dynamic,provider("RegProv"),ClassContext("local|hkey_local_Machine//software//microsoft//wbem")]class Richter_MySettings : CIM_Setting{	[key, PropertyContext("CIMOM")] string SettingId;;	[PropertyContext("Autorecover MOFs")] string AutoRecoverMOFs [];	[PropertyContext("Startup Heap Preallocation Size")]		uint32 HeapPreAllocationSize;};

说明

这些值可以透过Win32_WMISetting类别显示。这个MOF文件只做为示范用。

请注意此MOF文件的二个重要特性。第一,标准的登录提供者被宣告并使用「Win32Provider」和「instance of InstanceProviderRegistration」语法登录。如果一个开发者在CIMV2命名空间中使用这个登录提供者,则此宣告和登录可能已经被完成。第二次执行并不会产生不良的影响。

第二,设定子类别的宣告使用「class Richter_MySettings」陈述式处理。必须注意这个经由提供者修饰语使用的类别与登录提供者之关联,以及使用PropertyContext修饰语的属性与录登栏位之关联。PropertyContext修饰语指示了与登录值相对应的属性值。

  列表8-1 所示,说明了如何为第叁章所描述之TimeService建立一个WMI提供者之DLL。TimeServiceProvider使WMI可以看见TimeService服务的属性。透过对WMI的使用,一个客户端可以取回此属性。不像本书中的另一个范例,WMI SDK工具为此范例建立了原始程序代码。本节中,您将会学习到如何使用这些工具建立一个动态的扩展WMI的Win32_Service类别。这个范例的原始程序代码文件存放在附赠光碟的08-TimeServiceProvider目录中。现在我将透过一个简单的动态提供者来说明它的步骤。
 

你可能感兴趣的:(windows,Microsoft,服务器,service,资讯,程序开发)