欢迎光临 C++Builder 研究! 本站主要面向软件开发者(Developer/Programmer), 提供 C++Builder, Delphi, C/C++, VC++ 等相关的资料。发布信息请致信给
编程文档
本站首页 www.ccrun.com | 编程文档 |   关键字:

文件系统驱动编程基础篇之4——Wmi管理规范

关键字:文件系统驱动编程,Wmi管理规范

作者:wskjuf    更新:2008-10-06 22:12:43    浏览:19006

文件系统驱动编程基础篇之四——Wmi管理规范

文件系统驱动编程基础篇之四——WMI管理规范

一、前略

本系列文章为业余编程爱好者而写,仅仅作为初学者的一个借鉴,真正的精华存在于参考资料*中。知识的积累将经历从薄到厚,再从厚到薄的反复过程,为了打下牢固的基础,请读者务必在阅读本文的基础上花费必要的时间完成参考资料。 参考资料*:
1.《Programming the Microsoft Windows driver model》第一版(当前阶段主要阅读资料,阅读第十章)
2.《Windows Management Instrumentation (WMI)》(Msdn上关于WMI的标准文档,建议认真阅读)
3.《Microsoft Windows Management Instrumentation: Background and Overview》(3,4,5可用作参考,不严谨,且较陈旧)
4.《Windows Management Instrumentation: International Support Overview
5.《Microsoft Windows Management Instrumentation: Advantages to Developers
6.百度百科关于WMI有关名词的注释
7.《Windows Management Instrumentation (Windows 管理规范) 的秘密
8.《WDM Provider
8.《Managed Object Format (MOF)》(Msdn上关于MOF的标准文档,建议认真阅读)
9.《WMI Property Qualifiers
10.《MOF Data Types
11.《COM API for WMI
12.《WMI脚本入门》
13.《WQL (SQL for WMI)

阅读基础:了解COM组件的调用方法。

本章目的:了解WMI管理规范在系统下的广泛运用,深入理解MOF,学会编写内核模式和用户模式下的WMI程序。


二、WMI简介

一)基于Web的企业管理(Web-Based Enterprise Management (WBEM))的提出是为了解决企业在快速发展的过程中,总成本(Total cost of ownership(TCO)) 也随之快速增长的矛盾,它作为一项业界倡议,起始于1996年,规范了企业网络中受管资源的描述与使用。WBEM建立在通用信息模型(Common Information Model (CIM, 由Desktop Management Task Force,即DMTF推动的工业化标准))规划(schema)的基础上。WBEM提出了一个标准化的的方法用于建立统一的框架,不同技术和平台上产生的管理信息均以相同的形式供管理程序访问,这样就减少了维护费用和企业网络的寿命周期成本(life cycle costs)。(注:Common除了译为“通用”,也可译为“公共”)

从根本上说,WBEM提供了数据定义的信息标准和组件交互的处理标准。

二)CIM是一种机制,用于为受管资源建模并以受管对象格式(Managed Object Format (MOF))表现这些模型。使用 CIM 和 MOF,组成受管资源或资源网络的组件可以象在面向对象设计过程中使用的组件一样被建模和看待。
CIM由一个核心模型,许多通用模型以及扩展模型组成。核心模型是一系列类、连接和属性的集合,该对象组提供了所有管理域公用的基本信息模型;通用模型提供特定管理域的通用信息模型,这些特定的管理域,如系统、应用程序、设备、用户和网络等;扩展模型代表通用模型的特定技术扩展。

  • A core model—incorporates classes applicable to all management domains.
  • Common models—incorporate classes common to specific management domains, independent of particular technologies or implementations. Common domains include systems, applications, devices, users, and networks. These models provide a basis for the development of management applications and include a set of base classes for extension into technology-specific areas.
  • Extension models—these represent technology-specific extensions of the common models. These models are specific to environments, such as operating systems (for example, UNIX, or Microsoft Windows).

