如何从WMI获取方法

2018-04-12 c# .net methods wmi system.management

tl; dr为了得到这些方法,我应该SELECT而不是*吗?

更多信息:

这是一个例子:

using (var s = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM someClass"))
    foreach (var obj in s.Get())

如果我只要求一个属性,那还不够-尝试obj.InvokeMethod(...);时出现异常obj.InvokeMethod(...);

如果我要求*就足够了,但我宁愿避免这种情况。

我在WMI类的文档中看不到任何用于获取方法的属性( DisableEnable ,...)。如果不在列表中,那么*怎么起作用?难道不就是要索要清单上的所有这些吗?

编辑

有人建议使用ManagementClass代替ManagementObjectSearcher 。这会加载所有*属性吗? (如果没有,这是一个很好的答案。虽然在我的实际情况下,我需要的属性,除了调用方法的能力,而我的理论问题遗骸-是*不仅仅是所有 。)

Answers

就标题和TL; DR的要求而言,无法通过SELECT查询方法,但可以使用ManagementClass.Methods属性检查该管理类提供的方法。例如,这段代码...

using (ManagementClass processClass = new ManagementClass("Win32_Process"))
    foreach (MethodData method in processClass.Methods)
    {
        bool isStatic = method.Qualifiers
            .Cast<QualifierData>()
            .Any(qualifier => string.Equals(qualifier.Name, "Static", StringComparison.OrdinalIgnoreCase));

        Console.WriteLine($"{method.Origin}.{method.Name}() [{(isStatic ? "static" : "instance")}]");
        if (method.InParameters != null && method.InParameters.Properties.Count > 0)
        {
            Console.WriteLine("\tInput parameters:");
            foreach (PropertyData parameterProperty in method.InParameters.Properties)
                Console.WriteLine($"\t\t{parameterProperty.Type} {parameterProperty.Name}");
        }
        if (method.OutParameters != null && method.OutParameters.Properties.Count > 0)
        {
            Console.WriteLine("\tOutput parameters:");
            foreach (PropertyData parameterProperty in method.OutParameters.Properties)
                Console.WriteLine($"\t\t{parameterProperty.Type} {parameterProperty.Name}");
        }
    }

...产生这个输出...

Win32_Process.Create() [static]
    Input parameters:
        String CommandLine
        String CurrentDirectory
        Object ProcessStartupInformation
    Output parameters:
        UInt32 ProcessId
        UInt32 ReturnValue
Win32_Process.Terminate() [instance]
    Input parameters:
        UInt32 Reason
    Output parameters:
        UInt32 ReturnValue
Win32_Process.GetOwner() [instance]
    Output parameters:
        String Domain
        UInt32 ReturnValue
        String User
...

除非“获取方法”的含义与我认为的含义不同(与调用方法不同),否则其余的问题似乎完全是在处理其他问题,这是在调用方法之前填充Key属性的必要性。我相信这是您的另一个问题, WMI为什么通过搜索而不是直接进行搜索?

如果您真正要问的是“如何确定为调用方法而需要选择的最小属性集?”,那么您也可以使用ManagementClass来实现。最少的一组属性是具有Key限定符的所有属性,因此您将使用Properties属性来查找其Qualifiers属性包含Name"Key"Qualifiers任何属性。

考虑Win32_Product该类代表Windows Installer安装的产品,并且(根据我的Windows 10系统确定,与文档不同)具有以下这些Key属性 ...

  • IdentifyingNumber
  • Name
  • Version

假设您要检索这些属性以显示 ...

  • PackageName
  • Vendor
  • Version

...,然后为Name"Microsoft .NET Framework 4.8 SDK"的产品调用Uninstall()方法 。以下代码显示了尝试此任务的三种不同方式...

  1. 选择所有属性并调用该方法。
  2. 选择要显示的属性并调用该方法。
  3. 选择要显示的属性以及Key属性,然后调用该方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;

namespace SO49798851
{
    static class Program
    {
        static void Main()
        {
            // Win32_Product is slow to query, so be patient!
            const string className = "Win32_Product";
            const string condition = "Name = 'Microsoft .NET Framework 4.8 SDK'";
            string[] allProperties = new string[] { "*" };
            string[] displayProperties = new string[] { "PackageName", "Vendor", "Version" };
            string[] keyProperties = GetKeyPropertyNames(className);
            string[] displayPlusKeyProperties = displayProperties.Union(keyProperties).ToArray();
            // When run as a non-administrator, the Uninstall() method
            // still returns 0 despite not (appearing to) do anything
            const string methodName = "Uninstall";
            object[] methodArguments = Array.Empty<object>();

            Console.WriteLine($"Key properties for class {className}: {string.Join(", ", keyProperties)}");
            Console.WriteLine();

            FindAndInvoke(className, condition, allProperties,            methodName, methodArguments);
            FindAndInvoke(className, condition, displayProperties,        methodName, methodArguments);
            FindAndInvoke(className, condition, displayPlusKeyProperties, methodName, methodArguments);
        }

        static string[] GetKeyPropertyNames(string className)
        {
            using (ManagementClass managementClass = new ManagementClass(className))
            {
                return managementClass.Properties
                    .Cast<PropertyData>()
                    .Where(
                        property => property.Qualifiers
                            .Cast<QualifierData>()
                            .Any(qualifier => string.Equals(qualifier.Name, "Key", StringComparison.OrdinalIgnoreCase))
                    )
                    .Select(property => property.Name)
                    .ToArray();
            }
        }

        static void FindAndInvoke(
            string className,
            string condition,
            string[] selectedProperties,
            string methodName,
            object[] methodArguments
        )
        {
            if (selectedProperties == null)
                selectedProperties = Array.Empty<string>();

            ObjectQuery query = new SelectQuery(className, condition, selectedProperties);
            bool allPropertiesSelected = selectedProperties.Length < 1
                || selectedProperties.Any(propertyName => propertyName == "*");

            Console.WriteLine(query.QueryString);
            Console.WriteLine(new string('=', query.QueryString.Length));

            using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(query))
            using (ManagementObjectCollection searchResultCollection = searcher.Get())
            {
                // ManagementObjectCollection doesn't support indexing; this is the
                // least-ugly, least-verbose way to enumerate it with an index variable
                ManagementObject[] searchResultArray = searchResultCollection.Cast<ManagementObject>().ToArray();

                for (int i = 0; i < searchResultArray.Length; i++)
                    using (ManagementObject searchResult = searchResultArray[i])
                    {
                        Console.WriteLine($"{className}[{i}].Path.RelativePath: {searchResult.Path.RelativePath}");
                        Console.WriteLine($"{className}[{i}].Properties.Count: {searchResult.Properties.Count}");
                        foreach (PropertyData property in searchResult.Properties)
                            if (allPropertiesSelected
                                    || selectedProperties.Contains(property.Name, StringComparer.OrdinalIgnoreCase)
                            )
                            {
                                object displayValue = property.Value ?? "<null>";

                                Console.WriteLine($"{className}[{i}].Properties[\"{property.Name}\"]: {displayValue}");
                            }

                        try
                        {
                            object methodResult = searchResult.InvokeMethod(methodName, methodArguments);

                            Console.WriteLine($"{className}[{i}].{methodName}() completed with result {methodResult}.");
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"{className}[{i}].{methodName}() failed with {ex}.");
                        }
                        Console.WriteLine();
                    }
            }
        }
    }
}

