using System; using System.Management; using System.Reflection; using System.Text; using DynamicProxy; namespace WMI { // https://docs.microsoft.com/windows/win32/cimwin32prov/create-method-in-class-win32-service public enum ReturnValue : uint { Success = 0, NotSupported = 1, AccessDenied = 2, DependentServicesRunning = 3, InvalidServiceControl = 4, ServiceCannotAcceptControl = 5, ServiceNotActive = 6, ServiceRequestTimeout = 7, UnknownFailure = 8, PathNotFound = 9, ServiceAlreadyRunning = 10, ServiceDatabaseLocked = 11, ServiceDependencyDeleted = 12, ServiceDependencyFailure = 13, ServiceDisabled = 14, ServiceLogonFailure = 15, ServiceMarkedForDeletion = 16, ServiceNoThread = 17, StatusCircularDependency = 18, StatusDuplicateName = 19, StatusInvalidName = 20, StatusInvalidParameter = 21, StatusInvalidServiceAccount = 22, StatusServiceExists = 23, ServiceAlreadyPaused = 24, NoSuchService = 200 } /// /// Signals a problem in WMI related operations /// public class WmiException : Exception { public readonly ReturnValue ErrorCode; public WmiException(string message, ReturnValue code) : base(message) { ErrorCode = code; } public WmiException(ReturnValue code) : this(code.ToString(), code) { } } /// /// Associated a WMI class name to the proxy interface (which should extend from IWmiCollection) /// public class WmiClassName : Attribute { public readonly string Name; public WmiClassName(string name) => Name = name; } /// /// Marker interface to denote a collection in WMI. /// public interface IWmiCollection { } /// /// Marker interface to denote an individual managed object /// public interface IWmiObject { } public sealed class WmiRoot { private readonly ManagementScope wmiScope; public WmiRoot() { ConnectionOptions options = new ConnectionOptions { EnablePrivileges = true, Impersonation = ImpersonationLevel.Impersonate, Authentication = AuthenticationLevel.PacketPrivacy, }; this.wmiScope = new ManagementScope(@"\\.\root\cimv2", options); this.wmiScope.Connect(); } private static string Capitalize(string s) { return char.ToUpper(s[0]) + s.Substring(1); } private abstract class BaseHandler : IProxyInvocationHandler { public abstract object? Invoke(object proxy, MethodInfo method, object[] arguments); protected void CheckError(ManagementBaseObject result) { uint code = (uint)result["returnValue"]; if (code != 0) throw new WmiException((ReturnValue)code); } protected ManagementBaseObject GetMethodParameters(ManagementObject wmiObject, string methodName, ParameterInfo[] methodParameters, object[] arguments) { ManagementBaseObject wmiParameters = wmiObject.GetMethodParameters(methodName); for (int i = 0; i < arguments.Length; i++) { string capitalizedName = Capitalize(methodParameters[i].Name!); wmiParameters[capitalizedName] = arguments[i]; } return wmiParameters; } } private class InstanceHandler : BaseHandler, IWmiObject { private readonly ManagementObject wmiObject; public InstanceHandler(ManagementObject wmiObject) => this.wmiObject = wmiObject; public override object? Invoke(object proxy, MethodInfo method, object[] arguments) { if (method.DeclaringType == typeof(IWmiObject)) { return method.Invoke(this, arguments); } // TODO: proper property support if (method.Name.StartsWith("set_")) { this.wmiObject[method.Name.Substring(4)] = arguments[0]; return null; } if (method.Name.StartsWith("get_")) { return this.wmiObject[method.Name.Substring(4)]; } string methodName = method.Name; using ManagementBaseObject? wmiParameters = arguments.Length == 0 ? null : this.GetMethodParameters(this.wmiObject, methodName, method.GetParameters(), arguments); using ManagementBaseObject result = this.wmiObject.InvokeMethod(methodName, wmiParameters, null); this.CheckError(result); return null; } } private class ClassHandler : BaseHandler { private readonly ManagementClass wmiClass; private readonly string className; public ClassHandler(ManagementScope wmiScope, string className) { this.wmiClass = new ManagementClass(wmiScope, new ManagementPath(className), null); this.className = className; } public override object? Invoke(object proxy, MethodInfo method, object[] arguments) { ParameterInfo[] methodParameters = method.GetParameters(); if (method.Name == nameof(Win32Services.Select)) { // select method to find instances StringBuilder query = new StringBuilder("SELECT * FROM ").Append(this.className).Append(" WHERE "); for (int i = 0; i < arguments.Length; i++) { if (i != 0) query.Append(" AND "); query.Append(' ').Append(Capitalize(methodParameters[i].Name!)).Append(" = '").Append(arguments[i]).Append('\''); } using ManagementObjectSearcher searcher = new ManagementObjectSearcher(this.wmiClass.Scope, new ObjectQuery(query.ToString())); using ManagementObjectCollection results = searcher.Get(); // TODO: support collections foreach (ManagementObject wmiObject in results) { return ProxyFactory.Create(new InstanceHandler(wmiObject), method.ReturnType, true); } return null; } string methodName = method.Name; using ManagementBaseObject? wmiParameters = arguments.Length == 0 ? null : this.GetMethodParameters(this.wmiClass, methodName, methodParameters, arguments); using ManagementBaseObject result = this.wmiClass.InvokeMethod(methodName, wmiParameters, null); this.CheckError(result); return null; } } /// /// Obtains an object that corresponds to a table in WMI, which is a collection of a managed object. /// public T GetCollection() where T : IWmiCollection { WmiClassName className = (WmiClassName)typeof(T).GetCustomAttributes(typeof(WmiClassName), false)[0]; return (T)ProxyFactory.Create(new ClassHandler(this.wmiScope, className.Name), typeof(T), true); } } }