WMI简介
按照wikipedia的解释,WMI是Windows驱动模型的扩展集合,通过工具化的组件提供了操作系统的接口。WMI本身是基于Webm和CIM工业标准的管理架构Microsoft实现。相对于程序员,系统管理员可能更熟悉这些接口。WMI通过一种一致的面向对象的封装,提供了绝大部分操作系统核心功能的支持,可以直接使用在Windows 2000 SP4以上的机器中。对于缺省不支持WMI的OS,如Windows 2000之前的操作系统,有独立的安装包可以下载。
因为WMI主要是针对系统管理的,稍微了解一下你就可以知道,WMI似乎“生来”就对各种脚本语言友好。更新的powershell就不说了,VBscript一直使用WMI的最佳选择。C#的支持也似乎不错。
WMI基础
上面已经提到,WMI是微软的CIM工业标准的实现,提供了对Windows进行管理的完备详尽的支持。对比程序员来说,Windows系统管理员可能更熟悉Windows的WMI接口,因为这是使用脚本化的基础自动化日常管理任务的最好手段。Windows程序员一般非常熟悉win32 API并且针对此编程。不幸的是,有一些Windows服务并不提供基于Win32 API的接口支持,如HyperV。关于具体的WMI相关的信息,请参阅MSDN文档。简单说来,WMI提供了:
1. 完整的基于对象的管理模式。所有的可管理项都被抽象成具体的对象。而这些对象的模板被称为类
2. 类可以从其他的类中继承。
3. 一致的对象访问方法。
为了有效地熟悉WMI,建议安装WMIStudio工具,可以在网上直接下载到。
WMI对C/C++实现者的挑战
由于WMI支持的编程模型的限制,哪怕是一个简答查询,典型的情况也需要几十行代码,更不要说函数调用。MSDN有一些WMI的使用例子,你可以看出来,使用C++调用WMI简直就是复杂的要死。如果说C++要调用WMI就必须这么复杂的话,我们只能说C++根本就不适合这里。不过,C++的长处是为可能的应用提供基本的支持。在彻底放弃之前,我们还是试一试简单的C++封装能不能有效简化WMI的使用。
我们这里先列出使用WMI需要处理的的可能挑战(最好对比个MSDN的例子来看)
1. COM支持;
2. WMI locator。也就是说执行WMI命令的实体代理,可以是本地的,也可以是远端的;
3. WMI变量。WMI吧所有的支持的变量类型封装到VARIANT结构中,对于不同的类型,提供不同的宏来访问;
4. WMI对象。对于任何的WMI实体,都对应于一个COM对象;
5. 对象属性。对象可以有一个或者多个属性;
6. 对于函数调用,则有输入对象和输出对象的处理;
7. WMI查询,对象方法调用
8. 异步WMI调用的处理
使用C++封装WMI调用
1. COM执行环境的封装。使用RAII技术,可以简单地实现COM执行环境的封装。这样在程序的入口处调用enable_COM_support()就可以了。
2. WMI执行代理。在创建执行代理之前,我们必须创建WMI服务器对象。因为我们同时要保证执行代理可以方便地传来传去,我们对其内部引用的对象使用了基于引用计数的自动销毁策略。又是RAII:)
注意,wbem_server和the_locator是两个内部类,因为客户代码从来不用关心这些。对于他们,有一个执行代理就足够了。有了这个实现,我们就可以提供简单的辅助实现,直接返回本地的或者远程的执行代理。
3. 其实,对WMI变量的封装不是必须的,但是有几个我们认为重要的原因促使我们这样做:1. WMI变量使用是需要首先构造,而使用后需要释放;2. 使用WMI变量需要使用大量的宏,难以保证类型安全。基于此,我们实现了WMI变量的封装,同样,还是以RAII基础为基础。
其中我们使用了一个难看的宏来消除访问不同类型数组是导致的代码重复。需要注意的是,这个封装不是完备的,仅仅实现了常用的类型。如果需要增加新的类型,只需按照类似的模式加入即可。
4. WMI对象。和其他的WMI编程使用的实例一样,WMI对象也是基于RAII的,我们需要频繁地传递WMI对象,也加上了引用计数的支持。
5. 对象属性的封装。对象可能有对各属性,所以我们提供一个属性集类,通过它可以遍历对象的所有的属性。
6. 函数调用,我们封装了IN和OUT参数如下:
7. 有了上述的支持,WMI查询和调用就可以实现了,下面是代码:
完了。很多代码,不是吗?,幸运的是,我们只需要写一次即可。
实例
这是使用上述代码的例子。
注意,例子中硬编码了字串“Win32_TerminalServiceSetting”,这个是对应的WMI类的名字,这个是必须的。当然还有其他的方法可以消除这个限制,至少隐藏起来。
对比MSDN提供的代码,你会发现,使用我们封装后接口的代码少而且直观。Bjarne曾经说过4点学习C++应该关注的重点:问题域中最基本的对象;对象之间的关系;资源管理以及错误管理。完全可以用来检查这个实现。
注意,这只是日常工作中使用的对应于我们的需求的一个简明版本,还有调优的余地,也可能不能满足你的要求。但是通过这个例子,我想说明的是C++提供足够的强大的抽象机制和工具用来处理实现过程中遭遇的复杂性。C++程序员再也不用在使用WMI这一问题上羡慕脚本程序员了!
298