...并产生此输出...

Key properties for class Win32_Product: IdentifyingNumber, Name, Version

select * from Win32_Product where Name = 'Microsoft .NET Framework 4.8 SDK'
===========================================================================
Win32_Product[0].Path.RelativePath: Win32_Product.IdentifyingNumber="{949C0535-171C-480F-9CF4-D25C9E60FE88}",Name="Microsoft .NET Framework 4.8 SDK",Version="4.8.03928"
Win32_Product[0].Properties.Count: 27
Win32_Product[0].Properties["AssignmentType"]: 1
Win32_Product[0].Properties["Caption"]: Microsoft .NET Framework 4.8 SDK
Win32_Product[0].Properties["Description"]: Microsoft .NET Framework 4.8 SDK
Win32_Product[0].Properties["HelpLink"]: <null>
Win32_Product[0].Properties["HelpTelephone"]: <null>
Win32_Product[0].Properties["IdentifyingNumber"]: {949C0535-171C-480F-9CF4-D25C9E60FE88}
Win32_Product[0].Properties["InstallDate"]: 20191001
Win32_Product[0].Properties["InstallDate2"]: <null>
Win32_Product[0].Properties["InstallLocation"]: <null>
Win32_Product[0].Properties["InstallSource"]: C:\ProgramData\Microsoft\VisualStudio\Packages\Microsoft.Net.4.8.SDK,version=4.8.3928.1\
Win32_Product[0].Properties["InstallState"]: 5
Win32_Product[0].Properties["Language"]: 1033
Win32_Product[0].Properties["LocalPackage"]: C:\WINDOWS\Installer\34d24bd7.msi
Win32_Product[0].Properties["Name"]: Microsoft .NET Framework 4.8 SDK
Win32_Product[0].Properties["PackageCache"]: C:\WINDOWS\Installer\34d24bd7.msi
Win32_Product[0].Properties["PackageCode"]: {CC6C9CC4-DDCD-4C14-81E1-4007EE49D7C0}
Win32_Product[0].Properties["PackageName"]: sdk_tools48.msi
Win32_Product[0].Properties["ProductID"]: <null>
Win32_Product[0].Properties["RegCompany"]: <null>
Win32_Product[0].Properties["RegOwner"]: <null>
Win32_Product[0].Properties["SKUNumber"]: <null>
Win32_Product[0].Properties["Transforms"]: <null>
Win32_Product[0].Properties["URLInfoAbout"]: <null>
Win32_Product[0].Properties["URLUpdateInfo"]: <null>
Win32_Product[0].Properties["Vendor"]: Microsoft Corporation
Win32_Product[0].Properties["Version"]: 4.8.03928
Win32_Product[0].Properties["WordCount"]: 0
Win32_Product[0].Uninstall() completed with result 0.

