Reputation: 27683
tl;dr What should I SELECT
instead of *
in order to get the methods?
More info:
Here's an example:
using (var s = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM someClass"))
foreach (var obj in s.Get())
If I just ask for one property it's not enough - I get an exception when trying obj.InvokeMethod(...);
.
If I ask for *
it is enough but I rather avoid this if possible.
I don't see any property for getting the methods (-Disable
, Enable
,...) in the docs for WMI classes. And if it's not on the list - how come *
works? Isn't it just supposed to ask for all of those on the list?
EDIT
Someone suggested using ManagementClass instead of ManagementObjectSearcher
. Does this load all properties like *
? (If not, that's a good answer. Though in my actual case I need a property besides the ability to call a method. And my theoretical questions remains - is *
more than just all.)
Upvotes: 3
Views: 1747
Reputation: 16606
As far as what the title and TL;DR are asking, methods cannot be queried via SELECT
but you can use the ManagementClass.Methods
property to inspect those provided by that management class. For example, this code...
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}");
}
}
...produces this output...
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
...
Unless "get methods" has a different meaning than I think it does (distinct from calling methods), the rest of the question seems to be dealing with something else entirely, which is the necessity of populating Key
properties before invoking a method. I believe this is addressed in another question of yours, Why does WMI work through a search but not directly?
If what you're really asking is "How can I determine the minimum set of properties I need to select in order to invoke a method?", then you can accomplish that with ManagementClass
, too. That minimum set of properties are all properties with a Key
qualifier, so you would use the Properties
property to find any property whose Qualifiers
property contains a qualifier with a Name
of "Key"
.
Consider the Win32_Product
class, which represents products installed by Windows Installer and (as determined on my Windows 10 system, differing from the documentation) has these Key
properties...
IdentifyingNumber
Name
Version
Let's say you want to retrieve these properties to display...
PackageName
Vendor
Version
...and then call the Uninstall()
method for a product with a Name
of "Microsoft .NET Framework 4.8 SDK"
. The following code shows three different ways to attempt this task...
Key
properties and invoke the method.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();
}
}
}
}
}
...and produces this output...
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.
Here are the takeaways from those three attempts...
Key
properties - results in a failed method invocation.
Key
properties, Version
, was part of our query, the searchResult.Path
property is empty in this case, a sure sign that something isn't quite right with our result object.Key
properties results in a successful method invocation with no extraneous properties.
displayProperties
) and a collection of Key
properties (i.e. keyProperties
), the Union()
LINQ method makes it easy to combine them to get the minimum properties to select.Upvotes: 3