Browse Source

Migrate to MMI

NextTurn 6 years ago
parent
commit
30c5994c42

+ 4 - 0
Directory.Build.targets

@@ -4,4 +4,8 @@
     <DefineConstants>VNEXT</DefineConstants>
   </PropertyGroup>
 
+  <PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
+    <DefineConstants>$(DefineConstants);CIM</DefineConstants>
+  </PropertyGroup>
+
 </Project>

+ 1 - 1
src/Core/ServiceWrapper/Main.cs

@@ -596,7 +596,7 @@ namespace winsw
                     "\"" + descriptor.ExecutablePath + "\"",
                     ServiceType.OwnProcess,
                     ErrorControl.UserNotified,
-                    descriptor.StartMode,
+                    descriptor.StartMode.ToString(),
                     descriptor.Interactive,
                     username,
                     password,

+ 0 - 1
src/Core/ServiceWrapper/winsw.csproj

@@ -28,7 +28,6 @@
   <ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1'">
     <PackageReference Include="ilmerge" Version="3.0.29" />
     <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" />
-    <Reference Include="System.Management" />
     <Reference Include="System.ServiceProcess" />
   </ItemGroup>
 

+ 21 - 4
src/Core/WinSWCore/Util/ProcessHelper.cs

@@ -1,9 +1,14 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+#if !CIM
 using System.Management;
+#endif
 using System.Threading;
 using log4net;
