使用 Windows Management Instrumentation (WMI) - 第一部分






4.67/5 (10投票s)
本文向读者介绍了 Windows Management Instrumentation 以及如何使用 C# 编写 WMI 客户端代码。
引言
本文的重点是向读者介绍 Windows Management Instrumentation (WMI) 的概念,以及如何使用 .NET 平台上的 C# 来构建强大的应用程序。WMI 是 Microsoft 对 Web Based Enterprise Management (WBEM) 标准的实现,而 WBEM 又基于 Common Information Model (CIM)。WMI 为开发人员或管理员提供了一种非常一致的方式,可以在 Windows Server 上本地或远程执行各种复杂的系统管理任务。过去,此类管理任务可能需要使用 Microsoft 或第三方供应商提供的不同 API 接口,或者通过直接访问特定的注册表项并查询其值来完成。随着越来越多的供应商采用 WMI 标准,如今您可以找到大多数设备供应商公开的 WMI 类,允许最终用户/开发人员/管理员快速编写管理工具,而无需学习新的 API 或直接操作系统注册表。这是本文的第一部分,我将向您介绍所有基础知识,帮助您对 WMI 有一个良好的理解。在后续部分,我将实际处理一些现实生活中的挑战,并引导您完成完整的开发过程。
背景
Windows Management Instrumentation (WMI) 是由 Desktop Management Task Force (DMTF) 推广的 WBEM 标准的实现。WMI 是一种面向对象的服务器管理方法,基于 Common Information Model (CIM)。CIM 是一种面向对象的设计模式,能够表示广泛的对象。它使用抽象、继承、依赖关系、聚合和范围界定等面向对象特性,提供一个可扩展的基础模式,能够表示构成 Windows Server 构成要素(也称为托管实体)的所有对象的详细信息。WMI 为开发人员提供了一种标准的方式来编写脚本和应用程序,以管理其 Windows Server 的多个方面,包括连接到这些服务器的设备。这种方法的优点是它在不同的设备和服务集之间非常标准化,这意味着开发人员只需要专注于使用 WMI 方法和类来完成工作。使用 WMI 的另一个主要优点是它对所有类型的开发人员都非常易于访问,因为 WMI 方法和函数可以直接从 VBScript、Perl 脚本等脚本语言,以及 .NET 平台上的 C++ 和 C# 等高级语言中使用。因此,开发复杂的可管理性解决方案的能力对于广泛的开发人员来说变得易于实现。
WMI 的基本架构概念
WMI 架构由三个主要层组成
消费者 (Consumers) - 这基本上是使用公开的类并完成工作的层。这可以包括用任何支持的语言编写的脚本和可执行文件,以便查询 WMI 类并获取任何设备特定的信息。
WMI 基础设施 (WMI Infrastructure) - 这基本上是从消费者接收调用并将其传递给托管设备或元素的组件。此层中的主要组件是 CIM 对象管理器 (CIMOM) 和 CIM 命名空间。CIMOM 帮助实现客户端和设备之间的连接。
托管资源 (Managed Resources) - 这包括通过提供程序提供信息的设备、服务或任何其他实体。这些信息以类、方法和属性的形式提供。WMI 提供程序基本上是托管资源与其到实际 CIM 对象模型的本地接口之间的层。
下面的架构图显示了 WMI 的详细架构,我还在图中用红色框出了我们主要在代码中处理的组件(架构图来自 MSDN 文档)。

