WMI与.NET框架的交互

.NET框架中,Windows Management Instrumentation(WMI)提供了一种机制,允许开发者访问和操作Windows操作系统的管理信息。WMI对象在.NET框架中通过System.Management命名空间进行访问。但是,直接访问WMI对象的成员并不直观,需要通过特定的方法来实现。

例如,在C#中,要访问WMI对象的属性,不能直接使用点操作符(.),而必须使用ManagementObject.Properties["PropertyName"]或者ManagementObject["PropertyName"]来获取一个PropertyData对象,然后通过PropertyData.Value来获取属性值。

ManagementObject instance; PropertyData data = instance["PropertyName"]; Console.WriteLine(data.Value);

PropertyData对象还提供了其他属性,如Type、IsLocal、IsArray和Qualifiers,这些属性可以让获取关于PropertyData对象的额外信息。Qualifiers属性返回一个QualifierDataCollection,这是一个包含该属性所有WMI限定符的集合。

以下是一个使用System.Management命名空间获取Win32_Process信息的示例:

using System; using System.Management; class Program { static void Main() { string className = "Win32_Process"; ManagementClass WMIClass = new ManagementClass(className); ManagementObjectCollection instances = WMIClass.GetInstances(); foreach (ManagementObject instance in instances) { Console.WriteLine("Process Name:\t " + instance["Name"]); Console.WriteLine("Command Line:\t " + instance["CommandLine"] + "\n"); } } }

虽然这种方法可行,但直接访问对象成员会更简单。为了实现这一点,可以使用.NETFramework SDK附带的一个命令行工具:Management Strongly Typed Class Generator(mgmtclassgen.exe)。这个工具的基本语法是:

mgmtclassgen <WMI class name> /P output file path

例如:

mgmtclassgen Win32_Process /P C:\Win32_Process.cs

执行这个命令后,会创建一个名为Win32_Process.cs的C#源文件。这个文件可以被包含在Visual Studio项目中。打开Win32_Process.cs文件,会看到一段很长的代码,它定义了一个Process类,这个类封装了Win32_Process类。

使用mgmtclassgen.exe生成的类,可以更简洁地访问WMI对象的属性:

using System; using System.Management; using ROOT.CIMV2.Win32; class Program { static void Main() { foreach (Process win32Process in Process.GetInstances()) { Console.WriteLine(win32Process.Name); Console.WriteLine(win32Process.CommandLine + "\n"); } } }

这种方式比之前的示例更简洁,语法更清晰,并且支持Intellisense功能。

调用WMI类的方法并不直观,需要使用间接方法调用。首先需要使用ManagementClass.GetMethodParameters()获取方法输入参数信息,为每个参数赋值,然后将其与方法名一起传递给ManagementClass.InvokeMethod()。InvokeMethod()返回一个ManagementBaseObject,它代表输出参数,也需要间接访问它。

以下是一个调用Win32_Process类Create方法的示例:

ManagementBaseObject inParams = null; ManagementClass classObj = new ManagementClass(null, "Win32_Process", null); inParams = classObj.GetMethodParameters("Create"); inParams["CommandLine"] = "Notepad.exe"; ManagementBaseObject outParams = classObj.InvokeMethod("Create", inParams, null); Console.WriteLine(outParams.Properties["ProcessId"].Value);

与使用mgmtclassgen.exe生成的Process类相比,只需要一行代码就可以调用方法:

using System; using System.Management; using ROOT.CIMV2.Win32; class Program { static void Main() { uint handle; Process.Create("Notepad.Exe", null, null, out handle); Console.WriteLine(handle.ToString()); } }

如果查看生成的类代码中Process.Create()的定义,会发现它在幕后执行了与第一段代码相同的间接方法调用。只是这种情况下,复杂性被隐藏在了一个单独的类中。

大多数情况下,mgmtclassgen.exe工作得很好,但如果要使用的类来自Root\Cimv2 WMI命名空间之外,并且包含静态方法,如StdRegProv WMI类(用于访问注册表),就会出现问题。

mgmtclassgen.exe用于为StdRegProvWMI类生成一个强类型类。但是,如果不报告任何错误,上述简单的代码列出HKLM\Software子键会返回一个异常:"未找到"。如果查看StdRegProv.cs代码以找到其EnumKey()方法,会发现(经过一些重新格式化):

public static uint EnumKey(uint hDefKey, string sSubKeyName, out string[] sNames) { bool IsMethodStatic = true; if ((IsMethodStatic == true)) { System.Management.ManagementBaseObject inParams = null; System.Management.ManagementPath mgmtPath = new System.Management.ManagementPath(CreatedClassName); System.Management.ManagementClass classObj = new System.Management.ManagementClass(statMgmtScope, mgmtPath, null); inParams = classObj.GetMethodParameters("EnumKey"); inParams["hDefKey"] = ((System.UInt32)(hDefKey)); inParams["sSubKeyName"] = ((System.String)(sSubKeyName)); System.Management.ManagementBaseObject outParams = classObj.InvokeMethod("EnumKey", inParams, null); sNames = ((string[])(outParams.Properties["sNames"].Value)); return System.Convert.ToUInt32(outParams.Properties["ReturnValue"].Value); } else { sNames = null; return System.Convert.ToUInt32(0); } }

ManagementClass构造函数接受statMsgScope参数,它应该持有指向Root\Default WMI命名空间的ManagementScope的引用,StdRegProv类位于该命名空间。但是,在StdRegProv.cs的开头并没有这样做:

private static System.Management.ManagementScope statMgmtScope = null;

有了这个声明,statMgmtScope默认为Root\Cimv2 - 难怪方法调用抛出"未找到"异常 - Root\Cimv2命名空间中没有StdRegrov类。因此,最后要使其工作,需要将上述声明更改为:

private static System.Management.ManagementScope statMgmtScope = new ManagementScope("root\\default");
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485