+#if CIM
+using Microsoft.Management.Infrastructure;
+#endif
 
 namespace winsw.Util
 {
@@ -26,13 +31,25 @@ namespace winsw.Util
 
             try
             {
-                var searcher = new ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + pid);
-                foreach (var mo in searcher.Get())
+                string query = "SELECT * FROM Win32_Process WHERE ParentProcessID = " + pid;
+#if CIM
+                using CimSession session = CimSession.Create(null);
+                foreach (CimInstance instance in session.QueryInstances("root/cimv2", "WQL", query))
                 {
-                    var childProcessId = mo["ProcessID"];
-                    Logger.Info("Found child process: " + childProcessId + " Name: " + mo["Name"]);
+                    object childProcessId = instance.CimInstanceProperties["ProcessID"].Value;
+                    Logger.Info("Found child process: " + childProcessId + " Name: " + instance.CimInstanceProperties["Name"].Value);
                     childPids.Add(Convert.ToInt32(childProcessId));
                 }
+#else
+                using ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
+                using ManagementObjectCollection results = searcher.Get();
+                foreach (ManagementBaseObject wmiObject in results)
+                {
+                    var childProcessId = wmiObject["ProcessID"];
+                    Logger.Info("Found child process: " + childProcessId + " Name: " + wmiObject["Name"]);
+                    childPids.Add(Convert.ToInt32(childProcessId));
+                }
+#endif
             }
             catch (Exception ex)
             {

+ 1 - 1
src/Core/WinSWCore/WinSWCore.csproj

@@ -14,8 +14,8 @@
   </ItemGroup>
 
   <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
+    <PackageReference Include="Microsoft.Management.Infrastructure" Version="2.0.0" />
     <PackageReference Include="System.Diagnostics.EventLog" Version="4.7.0" />
-    <PackageReference Include="System.Management" Version="4.7.0" />
   </ItemGroup>
 
   <!-- error NU1605: Detected package downgrade: log4net 2.0.8 -->

+ 173 - 56
src/Core/WinSWCore/Wmi.cs

@@ -1,13 +1,19 @@
 using System;
+using System.Diagnostics;
+#if !CIM
 using System.Management;
+#endif
 using System.Reflection;
 using DynamicProxy;
+#if CIM
+using Microsoft.Management.Infrastructure;
+using Microsoft.Management.Infrastructure.Generic;
+#endif
 
 namespace WMI
 {
-    // Reference: http://msdn2.microsoft.com/en-us/library/aa389390(VS.85).aspx
-
-    public enum ReturnValue
+    // https://docs.microsoft.com/windows/win32/cimwin32prov/create-method-in-class-win32-service
+    public enum ReturnValue : uint
     {
         Success = 0,
         NotSupported = 1,
@@ -45,8 +51,8 @@ namespace WMI
     {
         public readonly ReturnValue ErrorCode;
 
-        public WmiException(string msg, ReturnValue code)
-            : base(msg)
+        public WmiException(string message, ReturnValue code)
+            : base(message)
         {
             ErrorCode = code;
         }
@@ -83,14 +89,21 @@ namespace WMI
         void Commit();
     }
 
-    public class WmiRoot
+    public sealed class WmiRoot
     {
-        private readonly ManagementScope scope;
+#if CIM
+        private const string CimNamespace = "root/cimv2";
 
-        public WmiRoot() : this(null) { }
+        private readonly CimSession cimSession;
+#else
+        private readonly ManagementScope wmiScope;
+#endif
 
-        public WmiRoot(string? machineName)
+        public WmiRoot(string? machineName = null)
         {
+#if CIM
+            this.cimSession = CimSession.Create(machineName);
+#else
             ConnectionOptions options = new ConnectionOptions
             {
                 EnablePrivileges = true,
@@ -104,8 +117,9 @@ namespace WMI
                 path = $@"\\{machineName}\root\cimv2";
             else
                 path = @"\root\cimv2";
-            scope = new ManagementScope(path, options);
-            scope.Connect();
+            wmiScope = new ManagementScope(path, options);
+            wmiScope.Connect();
+#endif
         }
 
         private static string Capitalize(string s)
@@ -113,96 +127,195 @@ namespace WMI
             return char.ToUpper(s[0]) + s.Substring(1);
         }
 
-        abstract class BaseHandler : IProxyInvocationHandler
+        private abstract class BaseHandler : IProxyInvocationHandler
         {
-            public abstract object? Invoke(object proxy, MethodInfo method, object[] args);
+            public abstract object? Invoke(object proxy, MethodInfo method, object[] arguments);
 
+#if CIM
+            protected void CheckError(CimMethodResult result)
+            {
+                uint code = (uint)result.ReturnValue.Value;
+                if (code != 0)
+                    throw new WmiException((ReturnValue)code);
+            }
+#else
             protected void CheckError(ManagementBaseObject result)
             {
-                int code = Convert.ToInt32(result["returnValue"]);
+                uint code = (uint)result["returnValue"];
                 if (code != 0)
                     throw new WmiException((ReturnValue)code);
             }
+#endif
+
+#if CIM
+            protected CimMethodParametersCollection GetMethodParameters(CimClass cimClass, string methodName, ParameterInfo[] methodParameters, object[] arguments)
+            {
+                CimMethodParametersCollection cimParameters = new CimMethodParametersCollection();
+                CimReadOnlyKeyedCollection<CimMethodParameterDeclaration> cimParameterDeclarations = cimClass.CimClassMethods[methodName].Parameters;
+                for (int i = 0; i < arguments.Length; i++)
+                {
+                    string capitalizedName = Capitalize(methodParameters[i].Name!);
+                    cimParameters.Add(CimMethodParameter.Create(capitalizedName, arguments[i], cimParameterDeclarations[capitalizedName].CimType, CimFlags.None));
+                }
+
+                return cimParameters;
+            }
+#else
+            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;
+            }
+#endif
         }
 
-        class InstanceHandler : BaseHandler, IWmiObject
+        private class InstanceHandler : BaseHandler, IWmiObject
         {
-            private readonly ManagementObject _mo;
+#if CIM
+            private readonly CimSession cimSession;
+            private readonly CimInstance cimInstance;
+
+            public InstanceHandler(CimSession cimSession, CimInstance cimInstance)
+            {
+                this.cimSession = cimSession;
+                this.cimInstance = cimInstance;
+            }
+#else
+            private readonly ManagementObject wmiObject;
 
-            public InstanceHandler(ManagementObject o) => _mo = o;
+            public InstanceHandler(ManagementObject wmiObject) => this.wmiObject = wmiObject;
+#endif
 
-            public override object? Invoke(object proxy, MethodInfo method, object[] args)
+            public override object? Invoke(object proxy, MethodInfo method, object[] arguments)
             {
                 if (method.DeclaringType == typeof(IWmiObject))
                 {
-                    return method.Invoke(this, args);
+                    return method.Invoke(this, arguments);
                 }
 
                 // TODO: proper property support
                 if (method.Name.StartsWith("set_"))
                 {
-                    _mo[method.Name.Substring(4)] = args[0];
+#if CIM
+                    CimProperty cimProperty = this.cimInstance.CimInstanceProperties[method.Name.Substring(4)];
+                    Debug.Assert((cimProperty.Flags & CimFlags.ReadOnly) == CimFlags.None);
+                    cimProperty.Value = arguments[0];
+#else
+                    this.wmiObject[method.Name.Substring(4)] = arguments[0];
+#endif
                     return null;
                 }
 
                 if (method.Name.StartsWith("get_"))
                 {
-                    return _mo[method.Name.Substring(4)];
+#if CIM
+                    return this.cimInstance.CimInstanceProperties[method.Name.Substring(4)].Value;
+#else
+                    return this.wmiObject[method.Name.Substring(4)];
+#endif
                 }
 
-                // method invocations
-                ParameterInfo[] methodArgs = method.GetParameters();
-
-                ManagementBaseObject wmiArgs = _mo.GetMethodParameters(method.Name);
-                for (int i = 0; i < args.Length; i++)
-                    wmiArgs[Capitalize(methodArgs[i].Name!)] = args[i];
-
-                CheckError(_mo.InvokeMethod(method.Name, wmiArgs, null));
+                string methodName = method.Name;
+#if CIM
+                using CimMethodParametersCollection? cimParameters = arguments.Length == 0 ? null :
+                    this.GetMethodParameters(this.cimInstance.CimClass, methodName, method.GetParameters(), arguments);
+                using CimMethodResult result = this.cimSession.InvokeMethod(CimNamespace, this.cimInstance, methodName, cimParameters);
+                this.CheckError(result);
+#else
+                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);
+#endif
                 return null;
             }
 
             public void Commit()
             {
-                _mo.Put();
+#if !CIM
+                this.wmiObject.Put();
+#endif
             }
         }
 
-        class ClassHandler : BaseHandler
+        private class ClassHandler : BaseHandler
         {
-            private readonly ManagementClass _mc;
-            private readonly string _wmiClass;
-
-            public ClassHandler(ManagementClass mc, string wmiClass) { _mc = mc; _wmiClass = wmiClass; }
+#if CIM
+            private readonly CimSession cimSession;
+            private readonly CimClass cimClass;
+#else
+            private readonly ManagementClass wmiClass;
+#endif
+            private readonly string className;
+
+#if CIM
+            public ClassHandler(CimSession cimSession, string className)
+            {
+                this.cimSession = cimSession;
+                this.cimClass = cimSession.GetClass(CimNamespace, className);
+                this.className = className;
+            }
+#else
+            public ClassHandler(ManagementScope wmiScope, string className)
+            {
+                this.wmiClass = new ManagementClass(wmiScope, new ManagementPath(className), null);
+                this.className = className;
+            }
+#endif
 
-            public override object? Invoke(object proxy, MethodInfo method, object[] args)
+            public override object? Invoke(object proxy, MethodInfo method, object[] arguments)
             {
-                ParameterInfo[] methodArgs = method.GetParameters();
+                ParameterInfo[] methodParameters = method.GetParameters();
 
-                if (method.Name.StartsWith("Select"))
+                if (method.Name == nameof(Win32Services.Select))
                 {
                     // select method to find instances
-                    string query = "SELECT * FROM " + _wmiClass + " WHERE ";
-                    for (int i = 0; i < args.Length; i++)
+                    string query = "SELECT * FROM " + this.className + " WHERE ";
+                    for (int i = 0; i < arguments.Length; i++)
                     {
                         if (i != 0)
                             query += " AND ";
 
-                        query += ' ' + Capitalize(methodArgs[i].Name!) + " = '" + args[i] + "'";
+                        query += ' ' + Capitalize(methodParameters[i].Name!) + " = '" + arguments[i] + "'";
                     }
 
-                    ManagementObjectSearcher searcher = new ManagementObjectSearcher(_mc.Scope, new ObjectQuery(query));
-                    ManagementObjectCollection results = searcher.Get();
+#if CIM
                     // TODO: support collections
-                    foreach (ManagementObject manObject in results)
-                        return ProxyFactory.GetInstance().Create(new InstanceHandler(manObject), method.ReturnType, true);
+                    foreach (CimInstance cimInstance in this.cimSession.QueryInstances(CimNamespace, "WQL", query))
+                    {
+                        return ProxyFactory.GetInstance().Create(new InstanceHandler(this.cimSession, cimInstance), method.ReturnType, true);
+                    }
+#else
+                    using ManagementObjectSearcher searcher = new ManagementObjectSearcher(this.wmiClass.Scope, new ObjectQuery(query));
+                    using ManagementObjectCollection results = searcher.Get();
+                    // TODO: support collections
+                    foreach (ManagementObject wmiObject in results)
+                    {
+                        return ProxyFactory.GetInstance().Create(new InstanceHandler(wmiObject), method.ReturnType, true);
+                    }
+#endif
+
                     return null;
                 }
 
-                ManagementBaseObject wmiArgs = _mc.GetMethodParameters(method.Name);
-                for (int i = 0; i < args.Length; i++)
-                    wmiArgs[Capitalize(methodArgs[i].Name!)] = args[i];
-
-                CheckError(_mc.InvokeMethod(method.Name, wmiArgs, null));
+                string methodName = method.Name;
+#if CIM
+                using CimMethodParametersCollection? cimParameters = arguments.Length == 0 ? null :
+                    this.GetMethodParameters(this.cimClass, methodName, methodParameters, arguments);
+                using CimMethodResult result = this.cimSession.InvokeMethod(CimNamespace, this.className, methodName, cimParameters);
+                this.CheckError(result);
+#else
+                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);
+#endif
                 return null;
             }
         }
@@ -212,12 +325,16 @@ namespace WMI
         /// </summary>
         public T GetCollection<T>() where T : IWmiCollection
         {
-            WmiClassName cn = (WmiClassName)typeof(T).GetCustomAttributes(typeof(WmiClassName), false)[0];
-
-            ObjectGetOptions getOptions = new ObjectGetOptions();
-            ManagementPath path = new ManagementPath(cn.Name);
-            ManagementClass manClass = new ManagementClass(scope, path, getOptions);
-            return (T)ProxyFactory.GetInstance().Create(new ClassHandler(manClass, cn.Name), typeof(T), true);
+            WmiClassName className = (WmiClassName)typeof(T).GetCustomAttributes(typeof(WmiClassName), false)[0];
+
+            return (T)ProxyFactory.GetInstance().Create(
+#if CIM
+                new ClassHandler(this.cimSession, className.Name),
+#else
+                new ClassHandler(this.wmiScope, className.Name),
+#endif
+                typeof(T),
+                true);
         }
     }
 }