在本文中,我们将重点关注消费者,我将向您展示如何用 C# 编写功能齐全的客户端代码,该代码可以连接到 CIM 命名空间、进行身份验证、实例化 `Win32_LogicalDisk` 类的对象,然后打印与该对象相关的某些属性。这应该能让您很好地掌握如何尝试编写自己的客户端并检查公开 WMI 类的其他资源。
使用 WMI 构建查询系统上所有逻辑磁盘属性的客户端应用程序
WMI 类可以被实例化,然后可以使用 WMI 查询语言 (WQL) 查询对象。WQL 的用法类似于数据库中的 SQL 查询。在查询实例化的 WMI 对象时,需要遵循特定的步骤。本节将清楚地概述所有这些步骤。
在本节中,我们有兴趣使用 `Win32_LogicalDisks` 类。所以首先,用户需要熟悉 `Win32_LogicalDisks` 类及其公开的方法和属性。`Win32_LogicalDisk` 类继承自 `CIM_LogicalDisk` 类。下面的代码片段显示了 MSDN 中 `Win32_LogicalDisk` 类的定义。首先需要清楚地理解您的客户端代码需要该类的哪些内容。
class Win32_LogicalDisk : CIM_LogicalDisk
{
uint16 Access;
uint16 Availability;
uint64 BlockSize;
string Caption;
boolean Compressed;
uint32 ConfigManagerErrorCode;
boolean ConfigManagerUserConfig;
string CreationClassName;
string Description;
string DeviceID;
uint32 DriveType;
boolean ErrorCleared;
string ErrorDescription;
string ErrorMethodology;
string FileSystem;
uint64 FreeSpace;
datetime InstallDate;
uint32 LastErrorCode;
uint32 MaximumComponentLength;
uint32 MediaType;
string Name;
uint64 NumberOfBlocks;
string PNPDeviceID;
uint16 PowerManagementCapabilities[];
boolean PowerManagementSupported;
string ProviderName;
string Purpose;
boolean QuotasDisabled;
boolean QuotasIncomplete;
boolean QuotasRebuilding;
uint64 Size;
string Status;
uint16 StatusInfo;
boolean SupportsDiskQuotas;
boolean SupportsFileBasedCompression;
string SystemCreationClassName;
string SystemName;
boolean VolumeDirty;
string VolumeName;
string VolumeSerialNumber;
};
请记住,MSDN 提供了所有 WMI 类的非常好的文档。因此,它是您了解类成员(包括类公开的属性和方法)的好地方。通常这时会有一个问题:我怎么知道系统中存在哪些类以及哪些类已被公开?这很简单,更复杂的方法是编写一个小型实用程序来枚举给定服务器上公开的所有类,但是更简单、更快捷的替代方法是使用公开可用的免费实用程序,例如 CIM Browser,它甚至随 WMI SDK 一起提供(或可以单独下载)。我将在本文末尾提供指向任何此类有趣工具的链接。
我想详细说明每次从任何应用程序使用 WMI 类时都必须遵循的标准步骤。一旦您理解了这些步骤,就可以几乎是模板化地开始以类似的方式使用其他类,或者如果您的应用程序需要处理多个类,您甚至可以使您的代码非常通用。
步骤 1 - 首先将 "System.Management.*" 导入到您的应用程序代码中。您将需要此程序集中公开的所有方法来操作和实例化 WMI 类。在客户端代码中执行所有 WMI 相关操作将需要以下对象
ConnectionOptions
ManagementScope
ObjectQuery
ManagementObjectSearcher
ManagementObjectCollection
ManagementObject
步骤 2 - 在这个例子中,我将向您展示如何将 WMI 客户端连接到远程系统,我将假设我们将始终查询远程系统(本地系统可以跳过此步骤)。一旦您完成了远程连接和执行,本地运行将更加简单和合乎逻辑。因此,步骤 2 基本上是创建一个 `ConnectionOptions` 对象。`ConnectionObject` 用于存储 `UserName` 和 `Password`,以便您的客户端能够连接到远程系统。如果正在执行的系统已登录,则不需要此项。
ConnectionOptions oConnect = new ConnectionOptions();
oConnect.Username = "CodeProject";
oConnect.Password = "Welcome";
步骤 3 - 现在我们需要设置一个我们要执行操作的命名空间。命名空间是 WMI 类和 CIM 类注册的地方。所有后续操作都在此命名空间的上下文中进行。下面的代码片段显示了如何创建和设置管理范围。`\Root\CimV2` 是 `Win32_LogicalDisk` 定义的命名空间的路径。
System.Management.ManagementScope oMgmtScope =
new System.Management.ManagementScope(oConnect, "\\root\\cimv2");
如果您注意到,我们通过将连接字符串传递给我们在步骤 2 中创建的远程系统,以及我们希望连接到的命名空间字符串(在本例中为 `root\cimv2`),来为我们的客户端创建一个新的管理范围。
步骤 4 - 一旦我们连接到命名空间并获得了管理范围,现在就必须创建一个可以我们在 WMI 类上执行的查询字符串。正如我之前提到的,WQL 类似于 SQL,用于查询和选择所需的属性及其值。下面的代码片段显示了如何形成一个查询并实例化一个查询对象。
System.Management.ObjectQuery oQuery =
new System.Management.ObjectQuery("select DeviceID,
DriveType,
FileSystem, FreeSpace,Size,Name from Win32_LogicalDisk where DriveType=3");
所以基本上我们正在创建一个查询对象,该对象将随后用于检索系统中所有逻辑驱动器的值。正如您所看到的,这使用了标准的 SQL 风格的查询语言来从类中选择属性。
步骤 5 - 一旦我们创建了查询对象,我们就需要在我们已经创建的管理范围内执行查询。
ManagementObjectSearcher oSearch = new ManagementObjectSearcher(oMgmtScope,oQuery);
步骤 6 - 一旦查询执行完毕,我们需要将结果存储在一个 `ManagementCollection` 对象中,然后可以使用该对象进行迭代,并为系统中每个逻辑磁盘检索结果值。
ManagementObjectCollection oCollection = oSearch.Get();
步骤 7 - 现在迭代 `oCollection` 对象,并检索系统中找到的每个逻辑驱动器的值。
//loop through all the logical drives and write out
//the details for the attributes requested
foreach( ManagementObject oReturnCollection in oCollection )
{
DeviceID, DriveType, FileSystem
//DeviceID
Console.WriteLine("Device ID : " + oReturnCollection["DeviceID"].ToString());
//DriveType
Console.WriteLine("Drive Type : " + oReturnCollection["DriveType"].ToString());
//FileSystem
Console.WriteLine("File System : " + oReturnCollection["FileSystem"].ToString());
// Disk name
Console.WriteLine("Name : " + oReturnCollection["Name"].ToString());
// Free Space in bytes
Console.WriteLine("FreeSpace: " + oReturnCollection["FreeSpace"].ToString());
// Size in bytes
Console.WriteLine("Size: " + oReturnCollection["Size"].ToString());
}
这完成了使用 C# 进行 WMI 编程的介绍。正如您所看到的,上面概述的步骤与您尝试使用的任何类都相同。您只需要知道您正在连接的命名空间以及需要使用的类。MSDN 提供了大量的文档,如果您处理的是专有设备,那么供应商应该提供了足够的文档。在 下一部分中,我们将介绍一个更高级的应用程序。我将介绍如何使用 `\root\virtualization` 命名空间提供的 WMI 类来管理 Hyper-V 虚拟机。我们还将学习如何执行方法。
希望本文能帮助您开始尝试不同的类。如果您安装了 CIM Browser,那么您可以快速浏览有趣的类,为您编写客户端代码并提取有价值的信息。也许在您转向更高级的应用程序之前,这是一个不错的练习。读者可以安装其平台的最新 WMI SDK,SDK 附带了所需的库和示例代码。
参考文献
- MSDN 在线 API 参考 WMI SDK 示例和文档
历史
- 2010 年 1 月 25 日:初始发布