diff --git a/Management/DynamicMgmtObject.cs b/Management/DynamicMgmtObject.cs
new file mode 100644
index 00000000..33642cf7
--- /dev/null
+++ b/Management/DynamicMgmtObject.cs
@@ -0,0 +1,137 @@
+using System;
+using System.Collections.Generic;
+using System.Dynamic;
+using System.Linq;
+using System.Management;
+
+namespace Vanara.Management
+{
+ /// A dynamic object to handle WMI references.
+ ///
+ ///
+ public class DynamicMgmtObject : DynamicObject, IDisposable
+ {
+ private readonly ManagementObject obj;
+
+ /// Initializes a new instance of the class.
+ /// The object.
+ public DynamicMgmtObject(ManagementObject obj) => this.obj = obj ?? throw new ArgumentNullException();
+
+ /// Initializes a new instance of the class.
+ /// The scope.
+ /// The service name.
+ public DynamicMgmtObject(ManagementScope scope, string service)
+ {
+ if (!scope.IsConnected)
+ scope.Connect();
+
+ obj = scope.GetWMIService(service);
+ if (obj is null) throw new ArgumentNullException();
+ }
+
+ /// Initializes a new instance of the class.
+ /// The class.
+ public DynamicMgmtObject(ManagementClass wmiclass) => obj = wmiclass.CreateInstance();
+
+ /// Performs an implicit conversion from to .
+ /// The .
+ /// The result of the conversion.
+ public static implicit operator DynamicMgmtObject(ManagementObject mbo) => new(mbo);
+
+ ///
+ public void Dispose() => ((IDisposable)obj).Dispose();
+
+ ///
+ public override IEnumerable GetDynamicMemberNames() => obj.Properties.Cast().Select(d => d.Name);
+
+ ///
+ public override bool TryConvert(ConvertBinder binder, out object result)
+ {
+ if (binder.Type == typeof(ManagementObject))
+ {
+ result = obj;
+ return true;
+ }
+ return base.TryConvert(binder, out result);
+ }
+
+ ///
+ public override bool TryGetMember(GetMemberBinder binder, out object result)
+ {
+ try { result = obj.GetPropertyValue(binder.Name); return true; }
+ catch (ManagementException)
+ {
+ result = binder.Name switch
+ {
+ "ClassPath" or "ManagementClassPath" => obj.ClassPath,
+ "Path" or "ManagementPath" => obj.Path,
+ "Scope" or "ManagementScope" => obj.Scope,
+ _ => null
+ };
+ return result is not null;
+ }
+ }
+
+ ///
+ public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
+ {
+ if (args.Length == 1)
+ {
+ ManagementBaseObject input = null;
+ if (args[0] is ManagementBaseObject mbo)
+ {
+ input = mbo;
+ }
+ else if (args[0] is IDictionary eo)
+ {
+ input = obj.GetMethodParameters(binder.Name);
+ foreach (KeyValuePair kv in eo)
+ input[kv.Key] = kv.Value;
+ }
+ else if (args[0] is (string Key, object Value)[] a)
+ {
+ input = obj.GetMethodParameters(binder.Name);
+ foreach ((string Key, object Value) in a)
+ input[Key] = Value;
+ }
+ if (input is not null)
+ {
+ result = (DynamicMgmtObject)obj.InvokeMethod(binder.Name, input, null);
+ return true;
+ }
+ }
+
+ try
+ {
+ result = obj.InvokeMethod(binder.Name, args);
+ return true;
+ }
+ catch (ManagementException) { }
+ return base.TryInvokeMember(binder, args, out result);
+ }
+
+ ///
+ public override bool TrySetMember(SetMemberBinder binder, object value)
+ {
+ try { obj.SetPropertyValue(binder.Name, value); return true; }
+ catch (ManagementException)
+ {
+ switch (binder.Name)
+ {
+ case "Path":
+ case "ManagementPath":
+ obj.Path = value as ManagementPath;
+ return true;
+
+ case "Scope":
+ case "ManagementScope":
+ obj.Scope = value as ManagementScope;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Management/ManagementExtensions.cs b/Management/ManagementExtensions.cs
new file mode 100644
index 00000000..cd7cf177
--- /dev/null
+++ b/Management/ManagementExtensions.cs
@@ -0,0 +1,226 @@
+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;
+ throw new InvalidOperationException(errMsg);
+ 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))
+ {
+ 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))
+ {
+ DataMemberAttribute attr = pi.GetCustomAttributes(false).FirstOrDefault() ?? new DataMemberAttribute() { Name = pi.Name };
+
+ nodelist = doc.SelectNodes($@"//PROPERTY[@NAME = '{attr.Name}']/VALUE/child::text()");
+ if (attr.IsRequired && nodelist.Count != 1)
+ throw new FormatException();
+ if (nodelist.Count == 0)
+ continue;
+
+ if (pi.PropertyType.IsEnum)
+ {
+ TypeConverter cv = TypeDescriptor.GetConverter(pi.PropertyType.GetEnumUnderlyingType());
+ var val = cv.ConvertFromInvariantString(nodelist[0].Value);
+ if (!Enum.IsDefined(pi.PropertyType, val))
+ throw new FormatException();
+ pi.SetValue(output, Enum.ToObject(pi.PropertyType, val));
+ }
+ else
+ {
+ TypeConverter cv = TypeDescriptor.GetConverter(pi.PropertyType);
+ var val = cv.ConvertFromInvariantString(nodelist[0].Value);
+ pi.SetValue(output, val);
+ }
+ }
+ return output;
+ }
+
+ /// 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;
+ }
+}
\ No newline at end of file
diff --git a/Management/Vanara.Management.csproj b/Management/Vanara.Management.csproj
new file mode 100644
index 00000000..852aff6b
--- /dev/null
+++ b/Management/Vanara.Management.csproj
@@ -0,0 +1,20 @@
+
+
+
+ Extensions and helper classes for System.Management.
+ $(AssemblyName)
+ Vanara.Management
+ $(AssemblyName)
+ vanara;net-extensions;WMI
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file