+ 3 - 5
src/Core/WinSWCore/WmiSchema.cs

@@ -48,18 +48,16 @@ namespace WMI
     public interface Win32Services : IWmiCollection
     {
         // ReturnValue Create(bool desktopInteract, string displayName, int errorControl, string loadOrderGroup, string loadOrderGroupDependencies, string name, string pathName, string serviceDependencies, string serviceType, string startMode, string startName, string startPassword);
-        void Create(string name, string displayName, string pathName, ServiceType serviceType, ErrorControl errorControl, StartMode startMode, bool desktopInteract, string? startName, string? startPassword, string[] serviceDependencies);
+        void Create(string name, string displayName, string pathName, ServiceType serviceType, ErrorControl errorControl, string startMode, bool desktopInteract, string? startName, string? startPassword, string[] serviceDependencies);
 
-        void Create(string name, string displayName, string pathName, ServiceType serviceType, ErrorControl errorControl, StartMode startMode, bool desktopInteract, string[] serviceDependencies);
+        void Create(string name, string displayName, string pathName, ServiceType serviceType, ErrorControl errorControl, string startMode, bool desktopInteract, string[] serviceDependencies);
 
         Win32Service Select(string name);
     }
 
-    // http://msdn.microsoft.com/en-us/library/windows/desktop/aa394418(v=vs.85).aspx
+    // https://docs.microsoft.com/windows/win32/cimwin32prov/win32-service
     public interface Win32Service : IWmiObject
     {
-        string Description { get; set; }
-        string Name { get; }
         bool Started { get; }
         void Delete();
         void StartService();