三)Windows Management Instrumentation——通常译为WMI管理规范,是微软提出的,与WBEM兼容的技术,同样也兼容于CIM2.0或2.5。WMI是Windows管理服务的主要组件,它提供的功能如下(从资料2节选,重点记忆红字即可mcieels):

  • A rich and consistent model of Windows 98 and Windows 2000 operation, configuration, and status. 是一个模型
  • A COM API that supplies a single point of access to all management information. 可COM访问
  • Interoperability with other Windows 2000 management services, which will simplify vendors' efforts to create well-integrated management applications. 可协作
  • A flexible architecture that allows vendors to extend the information model to cover new devices, applications, and other enhancements by writing code modules (WMI providers). 可扩展
  • A powerful event architecture that allows changes in management information to be identified, aggregated, compared to and associated with other management information, and forwarded to local or remote management applications. 有事件机制
  • A rich query language(WQL) that enables detailed queries of the information model. 可查询
  • A scriptable API, which enables management application developers to use Visual Basic? or Windows Script Host (WSH). 可脚本访问

图10-2是上图架构的简化,在WMI模型中,数据和事件被分成了消费者(Consumers)和生产者(Providers)两类。数据块就是抽象类的实例,其概念与C++中的类概念一致。如同C++中的类,WMI类也有数据成员和实现对象行为的方法。数据块中的内容并不是由WMI指定,而是由数据生产者和数据的使用目的决定的。送往驱动程序的数据最有可能来自管理者本身的操作。而驱动程序发出的数据通常是某种性能的统计数据,这些数据的消费者可能是某个性能监视程序。
?

WMI类允许同时存在全球化和本地化的数据,详看资料3。
WMI类又可称为主类(master class),由基类(basic class)和amendment(修正,纠正之意,用于本地化,难以揣摩合适的译文^_^)类组成。主类包含所有的属性集和限定(qualifiers)。基类是主类的子集,包含所有的属性集和部分限定,不包含本地限定(localizable qualifiers)。amendment和主类具有相同的名字,是一个抽象类,包含了关于本地限定的属性子集(includes a subset of properties with localizable qualifiers),不包含主类的其他属性。
amendment总是位于包含基类定义的命名空间下的子命名空间。每一个子命名空间包含着特别本地化(particular locale)的amendment类,这样设计的结果是多种语言的子命名空间可以被加入到储存库(repository,见架构图)中,因此可同时存在多种语言的类定义:

ROOT\CIMV2
ROOT\CIMV2\MS_409
ROOT\CIMV2\MS_407

WMI允许存在多重命名空间,每个命名空间中包含的类属于一个或多个用户模式生产者。生产者使用平台SDK中公开的COM接口来注册Windows管理服务(Windows Management Service)。操作系统(包括所有设备驱动程序)支持一个名为root\cimv2的命名空间,里面包含了CIM版本2。

四)WDM驱动程序可以作为WMI类实例的生产者。一个描述了驱动程序支持的各种类(驱动程序可以为这些类提供数据)的脚本称为驱动程序规划(schema)。我们可以使用MOF(Managed Object Format)语言定义规划。系统则维护一个称为储存库(repository)的数据字典,它包含了所有已知的规划定义。如果驱动程序做得正确,系统将在初始化驱动程序时自动把规划放到储存库中。
WDM生产者是WMI生产者的一个组成部分,它可以访问WDM硬件驱动的类、实例、方法和事件。硬件驱动的类位于root\wmi命名空间,Wmi.mof和Wmicore.mof定义了主要的WDM类。WDM生产者允许管理程序从满足WMI-for-WDM的设备驱动访问数据和事件,生产者主要以IWbemServices接口的形式提供这些服务。
为了查看命名空间,可到微软站点下载安装WMITools软件。WMI CIM Studio以树视图的形式展现了分层的命名空间。

三、MOF与WQL语言