select PackageName,Vendor,Version from Win32_Product where Name = 'Microsoft .NET Framework 4.8 SDK'
====================================================================================================
Win32_Product[0].Path.RelativePath: 
Win32_Product[0].Properties.Count: 3
Win32_Product[0].Properties["PackageName"]: sdk_tools48.msi
Win32_Product[0].Properties["Vendor"]: Microsoft Corporation
Win32_Product[0].Properties["Version"]: 4.8.03928
Win32_Product[0].Uninstall() failed with System.InvalidOperationException: Operation is not valid due to the current state of the object.
   at System.Management.ManagementObject.InvokeMethod(String methodName, Object[] args)
   at SO49798851.Program.FindAndInvoke(String className, String condition, String[] selectedProperties, String methodName, Object[] methodArguments) in ...\Program.cs:line 90.

select PackageName,Vendor,Version,IdentifyingNumber,Name from Win32_Product where Name = 'Microsoft .NET Framework 4.8 SDK'
===========================================================================================================================
Win32_Product[0].Path.RelativePath: Win32_Product.IdentifyingNumber="{949C0535-171C-480F-9CF4-D25C9E60FE88}",Name="Microsoft .NET Framework 4.8 SDK",Version="4.8.03928"
Win32_Product[0].Properties.Count: 5
Win32_Product[0].Properties["IdentifyingNumber"]: {949C0535-171C-480F-9CF4-D25C9E60FE88}
Win32_Product[0].Properties["Name"]: Microsoft .NET Framework 4.8 SDK
Win32_Product[0].Properties["PackageName"]: sdk_tools48.msi
Win32_Product[0].Properties["Vendor"]: Microsoft Corporation
Win32_Product[0].Properties["Version"]: 4.8.03928
Win32_Product[0].Uninstall() completed with result 0.

这是这三个尝试的总结...

  1. 当然,选择所有属性都会导致成功的方法调用,但是最终,我们得到的属性比我们想要的更多。
  2. 完全选择我们想要的属性-但不是全部选择Key属性-会导致方法调用失败。
    • 请注意,即使其中一个Key属性Version也是我们查询的一部分,但在这种情况下searchResult.Path属性为空,这肯定表明我们的结果对象有些不正确。
  3. 精确选择我们想要的属性以及 Key属性将导致成功的方法调用,而没有多余的属性。
    • 如果我们有想要的属性集合(即displayProperties )和Key属性的集合(即keyProperties ),则Union() LINQ方法可以轻松地将它们组合起来以获取最小的属性来选择。

Related