VBGood网站全文搜索 Google

搜索VBGood全站网页(全文搜索)
首页 - 经验之谈 - Windows 管理操作系统--在你的企业中管理Windows和应用程序(二)
发表评论(0)作者:Jeffrey Cooperstein, 平台:VB6.0+Win98, 阅读:14060, 日期:2001-08-15
对象路径

在文章的前面部分提到的描述名称空间和类的语法结构,引入了对象路径的概念。对象路径就是用来确认计算机,名称空间,类,以及事例的函数。对象路径的绝对形式确定了计算机,和名称空间。例如,\\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系统,我想你将会发现它是一个不可缺少的工具的。