MOF是一种基于接口定义语言 (IDL) 的语言,用于描述管理信息,即用于描述CIM。MOF 语法是以文本形式描述对象定义的方法。MOF 汇编器(如mofcomp.exe)处理 MOF 文件,并向 CIM 储存库添加必需的对象定义。C++Builder类型库(Type Library)也使用了IDL。
MOF语法类似于C++,但远比C++简单,如果在学习过程中遇到困难,可以联想两者的相似处。如MOF类的实例化可以想象成C++的构造函数,引用也可以联想成C++的&。
一个简单的MOF文件master.mof如下:


MOF和C++类定义有相似之处,也使用#pragma预处理。#pragma amendment指示编译器输出语言中性和语言特定的两个版本,“MS_409”表示本地标识符(LCID),类似还有“MS_408”等。
MOF的数据类型见资料10
MOF包含了丰富的限定(Qualifier ,[ ]里以逗号分隔的标识符),用于描述类、实例、属性、方法和方法的参数。限定名不区分大小写,除此之外,它还遵守类似于C++命名的一些约束。限定可分为三大类:标准限定(Standard qualifier),CIM限定(CIM qualifier),特殊限定(Unique qualifier),你还可以为自己的生产者创建自定义的限定。
限定可以被称为“限定风格”(Qualifier Flavor)的标志来修饰,语法为:

[qualifier1 : flavor1 flavor2 flavor3, qualifier2 : flavor1]

因此,综合了限定以及限定风格的分类后也可进行如下的划分(*为WDM里常见):


Qualifier Type

Description

注释

Meta

Refines the definition of meta-constructs by clarifying the actual usage of a class or property declaration.

通过MOF语法阐明类或属性声明的实际用途,元限定完善了CIM模式元结构的定义。

Standard*

Supports the descriptions that all CIM-compliant implementations must handle.

Optional

Addresses situations not common to all CIM-compliant implementations.

WMI-specific*

Describes qualifiers specific to WMI, such as performance counter class qualifiers.

Qualifier Flavors

Provides additional information about a qualifier, such as whether a derived class or instance can override the qualifier's original value.

一些限定与风格:

Qualifier

分类

注释

示例

Amended

Flavor

基类不用该限定,amendment类用于本地化

DisplayName("User Name") : Amended

Amendment

WMI-specific – Standard

指明类里包含被本地化的amended限定

Amendment

Description

Standard

描述了被命名的元素,默认为NULL

Description("This property shows when the object was created")

DisplayName

Standard

代替真实的元素名而显示在UI的名字

DisplayName("Time Stamp")

Dynamic

WMI-specific – Standard

指明类的实例被动态创建

Dynamic

Guid

自定义

必须,驱动程序用来辨别生产者

见wmi42.mof

Key*

Standard

键属性用来标识和区分每一个实例

Key

Locale

WMI-specific – Standard

为类或实例指定语言

Locale(0x409)

Provider

WMI-specific – Standard

限定的值是动态生产者的名字,生产者创建类实例和更新实例数据

Provider("WMIProv")

WMI

自定义

表示生产者类型

见wmi42.mof

WmiDataId

WMI-specific – WDM

Index in the WNODE of the data for the property. The WDM provider uses this qualifier to determine how the data is formatted while extracting data from the WNODE and generating WMI classes. The starting value is 1. (除了InstanceName和Active属性,其他属性都必须带有该Id)

WmiDataId(1)

* Key:Msdn上存在矛盾的说明。错误的说明是认为仅InstanceName能被声明为Key,但实际上可由多个属性组成复合键。

对于难以理解的限定,可通过CIM Studio来查看实际的效果。如属于Meta类别的Association是一个难以琢磨的限定,请看下面的mof文件:
<// 本文转自 C++Builder研究 - http://www.ccrun.com/article.asp?i=1057&d=d1f203
/p>

#pragma namespace("\\\\.\\root") instance of __Namespace
{
Name = "WMI" ;
} ;
#pragma namespace("\\\\.\\root\\WMI")
Class A{
[key] string aKey;
};
Class C{
[key] string cKey;
};
Class D{
[key] string dKey;
};
Class E{
[key] string eKey;
};
// The following class creates an association between the "A", "C", "D", "E" class
[Association] Class B{
[key] A ref aRef;
[Key, Min(1)] C ref cRef;
[key] D ref dRef;
[key] E ref eRef;
};

