using System;
using System.ComponentModel;
using System.Linq;
using System.Management;
using System.Reflection;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
namespace Vanara.Management
{
/// Extension methods to work more easily with .
public static class ManagementExtensions
{
internal enum JobState : ushort
{
New = 2,
Starting = 3,
Running = 4,
Suspended = 5,
ShuttingDown = 6,
Completed = 7,
Terminated = 8,
Killed = 9,
Exception = 10,
Service = 11,
QueryPending = 12,
CompletedWithWarnings = 32768
}
/// Calls a service method that returns a Job asynchronously.
/// The scope.
/// The cancellation token.
/// The progress.
/// The service.
/// The method.
/// The values.
/// The resulting .
public static async Task CallJobMethodAsync(this ManagementScope scope, CancellationToken cancellationToken, IProgress progress, string service, string method, params (string, object)[] values) =>
await Task.Factory.StartNew(() =>
{
if (!scope.IsConnected)
scope.Connect();
using ManagementObject imgMgmtSvc = scope.GetWMIService(service);
using ManagementBaseObject inParams = imgMgmtSvc.GetMethodParameters(method);
foreach ((string, object) kv in values)
inParams[kv.Item1] = kv.Item2;
ManagementBaseObject outputParameters = imgMgmtSvc.InvokeMethod(method, inParams, null);
const int sleepDur = 500;
if (outputParameters.IsAsync())
{
// The method invoked an asynchronous operation. Get the Job object and wait for it to complete. Then we can check its result.
using ManagementObject job = new((string)outputParameters["Job"]) { Scope = scope };
while (!job.GetProp("JobState").IsJobComplete())
{
if (progress is not null)
{
try { progress.Report(job.GetProp("PercentComplete")); }
catch { }
}
Task.Delay(sleepDur);
// ManagementObjects are offline objects. Call Get() on the object to have its current property state.
job.Get();
}
switch (job.GetProp("JobState"))
{
case JobState.Terminated:
case JobState.Killed:
throw new ThreadInterruptedException();
case JobState.Exception:
ManagementBaseObject errOut = job.InvokeMethod("GetError", null, null);
var xml = new XmlDocument();
xml.LoadXml(errOut.GetProp("Error"));
var errMsg = xml.DocumentElement.SelectSingleNode(@"//PROPERTY[@NAME='Message']/VALUE")?.InnerText;
var ex = new InvalidOperationException(errMsg);
#if DEBUG
foreach (var p in job.Properties.Cast())
ex.Data.Add(p.Name, p.Value);
#endif
throw ex;
case JobState.Completed:
case JobState.CompletedWithWarnings:
outputParameters.SetPropertyValue("ReturnValue", 0);
break;
default:
break;
}
}
progress?.Report(100);
return outputParameters;
}, cancellationToken);
/// Gets the embedded instance string usable by WMI
/// The type of the instance.
/// The instance.
/// Name of the server.
/// Embedded instance string usable by WMI.
/// Generic type does not have a DataContract attribute.
public static string GetInstanceText(T instance, string serverName = ".")
{
DataContractAttribute attr = typeof(T).GetCustomAttributes(false).FirstOrDefault();
if (attr is null)
throw new InvalidOperationException("Generic type does not have a DataContract attribute.");
var path = new ManagementPath() { Server = serverName, NamespacePath = attr.Namespace, ClassName = attr.Name };
using var settingsClass = new ManagementClass(path);
using ManagementObject settingsInstance = settingsClass.CreateInstance();
foreach (PropertyInfo pi in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
if (pi.GetCustomAttributes().Any() || !pi.CanRead)
continue;
DataMemberAttribute mattr = pi.GetCustomAttributes(false).FirstOrDefault() ?? new DataMemberAttribute() { Name = pi.Name };
var val = pi.PropertyType.IsEnum ? Convert.ChangeType(pi.GetValue(instance), pi.PropertyType.GetEnumUnderlyingType()) : pi.GetValue(instance);
settingsInstance.SetPropertyValue(mattr.Name, val);
}
return settingsInstance.GetText(TextFormat.WmiDtd20);
}
/// Gets the specified property value of from .
/// The property type
/// The object.
/// The property name.
/// The property value.
public static T GetProp(this ManagementBaseObject obj, string prop) => typeof(T).IsEnum ? (T)Enum.ToObject(typeof(T), obj[prop]) : (T)obj[prop];
/// Gets the result from a return value or throws the appropriate exception.
/// The method output object.
/// if set to throws all exceptions including those for 4096 and 32768.
/// on success; otherwise .
public static bool GetResultOrThrow(this ManagementBaseObject output, bool throwAll = false) => output.GetProp("ReturnValue") switch
{
0 => true,
4096 => throwAll ? throw new SynchronizationLockException() : false,
32768 => throwAll ? throw new Exception() : false,
32769 => throw new UnauthorizedAccessException(),
32770 => throw new NotSupportedException(),
32773 => throw new ArgumentException(),
32779 => throw new System.IO.FileNotFoundException(),
32778 => throw new OutOfMemoryException(),
32772 => throw new TimeoutException(),
//Status is unknown(32771)
//System is in use(32774)
//Invalid state for this operation(32775)
//Incorrect data type(32776)
//System is not available(32777)
_ => throw new Exception(),
};
/// Gets the specifid WMI service from a scope.
/// The scope.
/// The service path.
/// The service object.
public static ManagementObject GetWMIService(this ManagementScope scope, string path)
{
using ManagementClass imageManagementServiceClass = new(path) { Scope = scope };
return imageManagementServiceClass.GetInstances().Cast().FirstOrDefault();
}
///
/// Parses an embedded instance returned from the server and creates a new instance of with that information.
///
/// The type to fill with information from .
/// The embedded instance.
/// An instance of with the data contained in the embedded instance.
/// If there was a problem parsing the embedded instance.
/// If either param is null.
public static T Parse(string embeddedInstance) where T : class, new()
{
var doc = new XmlDocument();
doc.LoadXml(embeddedInstance);
XmlNodeList nodelist = doc.SelectNodes(@"/INSTANCE/@CLASSNAME");
var className = typeof(T).GetCustomAttributes(false).FirstOrDefault()?.Name ?? typeof(T).Name;
if (nodelist.Count != 1 || nodelist[0].Value != className)
{
throw new FormatException();
}
var output = new T();
foreach (PropertyInfo pi in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
if (pi.GetCustomAttributes().Any() || !pi.CanWrite)
continue;
DataMemberAttribute attr = pi.GetCustomAttributes(false).FirstOrDefault() ?? new DataMemberAttribute() { Name = pi.Name };
if (pi.PropertyType.IsArray)
{
if (doc.SelectSingleNode($@"//PROPERTY.ARRAY[@NAME = '{attr.Name}']") is not null)
{
var array = doc.SelectNodes($@"//PROPERTY.ARRAY[@NAME = '{attr.Name}']/VALUE.ARRAY/VALUE").Cast().Select(n => n.InnerText).ToArray();
var ret = Array.CreateInstance(pi.PropertyType.GetElementType(), array.Length);
for (int i = 0; i < array.Length; i++)
ret.SetValue(GetVal(pi.PropertyType.GetElementType(), array[i]), i);
pi.SetValue(output, ret);
}
else if (attr.IsRequired)
throw new FormatException();
}
else
{
nodelist = doc.SelectNodes($@"//PROPERTY[@NAME = '{attr.Name}']/VALUE/child::text()");
if (attr.IsRequired && nodelist.Count != 1)
throw new FormatException();
if (nodelist.Count == 0)
continue;
pi.SetValue(output, GetVal(pi.PropertyType, nodelist[0].Value));
}
}
return output;
static object GetVal(Type type, string value)
{
if (type == typeof(string))
return value;
else if (type.IsEnum)
{
TypeConverter cv = TypeDescriptor.GetConverter(type.GetEnumUnderlyingType());
var val = cv.ConvertFromInvariantString(value);
if (!Enum.IsDefined(type, val))
throw new FormatException();
return Enum.ToObject(type, val);
}
else
{
TypeConverter cv = TypeDescriptor.GetConverter(type);
return cv.ConvertFromInvariantString(value);
}
}
}
/// Converts a string in CIM_DATETIME format to a .
/// The CIM_DATETIME string in 'yyyymmddHHMMSS.mmmmmmsUUU' format.
/// A value in GMT equivalent to or if unable to process.
public static DateTime? CimToDateTime(string cimdate)
{
if (cimdate is null || !(cimdate.Length is 14 or 21 or 25))
return null;
var dts = cimdate;
if (dts.Length == 14)
dts += ".000000";
if (dts.Length == 21)
dts += "+000";
if (!short.TryParse(dts.Substring(21, 4), out var utcOffset))
return null;
dts = $"{dts.Substring(0, 21)}{(utcOffset < 0 ? '-' : '+')}{Math.Abs(utcOffset) / 60:D2}:{Math.Abs(utcOffset) % 60:D2}";
if (!DateTime.TryParseExact(dts, "yyyyMMddHHmmss.FFFFFFFzzz", null, System.Globalization.DateTimeStyles.None, out var dt))
return null;
return dt;
}
/// Converts a value to CIM_DATETIME format.
/// The value.
/// A CIM_DATETIME string in 'yyyymmddHHMMSS.mmmmmmsUUU' format.
public static string DateTimeToCim(this DateTime dt) => $"{dt.ToUniversalTime():yyyyMMddHHmmss.FFFFFFF}+000";
/// Verifies whether a job is completed.
/// An object that represents the JobState of the job.
/// True if the job is completed, False otherwise.
internal static bool IsJobComplete(this JobState jobStateObj) =>
jobStateObj is JobState.Completed or JobState.CompletedWithWarnings or JobState.Terminated or JobState.Exception or JobState.Killed;
/// Verifies whether a job succeeded.
/// An object representing the JobState of the job.
/// true if the job succeeded; otherwise, false.
internal static bool IsJobSuccessful(this JobState jobStateObj) => jobStateObj is JobState.Completed or JobState.CompletedWithWarnings;
private static bool IsAsync(this ManagementBaseObject output) => output.GetProp("ReturnValue") == 4096;
}
}