Merge pull request #366 from NextTurn/mmi

Migrate from `System.Management` to MMI
pull/357/head
Oleg Nenashev 2020-02-06 04:53:40 -05:00 committed by GitHub
commit eb96c4a2a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 204 additions and 68 deletions

View File

@ -4,4 +4,9 @@
<DefineConstants>VNEXT</DefineConstants> <DefineConstants>VNEXT</DefineConstants>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
<!-- https://docs.microsoft.com/windows/win32/wmisdk/common-information-model -->
<DefineConstants>$(DefineConstants);FEATURE_CIM</DefineConstants>
</PropertyGroup>
</Project> </Project>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -48,18 +48,16 @@ namespace WMI
public interface Win32Services : IWmiCollection 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); // 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); 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 public interface Win32Service : IWmiObject
{ {
string Description { get; set; }
string Name { get; }
bool Started { get; } bool Started { get; }
void Delete(); void Delete();
void StartService(); void StartService();