它在命名空间root\wmi下创建了A、B、C、D、E五个类,它们的Association关系分别为:
???…

MOF汇编器常用的三种操作:

  • 检查MOF文件语法:mofcomp –check master.mof
  • 创建语言中性和语言特定的MOF文件:mofcomp -MOF:g.mof -MFL:l.mof master.mof
  • 编译成二进制的BMF文件,该文件可以自定义资源的方式加入C++工程的资源文件中:mofcomp –B: bin.bmf l.mof

// g.mof,0x409 = 1033
[LOCALE(1033)]
class myclass
{
[key] string Name;
uint64 Value;
uint64 Timestamp;
};

// l.mof
#pragma namespace("\\\\.\\root\\default")
instance of __namespace{ name="ms_409";};
#pragma namespace("\\\\.\\root\\default\\ms_409")
[Description("Localized version of MyClass for American English") : Amended,AMENDMENT, LOCALE(0x409)]
class myclass
{
[DisplayName("User Name") : Amended,Description("The Name property contains the name of the user") : Amended,key] string Name;
[DisplayName("Time Stamp") : Amended,Description("This property shows when the object was created") : Amended] uint64 Timestamp;
};

注释// 或 /* */并非可随处添加,如汇编器提示“…文件域意外的符号…”时要特别注意注释是否放于错误的位置上。
instance of __namespace一行实例化了ms_409命名空间(即root\DEFAULT\ms_409),下一行则在这个命名空间加载myclass类。

通过语法检查的mof文件可以加入WMI命名空间(N表示默认加载的空间):

mofcomp -N:root\default g.mof

使用CIM Studio查看储存库里的g.mof,对于l.mof,Value不会出现在属性表中:
?

通过CIM Studio里的MOF Generator工具还可以生成你感兴趣的节点的MOF文件。
请阅读如下toaster.mof,理解包含的语法,并在WMI命名空间下验证自己的理解:

[Dynamic, Provider("WMIProv"),
WMI,
Description("Toaster driver information"),
guid("{BBA21300-6DD3-11d2-B844-00C04FAD5171}"),
locale("MS\\0x409")]
class ToasterDeviceInformation
{
[key, read]
string InstanceName;
[read] boolean Active;

[WmiDataId(1),
read,
WmiEnum{"0=I8042 Connector"
"1=Serial Connector",
"2=Parallel Connector",
"3=USB Connector" },
Description("How the toaster is connected to the computer")]
uint32 ConnectorType;

[WmiDataId(2),
read,
Description("This indicates the capacity in Kilo Watts of the toaster device.")]
uint32 Capacity;

[WmiDataId(3),
read,
Description("Number of errors that occurred on this device")]
uint32 ErrorCount;

[WmiDataId(4),
read,
Description("Indicates the number of controls on the toaster device.")]
uint32 Controls;

[WmiDataId(5),
read,
write,
Description("The DebugPrintLevel property indicates the debug output level of toaster device.")]
uint32 DebugPrintLevel;

[WmiDataId(6),
read,
Description("ModelName")]
string ModelName;
};
[WMI, Dynamic, Provider("WMIProv"),
guid("{01CDAFF1-C901-45b4-B359-B5542725E29C}"),
locale("MS\\0x409"),
WmiExpense(1),
Description("Notify Toaster Arrival")]
class ToasterNotifyDeviceArrival : WMIEvent
{
[key, read]
string InstanceName;

[read]
boolean Active;

[read,
Description("Device Model Name"),
WmiDataId(1)] string ModelName;
};

为了解系统默认生成的命名空间,可阅读system32\wbem路径下的MOF文件。为了掌握MOF的语法,可阅读DDK源代码目录下的MOF文件。
WQL是Ansi-SQL的简化版本,它可以用来查询命名空间下的类,如select * from wmi42,对此不再详述。

四、WMI与驱动程序

请先阅读资料1第二节,以及wmi42示例的SYS部分。
驱动程序对WMI的支持,体现在对系统控制IRP,即IRP_MJ_SYSTEM_CONTROL的支持上。除了以Ioctl类似的方式实现该IRP(AltWmi.cpp),更简单的方式是委托WMILIB来支持WMI(Wmi.cpp),这涉及到一个数据结构:

// This structure supplies context information for WMILIB to process the
// WMI irps. Memory for this structure may be paged.
typedef struct _WMILIB_CONTEXT
{
// WMI data block guid registration info
ULONG GuidCount;
PWMIGUIDREGINFO GuidList;

// WMI functionality callbacks
PWMI_QUERY_REGINFO QueryWmiRegInfo;
PWMI_QUERY_DATABLOCK QueryWmiDataBlock;
PWMI_SET_DATABLOCK SetWmiDataBlock;
PWMI_SET_DATAITEM SetWmiDataItem;
PWMI_EXECUTE_METHOD ExecuteWmiMethod;
PWMI_FUNCTION_CONTROL WmiFunctionControl;
} WMILIB_CONTEXT, *PWMILIB_CONTEXT;

Wmi.cpp是如何处理WMI请求的?针对系统控制IRP里的几个副功能码,我们定义了相应的回调函数,WMILIB_CONTEXT结构里的回调函数指针指向我们的函数。当WMI派遣例程DispatchWmi调用WmiSystemControl来处理IRP时,就会自动调用这些函数。AddDevice和RemoveDevice例程调用IoWMIRegistrationControl为设备对象注册或注销作为WMI数据生产者(WMI data provider)的驱动程序。注册之后,第一个被处理的系统控制IRP的副功能码是IRP_MN_REGINFO,派遣函数QueryRegInfo被调用以处理该功能码。在QueryRegInfo里,返回了系统用于创建数据块的MOF资源的名字。其他派遣函数就此略过,Msdn里对每个函数都有详细的叙述。
Wmi42正确启动后应该符合下图的情形(如果在安装上碰到问题,可暂时跳过,下篇将对安装问题作进一步的说明):
?

留有余力的读者,可进一步研究WMI的事件机制,在此略过。

五、WMI、COM与用户程序

请阅读资料1第三节,以及Chap10\WMI42示例的TEST部分。
本篇是初次涉及COM编程,今后我们还将陆续接触COM在各领域的应用。COM既是一种技术,也是一种组件,这种组件对外公开了一些称为“接口”的抽象类,除此以外都是不透明的。假如我们剖开外壳可以看到内部的实体,即不是接口的那些类,由被称为“类厂”的类来创建(即用类来创建类)。实体通过接口对外提供了COM的所有服务。接口、实体、类厂可看成COM组件的基本元素。
接口的使用有一个很重要的约定——凡是查询/获取一次接口,都要用AddRef()增加一次计数,接口使用后都要用Release()释放本次计数。有时候我们使用API函数后没有调用AddRef(),却需要Release(),原因是这些函数内部已有调用代码。这样的常用API并没有几个,我们可以通过实践很快的掌握,一些不确定是否需要释放计数,以及如何释放计数的API可以查阅Msdn。
COM出现后不久,一些“懒人”写出了自动维护约定的智能指针,随后ATL出现了。客观的说,微软在技术上的创新贡献是有目共睹的,但是ATL?它把所有看到的东西一律封装成模板,这样你可以仅用ATL就可以完成一个实用的Windows程序了,它的技术书籍重得甚至可拿来压舱底。相比大多数人的敬而远之,微软虽然也在逐渐淡化COM,但即便如此,今天很多的新生技术外表下都隐藏着COM。
WMI也是通过COM接口提供服务,详见资料11
TEST示例首先初始化COM和为进程设置了默认的安全值,接着打开root\wmi命名空间,设置接口权限,最后报告Wmi42类属性的信息。

在此例的基础上,可深入学习各类接口函数,弄清MOF语法如何以COM接口方式调用。
代码《let_us_try_wmi_samples》,《WMI C++ Application Examples》演示了如何使用WMI访问常见硬件,后篇系统的总结了用户模式下调用WMI的基本步骤。另可阅读Msdn上关于Vista下使用WMI的论述。

六、IClientSecurity接口与安全

vs2005的Msdn里并没有IClientSecurity接口的说明(官方站点有此说明的),既然微软似乎有意忘记了,笔者也实在不愿花费时间陷入细节中去,我们可在在实践中来体会该接口的功能。

IClientSecurity

Gives the client control over the security settings for each individual interface proxy of an object. The methods of IClientSecurity can be used to set or query the security settings of a specific interface proxy or to copy an interface proxy.

Every object has one proxy manager, and every proxy manager exposes the IClientSecurity interface automatically. Therefore, the client can query the proxy manager of an object for IClientSecurity, using any interface pointer on the object. If the QueryInterface call succeeds, the IClientSecurity pointer can be used to call an IClientSecurity method, passing a pointer to the interface proxy that the client is interested in. If a call to QueryInterface for IClientSecurity fails, either the object is implemented in-process or it is remoted by a custom marshaler that does not support security. (A custom marshaler can support security by offering the IClientSecurity interface to the client.)

The interface proxies passed as parameters to IClientSecurity methods must be from the same object as the IClientSecurity interface. That is, each object has a distinct IClientSecurity interface: calling IClientSecurity on one object and passing a proxy to another object will not work. Also, you cannot pass an interface to an IClientSecurity method if the interface does not use a proxy. This means that interfaces implemented locally by the proxy manager cannot be passed to IClientSecurity methods, except for IUnknown, which is the exception to this rule.

For more information about proxies, see IMarshal - Default Implementation.

When to Implement

The proxy manager for each object provides an implementation of IClientSecurity, so you would typically not implement this interface. If, however, you are defining objects that support custom marshaling, you may choose to implement IClientSecurity on the objects' custom proxies to maintain a consistent programming model for the objects' client applications. You may also choose to support this interface on in-process objects.

When to Use

Call the methods of this interface to examine or modify the security settings of a particular connection to an out-of-process object. For example, you might temporarily establish a higher security level — one with complex encryption — only for the period when sensitive information or data is being sent to the object. Alternately, you might establish different proxies to the same object with different security levels. You could use these security levels to support different clients that are calling your object or to support different operations within your application.

Methods in Vtable Order

IClientSecurity Methods Description
QueryBlanket

Retrieves authentication information.

SetBlanket Sets the authentication information that will be used to make calls on the specified proxy.
CopyProxy

Makes a copy of the specified proxy.

七、结语
除了Ioctl,现在我们又掌握了一个了解系统的重要手段,读者在关注WMI细节的同时,也请留意一下系统性的事物如何把各个分系统进行整合。
本篇作为选读内容,不设参考完成时间。

上篇文章:文件系统驱动编程基础篇之3——Ioctl控制操作
下篇文章:浅析C++中内存分配的方式
相关搜索:
  中搜索“文件系统驱动编程基础篇之4——Wmi管理规范 ”相关内容
  中搜索“文件系统驱动编程基础篇之4——Wmi管理规范 ”相关内容
  中搜索“文件系统驱动编程基础篇之4——Wmi管理规范 ”相关内容
  中搜索“文件系统驱动编程基础篇之4——Wmi管理规范 ”相关内容
  中搜索“文件系统驱动编程基础篇之4——Wmi管理规范 ”相关内容
  中搜索“文件系统驱动编程基础篇之4——Wmi管理规范 ”相关内容
C++Builder 研究 - http://www.ccrun.com © 2001,2011  总访问量: 43667518  来访IP: 23.20.54.196  晋ICP备05000574号
Tags: Borland CodeGear Embarcadero C++Builder Delphi VC++ C/C++ RAD Studio BCB BDS Source Code VCL MFC COM SDK Components Controls Developer Programmer 编程学习资料 源代码 源程序 源码 编程文档 经验技巧 组件 控件 元件 开源 函数 软件开发 一切尽在C++Builder研究!