diff --git a/src/Core/ServiceWrapper/Logging/WrapperServiceEventLogProvider.cs b/src/Core/ServiceWrapper/Logging/WrapperServiceEventLogProvider.cs index f051d32..be7cad5 100644 --- a/src/Core/ServiceWrapper/Logging/WrapperServiceEventLogProvider.cs +++ b/src/Core/ServiceWrapper/Logging/WrapperServiceEventLogProvider.cs @@ -7,11 +7,11 @@ namespace winsw.Logging /// public class WrapperServiceEventLogProvider : IServiceEventLogProvider { - public WrapperService service { get; set; } + public WrapperService? service { get; set; } - public EventLog locate() + public EventLog? locate() { - WrapperService _service = service; + WrapperService? _service = service; if (_service != null && !_service.IsShuttingDown) { return _service.EventLog; diff --git a/src/Core/ServiceWrapper/Main.cs b/src/Core/ServiceWrapper/Main.cs index ec0fa74..4602453 100644 --- a/src/Core/ServiceWrapper/Main.cs +++ b/src/Core/ServiceWrapper/Main.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; using System.Runtime.InteropServices; @@ -28,7 +29,7 @@ namespace winsw private readonly Process _process = new Process(); private readonly ServiceDescriptor _descriptor; - private Dictionary _envs; + private Dictionary? _envs; internal WinSWExtensionManager ExtensionManager { get; private set; } @@ -52,7 +53,7 @@ namespace winsw /// /// The version will be taken from /// - public static Version Version => Assembly.GetExecutingAssembly().GetName().Version; + public static Version Version => Assembly.GetExecutingAssembly().GetName().Version!; /// /// Indicates that the system is shutting down. @@ -92,7 +93,7 @@ namespace winsw { using (var tr = new StreamReader(file, Encoding.UTF8)) { - string line; + string? line; while ((line = tr.ReadLine()) != null) { LogEvent("Handling copy: " + line); @@ -223,7 +224,7 @@ namespace winsw } } - string startarguments = _descriptor.Startarguments; + string? startarguments = _descriptor.Startarguments; if (startarguments == null) { @@ -282,7 +283,7 @@ namespace winsw /// private void StopIt() { - string stoparguments = _descriptor.Stoparguments; + string? stoparguments = _descriptor.Stoparguments; LogEvent("Stopping " + _descriptor.Id); Log.Info("Stopping " + _descriptor.Id); _orderlyShutdown = true; @@ -307,7 +308,7 @@ namespace winsw stoparguments += " " + _descriptor.Arguments; Process stopProcess = new Process(); - string executable = _descriptor.StopExecutable; + string? executable = _descriptor.StopExecutable; if (executable == null) { @@ -399,10 +400,10 @@ namespace winsw Advapi32.SetServiceStatus(handle, ref _wrapperServiceStatus); } - private void StartProcess(Process processToStart, string arguments, string executable, LogHandler logHandler, bool redirectStdin) + private void StartProcess(Process processToStart, string arguments, string executable, LogHandler? logHandler, bool redirectStdin) { // Define handler of the completed process - ProcessCompletionCallback processCompletionCallback = proc => + void OnProcessCompleted(Process proc) { string msg = processToStart.Id + " - " + processToStart.StartInfo.FileName + " " + processToStart.StartInfo.Arguments; try @@ -427,16 +428,11 @@ namespace winsw { LogEvent("WaitForExit " + ioe.Message); } - - try + finally { proc.Dispose(); } - catch (InvalidOperationException ioe) - { - LogEvent("Dispose " + ioe.Message); - } - }; + } // Invoke process and exit ProcessHelper.StartProcessAndCallbackForExit( @@ -446,7 +442,7 @@ namespace winsw envVars: _envs, workingDirectory: _descriptor.WorkingDirectory, priority: _descriptor.Priority, - callback: processCompletionCallback, + callback: OnProcessCompleted, logHandler: logHandler, redirectStdin: redirectStdin, hideWindow: _descriptor.HideWindow); @@ -475,6 +471,7 @@ namespace winsw } } + [DoesNotReturn] private static void ThrowNoSuchService() { throw new WmiException(ReturnValue.NoSuchService); @@ -488,7 +485,7 @@ namespace winsw /// Service descriptor. If null, it will be initialized within the method. /// In such case configs will be loaded from the XML Configuration File. /// Any unhandled exception - public static void Run(string[] _args, ServiceDescriptor descriptor = null) + public static void Run(string[] _args, ServiceDescriptor? descriptor = null) { bool isCLIMode = _args.Length > 0; @@ -543,8 +540,9 @@ namespace winsw throw new Exception("Installation failure: Service with id '" + d.Id + "' already exists"); } - string username = null, password = null; - bool setallowlogonasaserviceright = false; + string? username = null; + string? password = null; + bool setallowlogonasaserviceright = false; // This variable is very readable. if (args.Count > 1 && args[1] == "/p") { // we expected username/password on stdin @@ -573,7 +571,7 @@ namespace winsw if (setallowlogonasaserviceright) { - LogonAsAService.AddLogonAsAServiceRight(username); + LogonAsAService.AddLogonAsAServiceRight(username!); } svc.Create( @@ -712,8 +710,7 @@ namespace winsw // run restart from another process group. see README.md for why this is useful. STARTUPINFO si = default; - - bool result = Kernel32.CreateProcess(null, d.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, 0x200/*CREATE_NEW_PROCESS_GROUP*/, IntPtr.Zero, null, ref si, out PROCESS_INFORMATION pi); + bool result = Kernel32.CreateProcess(null, d.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, 0x200/*CREATE_NEW_PROCESS_GROUP*/, IntPtr.Zero, null, ref si, out _); if (!result) { throw new Exception("Failed to invoke restart: " + Marshal.GetLastWin32Error()); @@ -731,6 +728,7 @@ namespace winsw Console.WriteLine("Started"); else Console.WriteLine("Stopped"); + return; } diff --git a/src/Core/ServiceWrapper/NullableAttributes.cs b/src/Core/ServiceWrapper/NullableAttributes.cs new file mode 100644 index 0000000..45b5ff2 --- /dev/null +++ b/src/Core/ServiceWrapper/NullableAttributes.cs @@ -0,0 +1,8 @@ +#if !NETCOREAPP +namespace System.Diagnostics.CodeAnalysis +{ + /// Applied to a method that will never return under any circumstance. + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + internal sealed class DoesNotReturnAttribute : Attribute { } +} +#endif diff --git a/src/Core/ServiceWrapper/SigIntHelper.cs b/src/Core/ServiceWrapper/SigIntHelper.cs index 7dfabe6..4134740 100644 --- a/src/Core/ServiceWrapper/SigIntHelper.cs +++ b/src/Core/ServiceWrapper/SigIntHelper.cs @@ -15,7 +15,7 @@ namespace winsw private static extern bool FreeConsole(); [DllImport(KERNEL32)] - private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add); + private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate? HandlerRoutine, bool Add); // Delegate type to be used as the Handler Routine for SCCH private delegate bool ConsoleCtrlDelegate(CtrlTypes CtrlType); diff --git a/src/Core/ServiceWrapper/winsw.csproj b/src/Core/ServiceWrapper/winsw.csproj index 4a54097..cd4d7c5 100644 --- a/src/Core/ServiceWrapper/winsw.csproj +++ b/src/Core/ServiceWrapper/winsw.csproj @@ -3,6 +3,8 @@ Exe net20;net40;net461;netcoreapp3.1 + latest + enable Windows Service Wrapper Allows arbitrary process to run as a Windows service by wrapping it. diff --git a/src/Core/WinSWCore/Configuration/DefaultSettings.cs b/src/Core/WinSWCore/Configuration/DefaultSettings.cs index fedbd6b..d28c46c 100644 --- a/src/Core/WinSWCore/Configuration/DefaultSettings.cs +++ b/src/Core/WinSWCore/Configuration/DefaultSettings.cs @@ -13,36 +13,29 @@ namespace winsw.Configuration /// public sealed class DefaultWinSWSettings : IWinSWConfiguration { - public string Id => null; - public string Caption => null; - public string Description => null; - public string Executable => null; + public string Id => throw new InvalidOperationException(nameof(Id) + " must be specified."); + public string Caption => throw new InvalidOperationException(nameof(Caption) + " must be specified."); + public string Description => throw new InvalidOperationException(nameof(Description) + " must be specified."); + public string Executable => throw new InvalidOperationException(nameof(Executable) + " must be specified."); public bool HideWindow => false; - public string ExecutablePath - { - get - { - // this returns the executable name as given by the calling process, so - // it needs to be absolutized. - string p = Environment.GetCommandLineArgs()[0]; - return Path.GetFullPath(p); - } - } + // this returns the executable name as given by the calling process, so + // it needs to be absolutized. + public string ExecutablePath => Path.GetFullPath(Environment.GetCommandLineArgs()[0]); // Installation public bool AllowServiceAcountLogonRight => false; - public string ServiceAccountPassword => null; + public string? ServiceAccountPassword => null; public string ServiceAccountUser => "NULL\\NULL"; - public List FailureActions => new List(); + public List FailureActions => new List(0); public TimeSpan ResetFailureAfter => TimeSpan.FromDays(1); // Executable management public string Arguments => string.Empty; - public string Startarguments => null; - public string StopExecutable => null; - public string Stoparguments => null; - public string WorkingDirectory => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + public string? Startarguments => null; + public string? StopExecutable => null; + public string? Stoparguments => null; + public string WorkingDirectory => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; public ProcessPriorityClass Priority => ProcessPriorityClass.Normal; public TimeSpan StopTimeout => TimeSpan.FromSeconds(15); public bool StopParentProcessFirst => false; @@ -56,7 +49,7 @@ namespace winsw.Configuration public bool Interactive => false; // Logging - public string LogDirectory => Path.GetDirectoryName(ExecutablePath); + public string LogDirectory => Path.GetDirectoryName(ExecutablePath)!; public string LogMode => "append"; public bool OutFileDisabled => false; @@ -65,13 +58,13 @@ namespace winsw.Configuration public string ErrFilePattern => ".err.log"; // Environment - public List Downloads => new List(); - public Dictionary EnvironmentVariables => new Dictionary(); + public List Downloads => new List(0); + public Dictionary EnvironmentVariables => new Dictionary(0); // Misc public bool BeepOnShutdown => false; // Extensions - public XmlNode ExtensionsConfiguration => null; + public XmlNode? ExtensionsConfiguration => null; } } diff --git a/src/Core/WinSWCore/Configuration/IWinSWConfiguration.cs b/src/Core/WinSWCore/Configuration/IWinSWConfiguration.cs index be222d5..3d9585b 100644 --- a/src/Core/WinSWCore/Configuration/IWinSWConfiguration.cs +++ b/src/Core/WinSWCore/Configuration/IWinSWConfiguration.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Xml; using WMI; + namespace winsw.Configuration { public interface IWinSWConfiguration @@ -18,16 +19,16 @@ namespace winsw.Configuration // Installation bool AllowServiceAcountLogonRight { get; } - string ServiceAccountPassword { get; } + string? ServiceAccountPassword { get; } string ServiceAccountUser { get; } List FailureActions { get; } TimeSpan ResetFailureAfter { get; } // Executable management string Arguments { get; } - string Startarguments { get; } - string StopExecutable { get; } - string Stoparguments { get; } + string? Startarguments { get; } + string? StopExecutable { get; } + string? Stoparguments { get; } string WorkingDirectory { get; } ProcessPriorityClass Priority { get; } TimeSpan StopTimeout { get; } @@ -53,6 +54,6 @@ namespace winsw.Configuration bool BeepOnShutdown { get; } // Extensions - XmlNode ExtensionsConfiguration { get; } + XmlNode? ExtensionsConfiguration { get; } } } diff --git a/src/Core/WinSWCore/Download.cs b/src/Core/WinSWCore/Download.cs index b6fe87b..9829995 100755 --- a/src/Core/WinSWCore/Download.cs +++ b/src/Core/WinSWCore/Download.cs @@ -23,20 +23,21 @@ namespace winsw public readonly string From; public readonly string To; public readonly AuthType Auth = AuthType.none; - public readonly string Username; - public readonly string Password; + public readonly string? Username; + public readonly string? Password; public readonly bool UnsecureAuth; public readonly bool FailOnError; public string ShortId => $"(download from {From})"; + // internal public Download( string from, string to, bool failOnError = false, AuthType auth = AuthType.none, - string username = null, - string password = null, + string? username = null, + string? password = null, bool unsecureAuth = false) { From = from; @@ -119,7 +120,7 @@ namespace winsw break; case AuthType.basic: - SetBasicAuthHeader(req, Username, Password); + SetBasicAuthHeader(req, Username!, Password!); break; default: diff --git a/src/Core/WinSWCore/DynamicProxy.cs b/src/Core/WinSWCore/DynamicProxy.cs index c706722..740e44e 100644 --- a/src/Core/WinSWCore/DynamicProxy.cs +++ b/src/Core/WinSWCore/DynamicProxy.cs @@ -1,5 +1,5 @@ using System; -using System.Collections; +using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; @@ -15,7 +15,7 @@ namespace DynamicProxy /// The method info that can be used to invoke the actual method on the object implementation /// Parameters to pass to the method /// Object - object Invoke(object proxy, MethodInfo method, object[] parameters); + object? Invoke(object proxy, MethodInfo method, object[] parameters); } /// @@ -23,7 +23,7 @@ namespace DynamicProxy /// public class MetaDataFactory { - private static Hashtable typeMap = new Hashtable(); + private static readonly Dictionary typeMap = new Dictionary(); /// /// Class constructor. Private because this is a static class. @@ -41,12 +41,16 @@ namespace DynamicProxy { if (interfaceType != null) { - lock (typeMap.SyncRoot) + lock (typeMap) { - if (!typeMap.ContainsKey(interfaceType.FullName)) +#if NETCOREAPP + _ = typeMap.TryAdd(interfaceType.FullName!, interfaceType); +#else + if (!typeMap.ContainsKey(interfaceType.FullName!)) { - typeMap.Add(interfaceType.FullName, interfaceType); + typeMap.Add(interfaceType.FullName!, interfaceType); } +#endif } } } @@ -59,10 +63,10 @@ namespace DynamicProxy ///MethodInfo public static MethodInfo GetMethod(string name, int i) { - Type type = null; - lock (typeMap.SyncRoot) + Type? type = null; + lock (typeMap) { - type = (Type)typeMap[name]; + type = typeMap[name]; } return type.GetMethods()[i]; @@ -70,10 +74,10 @@ namespace DynamicProxy public static PropertyInfo GetProperty(string name, int i) { - Type type = null; - lock (typeMap.SyncRoot) + Type? type = null; + lock (typeMap) { - type = (Type)typeMap[name]; + type = typeMap[name]; } return type.GetProperties()[i]; @@ -84,30 +88,27 @@ namespace DynamicProxy /// public class ProxyFactory { - private static ProxyFactory _instance; + private static ProxyFactory? _instance; private static readonly object LockObj = new object(); - private readonly Hashtable _typeMap = Hashtable.Synchronized(new Hashtable()); - private static readonly Hashtable OpCodeTypeMapper = new Hashtable(); + private readonly Dictionary _typeMap = new Dictionary(); - private const string PROXY_SUFFIX = "Proxy"; - private const string ASSEMBLY_NAME = "ProxyAssembly"; - private const string MODULE_NAME = "ProxyModule"; - private const string HANDLER_NAME = "handler"; - - // Initialize the value type mapper. This is needed for methods with intrinsic - // return types, used in the Emit process. - static ProxyFactory() + private static readonly Dictionary OpCodeTypeMapper = new Dictionary { - OpCodeTypeMapper.Add(typeof(bool), OpCodes.Ldind_I1); - OpCodeTypeMapper.Add(typeof(short), OpCodes.Ldind_I2); - OpCodeTypeMapper.Add(typeof(int), OpCodes.Ldind_I4); - OpCodeTypeMapper.Add(typeof(long), OpCodes.Ldind_I8); - OpCodeTypeMapper.Add(typeof(double), OpCodes.Ldind_R8); - OpCodeTypeMapper.Add(typeof(float), OpCodes.Ldind_R4); - OpCodeTypeMapper.Add(typeof(ushort), OpCodes.Ldind_U2); - OpCodeTypeMapper.Add(typeof(uint), OpCodes.Ldind_U4); - } + { typeof(bool), OpCodes.Ldind_I1 }, + { typeof(short), OpCodes.Ldind_I2 }, + { typeof(int), OpCodes.Ldind_I4 }, + { typeof(long), OpCodes.Ldind_I8 }, + { typeof(double), OpCodes.Ldind_R8 }, + { typeof(float), OpCodes.Ldind_R4 }, + { typeof(ushort), OpCodes.Ldind_U2 }, + { typeof(uint), OpCodes.Ldind_U4 }, + }; + + private const string ProxySuffix = "Proxy"; + private const string AssemblyName = "ProxyAssembly"; + private const string ModuleName = "ProxyModule"; + private const string HandlerName = "handler"; private ProxyFactory() { @@ -120,24 +121,25 @@ namespace DynamicProxy CreateInstance(); } - return _instance; + return _instance!; } private static void CreateInstance() { lock (LockObj) { - if (_instance == null) - { - _instance = new ProxyFactory(); - } + _instance ??= new ProxyFactory(); } } public object Create(IProxyInvocationHandler handler, Type objType, bool isObjInterface) { - string typeName = objType.FullName + PROXY_SUFFIX; - Type type = (Type)_typeMap[typeName]; + string typeName = objType.FullName + ProxySuffix; + Type? type = null; + lock (_typeMap) + { + _ = _typeMap.TryGetValue(typeName, out type); + } // check to see if the type was in the cache. If the type was not cached, then // create a new instance of the dynamic type and add it to the cache. @@ -152,11 +154,14 @@ namespace DynamicProxy type = CreateType(handler, objType.GetInterfaces(), typeName); } - _typeMap.Add(typeName, type); + lock (_typeMap) + { + _typeMap.Add(typeName, type); + } } // return a new instance of the type. - return Activator.CreateInstance(type, new object[] { handler }); + return Activator.CreateInstance(type, new object[] { handler })!; } public object Create(IProxyInvocationHandler handler, Type objType) @@ -166,88 +171,78 @@ namespace DynamicProxy private Type CreateType(IProxyInvocationHandler handler, Type[] interfaces, string dynamicTypeName) { - Type retVal = null; + Type objType = typeof(object); + Type handlerType = typeof(IProxyInvocationHandler); - if (handler != null && interfaces != null) - { - Type objType = typeof(object); - Type handlerType = typeof(IProxyInvocationHandler); + AssemblyName assemblyName = new AssemblyName(); + assemblyName.Name = AssemblyName; + assemblyName.Version = new Version(1, 0, 0, 0); - AssemblyName assemblyName = new AssemblyName(); - assemblyName.Name = ASSEMBLY_NAME; - assemblyName.Version = new Version(1, 0, 0, 0); - - // create a new assembly for this proxy, one that isn't presisted on the file system - AssemblyBuilder assemblyBuilder = + // create a new assembly for this proxy, one that isn't presisted on the file system + AssemblyBuilder assemblyBuilder = #if VNEXT - AssemblyBuilder.DefineDynamicAssembly( + AssemblyBuilder.DefineDynamicAssembly( #else - AppDomain.CurrentDomain.DefineDynamicAssembly( + AppDomain.CurrentDomain.DefineDynamicAssembly( #endif - assemblyName, AssemblyBuilderAccess.Run); + assemblyName, AssemblyBuilderAccess.Run); - // create a new module for this proxy - ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(MODULE_NAME); + // create a new module for this proxy + ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(ModuleName); - // Set the class to be public and sealed - TypeAttributes typeAttributes = - TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed; + // Set the class to be public and sealed + TypeAttributes typeAttributes = + TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed; - // Gather up the proxy information and create a new type builder. One that - // inherits from Object and implements the interface passed in - TypeBuilder typeBuilder = moduleBuilder.DefineType( - dynamicTypeName, typeAttributes, objType, interfaces); + // Gather up the proxy information and create a new type builder. One that + // inherits from Object and implements the interface passed in + TypeBuilder typeBuilder = moduleBuilder.DefineType( + dynamicTypeName, typeAttributes, objType, interfaces); - // Define a member variable to hold the delegate - FieldBuilder handlerField = typeBuilder.DefineField( - HANDLER_NAME, handlerType, FieldAttributes.Private); + // Define a member variable to hold the delegate + FieldBuilder handlerField = typeBuilder.DefineField( + HandlerName, handlerType, FieldAttributes.Private); - // build a constructor that takes the delegate object as the only argument - // ConstructorInfo defaultObjConstructor = objType.GetConstructor( new Type[0] ); - ConstructorInfo superConstructor = objType.GetConstructor(new Type[0]); - ConstructorBuilder delegateConstructor = typeBuilder.DefineConstructor( - MethodAttributes.Public, CallingConventions.Standard, new Type[] { handlerType }); + // build a constructor that takes the delegate object as the only argument + ConstructorInfo baseConstructor = objType.GetConstructor(Type.EmptyTypes)!; + ConstructorBuilder delegateConstructor = typeBuilder.DefineConstructor( + MethodAttributes.Public, CallingConventions.Standard, new Type[] { handlerType }); -#region( "Constructor IL Code" ) - ILGenerator constructorIL = delegateConstructor.GetILGenerator(); + #region( "Constructor IL Code" ) + ILGenerator constructorIL = delegateConstructor.GetILGenerator(); - // Load "this" - constructorIL.Emit(OpCodes.Ldarg_0); - // Load first constructor parameter - constructorIL.Emit(OpCodes.Ldarg_1); - // Set the first parameter into the handler field - constructorIL.Emit(OpCodes.Stfld, handlerField); - // Load "this" - constructorIL.Emit(OpCodes.Ldarg_0); - // Call the super constructor - constructorIL.Emit(OpCodes.Call, superConstructor); - // Constructor return - constructorIL.Emit(OpCodes.Ret); -#endregion + // Load "this" + constructorIL.Emit(OpCodes.Ldarg_0); + // Load first constructor parameter + constructorIL.Emit(OpCodes.Ldarg_1); + // Set the first parameter into the handler field + constructorIL.Emit(OpCodes.Stfld, handlerField); + // Load "this" + constructorIL.Emit(OpCodes.Ldarg_0); + // Call the super constructor + constructorIL.Emit(OpCodes.Call, baseConstructor); + // Constructor return + constructorIL.Emit(OpCodes.Ret); + #endregion - // for every method that the interfaces define, build a corresponding - // method in the dynamic type that calls the handlers invoke method. - foreach (Type interfaceType in interfaces) - { - GenerateMethod(interfaceType, handlerField, typeBuilder); - } - - retVal = typeBuilder.CreateType(); - - // assemblyBuilder.Save(dynamicTypeName + ".dll"); + // for every method that the interfaces define, build a corresponding + // method in the dynamic type that calls the handlers invoke method. + foreach (Type interfaceType in interfaces) + { + GenerateMethod(interfaceType, handlerField, typeBuilder); } - return retVal; + return typeBuilder.CreateType()!; } - private static readonly MethodInfo INVOKE_METHOD = typeof(IProxyInvocationHandler).GetMethod("Invoke"); - private static readonly MethodInfo GET_METHODINFO_METHOD = typeof(MetaDataFactory).GetMethod("GetMethod", new Type[] { typeof(string), typeof(int) }); + private static readonly MethodInfo INVOKE_METHOD = typeof(IProxyInvocationHandler).GetMethod(nameof(IProxyInvocationHandler.Invoke))!; + private static readonly MethodInfo GET_METHODINFO_METHOD = typeof(MetaDataFactory).GetMethod(nameof(MetaDataFactory.GetMethod))!; private void GenerateMethod(Type interfaceType, FieldBuilder handlerField, TypeBuilder typeBuilder) { MetaDataFactory.Add(interfaceType); MethodInfo[] interfaceMethods = interfaceType.GetMethods(); - PropertyInfo[] props = interfaceType.GetProperties(); + // PropertyInfo[] props = interfaceType.GetProperties(); for (int i = 0; i < interfaceMethods.Length; i++) { @@ -272,7 +267,7 @@ namespace DynamicProxy CallingConventions.Standard, methodInfo.ReturnType, methodParameters); -#region( "Handler Method IL Code" ) + #region( "Handler Method IL Code" ) ILGenerator methodIL = methodBuilder.GetILGenerator(); // load "this" @@ -283,7 +278,7 @@ namespace DynamicProxy methodIL.Emit(OpCodes.Ldarg_0); // load the name of the interface, used to get the MethodInfo object // from MetaDataFactory - methodIL.Emit(OpCodes.Ldstr, interfaceType.FullName); + methodIL.Emit(OpCodes.Ldstr, interfaceType.FullName!); // load the index, used to get the MethodInfo object // from MetaDataFactory methodIL.Emit(OpCodes.Ldc_I4, i); @@ -315,7 +310,7 @@ namespace DynamicProxy if (methodInfo.ReturnType != typeof(void)) { - // if the return type if a value type, then unbox the return value + // if the return type is a value type, then unbox the return value // so that we don't get junk. if (methodInfo.ReturnType.IsValueType) { @@ -330,7 +325,7 @@ namespace DynamicProxy } else { - methodIL.Emit((OpCode)OpCodeTypeMapper[methodInfo.ReturnType]); + methodIL.Emit(OpCodeTypeMapper[methodInfo.ReturnType]); } } } @@ -343,7 +338,7 @@ namespace DynamicProxy // Return methodIL.Emit(OpCodes.Ret); -#endregion + #endregion } // for (int i = 0; i < props.Length; i++) diff --git a/src/Core/WinSWCore/Extensions/AbstractWinSWExtension.cs b/src/Core/WinSWCore/Extensions/AbstractWinSWExtension.cs index d665a2c..5173e84 100644 --- a/src/Core/WinSWCore/Extensions/AbstractWinSWExtension.cs +++ b/src/Core/WinSWCore/Extensions/AbstractWinSWExtension.cs @@ -1,5 +1,4 @@ -using System; -using System.Xml; +using System.Xml; namespace winsw.Extensions { @@ -7,7 +6,9 @@ namespace winsw.Extensions { public abstract string DisplayName { get; } +#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. public WinSWExtensionDescriptor Descriptor { get; set; } +#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. public virtual void Configure(ServiceDescriptor descriptor, XmlNode node) { diff --git a/src/Core/WinSWCore/Extensions/IWinSWExtension.cs b/src/Core/WinSWCore/Extensions/IWinSWExtension.cs index 4562c4f..e10819a 100644 --- a/src/Core/WinSWCore/Extensions/IWinSWExtension.cs +++ b/src/Core/WinSWCore/Extensions/IWinSWExtension.cs @@ -1,5 +1,4 @@ -using System; -using System.Xml; +using System.Xml; namespace winsw.Extensions { diff --git a/src/Core/WinSWCore/Extensions/WinSWExtensionDescriptor.cs b/src/Core/WinSWCore/Extensions/WinSWExtensionDescriptor.cs index 004560c..60fe91a 100644 --- a/src/Core/WinSWCore/Extensions/WinSWExtensionDescriptor.cs +++ b/src/Core/WinSWCore/Extensions/WinSWExtensionDescriptor.cs @@ -1,5 +1,4 @@ -using System; -using System.Xml; +using System.Xml; using winsw.Util; namespace winsw.Extensions diff --git a/src/Core/WinSWCore/Extensions/WinSWExtensionManager.cs b/src/Core/WinSWCore/Extensions/WinSWExtensionManager.cs index 9c6ffb7..f15b5e2 100644 --- a/src/Core/WinSWCore/Extensions/WinSWExtensionManager.cs +++ b/src/Core/WinSWCore/Extensions/WinSWExtensionManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Reflection; using System.Xml; using log4net; @@ -128,8 +127,8 @@ namespace winsw.Extensions throw new ExtensionException(id, "Extension has been already loaded"); } - var extensionsConfig = ServiceDescriptor.ExtensionsConfiguration; - XmlElement configNode = (extensionsConfig != null) ? extensionsConfig.SelectSingleNode("extension[@id='" + id + "'][1]") as XmlElement : null; + XmlNode? extensionsConfig = ServiceDescriptor.ExtensionsConfiguration; + XmlElement? configNode = (extensionsConfig != null) ? extensionsConfig.SelectSingleNode("extension[@id='" + id + "'][1]") as XmlElement : null; if (configNode == null) { throw new ExtensionException(id, "Cannot get the configuration entry"); @@ -165,13 +164,13 @@ namespace winsw.Extensions try { - Type t = Type.GetType(className); + Type? t = Type.GetType(className); if (t == null) { throw new ExtensionException(id, "Class " + className + " does not exist"); } - created = Activator.CreateInstance(t); + created = Activator.CreateInstance(t)!; } catch (Exception ex) { diff --git a/src/Core/WinSWCore/LogAppenders.cs b/src/Core/WinSWCore/LogAppenders.cs index eaca2eb..238f303 100644 --- a/src/Core/WinSWCore/LogAppenders.cs +++ b/src/Core/WinSWCore/LogAppenders.cs @@ -30,7 +30,9 @@ namespace winsw /// /// Error and information about logging should be reported here. /// +#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. public EventLogger EventLogger { get; set; } +#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. /// /// Convenience method to copy stuff from StreamReader to StreamWriter @@ -403,7 +405,7 @@ namespace winsw var buf = new byte[1024]; - var baseDirectory = Path.GetDirectoryName(BaseLogFileName); + var baseDirectory = Path.GetDirectoryName(BaseLogFileName)!; var baseFileName = Path.GetFileName(BaseLogFileName); var logFile = BaseLogFileName + extension; @@ -411,10 +413,10 @@ namespace winsw var sz = new FileInfo(logFile).Length; // We auto roll at time is configured then we need to create a timer and wait until time is elasped and roll the file over - if (AutoRollAtTime != null) + if (AutoRollAtTime is TimeSpan autoRollAtTime) { // Run at start - var tickTime = SetupRollTimer(); + var tickTime = SetupRollTimer(autoRollAtTime); var timer = new System.Timers.Timer(tickTime); timer.Elapsed += (s, e) => { @@ -444,7 +446,7 @@ namespace winsw finally { // Recalculate the next interval - timer.Interval = SetupRollTimer(); + timer.Interval = SetupRollTimer(autoRollAtTime); timer.Start(); } }; @@ -543,7 +545,7 @@ namespace winsw #if VNEXT private void ZipOneFile(string sourceFilePath, string entryName, string zipFilePath) { - ZipArchive zipArchive = null; + ZipArchive? zipArchive = null; try { zipArchive = ZipFile.Open(zipFilePath, ZipArchiveMode.Update); @@ -565,7 +567,7 @@ namespace winsw #else private void ZipOneFile(string sourceFilePath, string entryName, string zipFilePath) { - ZipFile zipFile = null; + ZipFile? zipFile = null; try { zipFile = new ZipFile(File.Open(zipFilePath, FileMode.OpenOrCreate)); @@ -590,11 +592,17 @@ namespace winsw } #endif - private double SetupRollTimer() + private double SetupRollTimer(TimeSpan autoRollAtTime) { var nowTime = DateTime.Now; - var scheduledTime = new DateTime(nowTime.Year, nowTime.Month, nowTime.Day, AutoRollAtTime.Value.Hours, - AutoRollAtTime.Value.Minutes, AutoRollAtTime.Value.Seconds, 0); // Specify your time HH,MM,SS + var scheduledTime = new DateTime( + nowTime.Year, + nowTime.Month, + nowTime.Day, + autoRollAtTime.Hours, + autoRollAtTime.Minutes, + autoRollAtTime.Seconds, + 0); if (nowTime > scheduledTime) scheduledTime = scheduledTime.AddDays(1); diff --git a/src/Core/WinSWCore/Logging/IServiceEventLogProvider.cs b/src/Core/WinSWCore/Logging/IServiceEventLogProvider.cs index c590b47..f35d6e9 100644 --- a/src/Core/WinSWCore/Logging/IServiceEventLogProvider.cs +++ b/src/Core/WinSWCore/Logging/IServiceEventLogProvider.cs @@ -11,6 +11,6 @@ namespace winsw.Logging /// Locates Event Log for the service. /// /// Event Log or null if it is not avilable - EventLog locate(); + EventLog? locate(); } } diff --git a/src/Core/WinSWCore/Logging/ServiceEventLogAppender.cs b/src/Core/WinSWCore/Logging/ServiceEventLogAppender.cs index 0c4f939..0a62312 100644 --- a/src/Core/WinSWCore/Logging/ServiceEventLogAppender.cs +++ b/src/Core/WinSWCore/Logging/ServiceEventLogAppender.cs @@ -10,16 +10,16 @@ namespace winsw.Logging /// public class ServiceEventLogAppender : AppenderSkeleton { +#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. public IServiceEventLogProvider provider { get; set; } +#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. override protected void Append(LoggingEvent loggingEvent) { - EventLog eventLog = provider.locate(); - if (eventLog != null) - { - // We write the event iff the provider is ready - eventLog.WriteEntry(loggingEvent.RenderedMessage, toEventLogEntryType(loggingEvent.Level)); - } + EventLog? eventLog = provider.locate(); + + // We write the event iff the provider is ready + eventLog?.WriteEntry(loggingEvent.RenderedMessage, toEventLogEntryType(loggingEvent.Level)); } private static EventLogEntryType toEventLogEntryType(Level level) diff --git a/src/Core/WinSWCore/Native/Advapi32.cs b/src/Core/WinSWCore/Native/Advapi32.cs index 99f23ba..14247eb 100755 --- a/src/Core/WinSWCore/Native/Advapi32.cs +++ b/src/Core/WinSWCore/Native/Advapi32.cs @@ -133,7 +133,7 @@ namespace winsw.Native } } - private static string GetDomain(string s) + private static string? GetDomain(string s) { int stop = s.IndexOf("\\", StringComparison.Ordinal); if (stop >= 0) @@ -151,7 +151,7 @@ namespace winsw.Native private static string GetLocalAccountIfLocalAccount(string username) { var machinename = Environment.MachineName; - string domain = GetDomain(username); + string? domain = GetDomain(username); if (domain == null || domain.ToLower() == machinename.ToLower() || domain == ".") { return GetLogin(username); @@ -287,7 +287,7 @@ namespace winsw.Native internal static extern bool ChangeServiceConfig2(IntPtr hService, SERVICE_CONFIG_INFOLEVEL dwInfoLevel, ref SERVICE_DELAYED_AUTO_START sfa); [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - internal static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess); + internal static extern IntPtr OpenSCManager(string? machineName, string? databaseName, uint dwAccess); [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] internal static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess); diff --git a/src/Core/WinSWCore/Native/Kernel32.cs b/src/Core/WinSWCore/Native/Kernel32.cs index abc7c85..14dd870 100755 --- a/src/Core/WinSWCore/Native/Kernel32.cs +++ b/src/Core/WinSWCore/Native/Kernel32.cs @@ -13,12 +13,17 @@ namespace winsw.Native public static extern bool SetStdHandle(int nStdHandle, SafeFileHandle handle); [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool CreateProcess(string lpApplicationName, - string lpCommandLine, IntPtr lpProcessAttributes, - IntPtr lpThreadAttributes, bool bInheritHandles, - uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, - [In] ref STARTUPINFO lpStartupInfo, - out PROCESS_INFORMATION lpProcessInformation); + public static extern bool CreateProcess( + string? lpApplicationName, + string lpCommandLine, + IntPtr lpProcessAttributes, + IntPtr lpThreadAttributes, + bool bInheritHandles, + uint dwCreationFlags, + IntPtr lpEnvironment, + string? lpCurrentDirectory, + [In] ref STARTUPINFO lpStartupInfo, + out PROCESS_INFORMATION lpProcessInformation); [DllImport("kernel32.dll")] public static extern int GetLastError(); diff --git a/src/Core/WinSWCore/NullableAttributes.cs b/src/Core/WinSWCore/NullableAttributes.cs new file mode 100644 index 0000000..71aab86 --- /dev/null +++ b/src/Core/WinSWCore/NullableAttributes.cs @@ -0,0 +1,12 @@ +#if !NETCOREAPP +namespace System.Diagnostics.CodeAnalysis +{ + /// Specifies that null is allowed as an input even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class AllowNullAttribute : Attribute { } + + /// Specifies that an output may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class MaybeNullAttribute : Attribute { } +} +#endif diff --git a/src/Core/WinSWCore/ServiceDescriptor.cs b/src/Core/WinSWCore/ServiceDescriptor.cs index 510206e..511dbb8 100755 --- a/src/Core/WinSWCore/ServiceDescriptor.cs +++ b/src/Core/WinSWCore/ServiceDescriptor.cs @@ -1,8 +1,8 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Text; using System.Xml; using winsw.Configuration; using winsw.Native; @@ -81,7 +81,9 @@ namespace winsw /// /// Loads descriptor from existing DOM /// +#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. public ServiceDescriptor(XmlDocument dom) +#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. { this.dom = dom; } @@ -96,10 +98,10 @@ namespace winsw private string SingleElement(string tagName) { - return SingleElement(tagName, false); + return SingleElement(tagName, false)!; } - private string SingleElement(string tagName, bool optional) + private string? SingleElement(string tagName, bool optional) { var n = dom.SelectSingleNode("//" + tagName); if (n == null && !optional) @@ -174,7 +176,7 @@ namespace winsw /// /// Optionally specify a different Path to an executable to shutdown the service. /// - public string StopExecutable => SingleElement("stopexecutable", true); + public string? StopExecutable => SingleElement("stopexecutable", true); /// /// Arguments or multiple optional argument elements which overrule the arguments element. @@ -183,7 +185,7 @@ namespace winsw { get { - string arguments = AppendTags("argument", null); + string? arguments = AppendTags("argument", null); if (arguments == null) { @@ -206,19 +208,19 @@ namespace winsw /// /// Multiple optional startargument elements. /// - public string Startarguments => AppendTags("startargument", Defaults.Startarguments); + public string? Startarguments => AppendTags("startargument", Defaults.Startarguments); /// /// Multiple optional stopargument elements. /// - public string Stoparguments => AppendTags("stopargument", Defaults.Stoparguments); + public string? Stoparguments => AppendTags("stopargument", Defaults.Stoparguments); public string WorkingDirectory { get { var wd = SingleElement("workingdirectory", true); - return string.IsNullOrEmpty(wd) ? Defaults.WorkingDirectory : wd; + return string.IsNullOrEmpty(wd) ? Defaults.WorkingDirectory : wd!; } } @@ -226,65 +228,65 @@ namespace winsw { get { - List res = new List(); - - XmlNode argumentNode = ExtensionsConfiguration; - XmlNodeList extensions = argumentNode?.SelectNodes("extension"); - if (extensions != null) + XmlNode? argumentNode = ExtensionsConfiguration; + XmlNodeList? extensions = argumentNode?.SelectNodes("extension"); + if (extensions == null) { - foreach (XmlNode e in extensions) - { - XmlElement extension = (XmlElement)e; - string extensionId = XmlHelper.SingleAttribute(extension, "id"); - res.Add(extensionId); - } + return new List(0); } - return res; + List result = new List(extensions.Count); + for (int i = 0; i < extensions.Count; i++) + { + result.Add(XmlHelper.SingleAttribute((XmlElement)extensions[i], "id")); + } + + return result; } } - public XmlNode ExtensionsConfiguration => dom.SelectSingleNode("//extensions"); + public XmlNode? ExtensionsConfiguration => dom.SelectSingleNode("//extensions"); /// /// Combines the contents of all the elements of the given name, /// or return null if no element exists. Handles whitespace quotation. /// - private string AppendTags(string tagName, string defaultValue = null) + private string? AppendTags(string tagName, string? defaultValue = null) { - XmlNode argumentNode = dom.SelectSingleNode("//" + tagName); - + XmlNode? argumentNode = dom.SelectSingleNode("//" + tagName); if (argumentNode == null) { return defaultValue; } - else + + StringBuilder arguments = new StringBuilder(); + + XmlNodeList argumentNodeList = dom.SelectNodes("//" + tagName); + for (int i = 0; i < argumentNodeList.Count; i++) { - string arguments = string.Empty; + arguments.Append(' '); - foreach (XmlElement argument in dom.SelectNodes("//" + tagName)) + string token = Environment.ExpandEnvironmentVariables(argumentNodeList[i].InnerText); + + if (token.StartsWith("\"") && token.EndsWith("\"")) { - string token = Environment.ExpandEnvironmentVariables(argument.InnerText); - - if (token.StartsWith("\"") && token.EndsWith("\"")) + // for backward compatibility, if the argument is already quoted, leave it as is. + // in earlier versions we didn't handle quotation, so the user might have worked + // around it by themselves + } + else + { + if (token.Contains(" ")) { - // for backward compatibility, if the argument is already quoted, leave it as is. - // in earlier versions we didn't handle quotation, so the user might have worked - // around it by themselves + arguments.Append('"').Append(token).Append('"'); + continue; } - else - { - if (token.Contains(" ")) - { - token = '"' + token + '"'; - } - } - - arguments += " " + token; } - return arguments; + arguments.Append(token); } + + return arguments.ToString(); } /// @@ -311,7 +313,7 @@ namespace winsw { get { - string mode = null; + string? mode = null; // first, backward compatibility with older configuration XmlElement e = (XmlElement)dom.SelectSingleNode("//logmode"); @@ -469,19 +471,19 @@ namespace winsw { get { - var xmlNodeList = dom.SelectNodes("//depend"); - if (xmlNodeList != null) + XmlNodeList? nodeList = dom.SelectNodes("//depend"); + if (nodeList == null) { - ArrayList serviceDependencies = new ArrayList(); - foreach (XmlNode depend in xmlNodeList) - { - serviceDependencies.Add(depend.InnerText); - } - - return (string[])serviceDependencies.ToArray(typeof(string)); + return Defaults.ServiceDependencies; } - return Defaults.ServiceDependencies; + string[] serviceDependencies = new string[nodeList.Count]; + for (int i = 0; i < nodeList.Count; i++) + { + serviceDependencies[i] = nodeList[i].InnerText; + } + + return serviceDependencies; } } @@ -558,10 +560,12 @@ namespace winsw get { Dictionary map = new Dictionary(); - foreach (XmlNode n in dom.SelectNodes("//env")) + XmlNodeList nodeList = dom.SelectNodes("//env"); + for (int i = 0; i < nodeList.Count; i++) { - string key = n.Attributes["name"].Value; - string value = Environment.ExpandEnvironmentVariables(n.Attributes["value"].Value); + XmlNode node = nodeList[i]; + string key = node.Attributes["name"].Value; + string value = Environment.ExpandEnvironmentVariables(node.Attributes["value"].Value); map[key] = value; Environment.SetEnvironmentVariable(key, value); @@ -579,22 +583,22 @@ namespace winsw { get { - var xmlNodeList = dom.SelectNodes("//download"); - if (xmlNodeList == null) + XmlNodeList? nodeList = dom.SelectNodes("//download"); + if (nodeList == null) { return Defaults.Downloads; } - List r = new List(); - foreach (XmlNode n in xmlNodeList) + List result = new List(nodeList.Count); + for (int i = 0; i < nodeList.Count; i++) { - if (n is XmlElement el) + if (nodeList[i] is XmlElement element) { - r.Add(new Download(el)); + result.Add(new Download(element)); } } - return r; + return result; } } @@ -602,41 +606,35 @@ namespace winsw { get { - List r = new List(); - var childNodes = dom.SelectNodes("//onfailure"); - if (childNodes != null) + XmlNodeList? childNodes = dom.SelectNodes("//onfailure"); + if (childNodes == null) { - foreach (XmlNode n in childNodes) - { - SC_ACTION_TYPE type; - string action = n.Attributes["action"].Value; - switch (action) - { - case "restart": - type = SC_ACTION_TYPE.SC_ACTION_RESTART; - break; - case "none": - type = SC_ACTION_TYPE.SC_ACTION_NONE; - break; - case "reboot": - type = SC_ACTION_TYPE.SC_ACTION_REBOOT; - break; - default: - throw new Exception("Invalid failure action: " + action); - } - - XmlAttribute delay = n.Attributes["delay"]; - r.Add(new SC_ACTION(type, delay != null ? ParseTimeSpan(delay.Value) : TimeSpan.Zero)); - } + return new List(0); } - return r; + List result = new List(childNodes.Count); + for (int i = 0; i < childNodes.Count; i++) + { + XmlNode node = childNodes[i]; + string action = node.Attributes["action"].Value; + SC_ACTION_TYPE type = action switch + { + "restart" => SC_ACTION_TYPE.SC_ACTION_RESTART, + "none" => SC_ACTION_TYPE.SC_ACTION_NONE, + "reboot" => SC_ACTION_TYPE.SC_ACTION_REBOOT, + _ => throw new Exception("Invalid failure action: " + action) + }; + XmlAttribute? delay = node.Attributes["delay"]; + result.Add(new SC_ACTION(type, delay != null ? ParseTimeSpan(delay.Value) : TimeSpan.Zero)); + } + + return result; } } public TimeSpan ResetFailureAfter => SingleTimeSpanElement(dom, "resetfailure", Defaults.ResetFailureAfter); - protected string GetServiceAccountPart(string subNodeName) + protected string? GetServiceAccountPart(string subNodeName) { var node = dom.SelectSingleNode("//serviceaccount"); @@ -652,15 +650,15 @@ namespace winsw return null; } - protected string AllowServiceLogon => GetServiceAccountPart("allowservicelogon"); + protected string? AllowServiceLogon => GetServiceAccountPart("allowservicelogon"); // ReSharper disable once InconsistentNaming - protected string serviceAccountDomain => GetServiceAccountPart("domain"); + protected string? serviceAccountDomain => GetServiceAccountPart("domain"); // ReSharper disable once InconsistentNaming - protected string serviceAccountName => GetServiceAccountPart("user"); + protected string? serviceAccountName => GetServiceAccountPart("user"); - public string ServiceAccountPassword => GetServiceAccountPart("password"); + public string? ServiceAccountPassword => GetServiceAccountPart("password"); public string ServiceAccountUser => (serviceAccountDomain ?? "NULL") + @"\" + (serviceAccountName ?? "NULL"); diff --git a/src/Core/WinSWCore/Util/ProcessHelper.cs b/src/Core/WinSWCore/Util/ProcessHelper.cs index c8d6e77..7683048 100644 --- a/src/Core/WinSWCore/Util/ProcessHelper.cs +++ b/src/Core/WinSWCore/Util/ProcessHelper.cs @@ -131,8 +131,17 @@ namespace winsw.Util /// Completion callback. If null, the completion won't be monitored /// Log handler. If enabled, logs will be redirected to the process and then reported /// Redirect standard input - public static void StartProcessAndCallbackForExit(Process processToStart, string executable = null, string arguments = null, Dictionary envVars = null, - string workingDirectory = null, ProcessPriorityClass? priority = null, ProcessCompletionCallback callback = null, bool redirectStdin = true, LogHandler logHandler = null, bool hideWindow = false) + public static void StartProcessAndCallbackForExit( + Process processToStart, + string? executable = null, + string? arguments = null, + Dictionary? envVars = null, + string? workingDirectory = null, + ProcessPriorityClass? priority = null, + ProcessCompletionCallback? callback = null, + bool redirectStdin = true, + LogHandler? logHandler = null, + bool hideWindow = false) { var ps = processToStart.StartInfo; ps.FileName = executable ?? ps.FileName; diff --git a/src/Core/WinSWCore/Util/SigIntHelper.cs b/src/Core/WinSWCore/Util/SigIntHelper.cs index d6e639c..801539b 100644 --- a/src/Core/WinSWCore/Util/SigIntHelper.cs +++ b/src/Core/WinSWCore/Util/SigIntHelper.cs @@ -19,7 +19,7 @@ namespace winsw.Util private static extern bool FreeConsole(); [DllImport(KERNEL32)] - private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add); + private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate? HandlerRoutine, bool Add); // Delegate type to be used as the Handler Routine for SCCH private delegate bool ConsoleCtrlDelegate(CtrlTypes CtrlType); diff --git a/src/Core/WinSWCore/Util/XmlHelper.cs b/src/Core/WinSWCore/Util/XmlHelper.cs index e5c98e5..231ea10 100644 --- a/src/Core/WinSWCore/Util/XmlHelper.cs +++ b/src/Core/WinSWCore/Util/XmlHelper.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Xml; @@ -11,10 +12,10 @@ namespace winsw.Util /// /// Parent node /// Element name - /// If optional, don't throw an exception if the elemen is missing + /// If optional, don't throw an exception if the element is missing /// String value or null /// The required element is missing - public static string SingleElement(XmlNode node, string tagName, bool optional) + public static string? SingleElement(XmlNode node, string tagName, bool optional) { var n = node.SelectSingleNode(tagName); if (n == null && !optional) @@ -28,10 +29,10 @@ namespace winsw.Util /// /// Parent node /// Element name - /// If otional, don't throw an exception if the elemen is missing + /// If otional, don't throw an exception if the element is missing /// String value or null /// The required element is missing - public static XmlNode SingleNode(XmlNode node, string tagName, bool optional) + public static XmlNode? SingleNode(XmlNode node, string tagName, bool optional) { var n = node.SelectSingleNode(tagName); if (n == null && !optional) @@ -54,7 +55,7 @@ namespace winsw.Util throw new InvalidDataException("Attribute <" + attributeName + "> is missing in configuration XML"); } - return SingleAttribute(node, attributeName, default(TAttributeType)); + return SingleAttribute(node, attributeName, default); } /// @@ -64,7 +65,8 @@ namespace winsw.Util /// Attribute name /// Default value /// Attribute value (or default) - public static TAttributeType SingleAttribute(XmlElement node, string attributeName, TAttributeType defaultValue) + [return: MaybeNull] + public static TAttributeType SingleAttribute(XmlElement node, string attributeName, [AllowNull] TAttributeType defaultValue) { if (!node.HasAttribute(attributeName)) return defaultValue; diff --git a/src/Core/WinSWCore/WinSWCore.csproj b/src/Core/WinSWCore/WinSWCore.csproj index 40bcd01..cc339d4 100644 --- a/src/Core/WinSWCore/WinSWCore.csproj +++ b/src/Core/WinSWCore/WinSWCore.csproj @@ -2,6 +2,8 @@ net20;net40;net461;netcoreapp3.1 + latest + enable winsw true diff --git a/src/Core/WinSWCore/Wmi.cs b/src/Core/WinSWCore/Wmi.cs index d8837ba..119123f 100755 --- a/src/Core/WinSWCore/Wmi.cs +++ b/src/Core/WinSWCore/Wmi.cs @@ -89,17 +89,19 @@ namespace WMI public WmiRoot() : this(null) { } - public WmiRoot(string machineName) + public WmiRoot(string? machineName) { - ConnectionOptions options = new ConnectionOptions(); - options.EnablePrivileges = true; - options.Impersonation = ImpersonationLevel.Impersonate; - options.Authentication = AuthenticationLevel.PacketPrivacy; + ConnectionOptions options = new ConnectionOptions + { + EnablePrivileges = true, + Impersonation = ImpersonationLevel.Impersonate, + Authentication = AuthenticationLevel.PacketPrivacy, + }; string path; if (machineName != null) - path = string.Format(@"\\{0}\root\cimv2", machineName); + path = $@"\\{machineName}\root\cimv2"; else path = @"\root\cimv2"; scope = new ManagementScope(path, options); @@ -113,7 +115,7 @@ namespace WMI abstract class BaseHandler : IProxyInvocationHandler { - public abstract object Invoke(object proxy, MethodInfo method, object[] args); + public abstract object? Invoke(object proxy, MethodInfo method, object[] args); protected void CheckError(ManagementBaseObject result) { @@ -127,9 +129,9 @@ namespace WMI { private readonly ManagementObject _mo; - public InstanceHandler(ManagementObject o) { _mo = o; } + public InstanceHandler(ManagementObject o) => _mo = o; - public override object Invoke(object proxy, MethodInfo method, object[] args) + public override object? Invoke(object proxy, MethodInfo method, object[] args) { if (method.DeclaringType == typeof(IWmiObject)) { @@ -153,7 +155,7 @@ namespace WMI ManagementBaseObject wmiArgs = _mo.GetMethodParameters(method.Name); for (int i = 0; i < args.Length; i++) - wmiArgs[Capitalize(methodArgs[i].Name)] = args[i]; + wmiArgs[Capitalize(methodArgs[i].Name!)] = args[i]; CheckError(_mo.InvokeMethod(method.Name, wmiArgs, null)); return null; @@ -172,7 +174,7 @@ namespace WMI public ClassHandler(ManagementClass mc, string wmiClass) { _mc = mc; _wmiClass = wmiClass; } - public override object Invoke(object proxy, MethodInfo method, object[] args) + public override object? Invoke(object proxy, MethodInfo method, object[] args) { ParameterInfo[] methodArgs = method.GetParameters(); @@ -185,7 +187,7 @@ namespace WMI if (i != 0) query += " AND "; - query += ' ' + Capitalize(methodArgs[i].Name) + " = '" + args[i] + "'"; + query += ' ' + Capitalize(methodArgs[i].Name!) + " = '" + args[i] + "'"; } ManagementObjectSearcher searcher = new ManagementObjectSearcher(_mc.Scope, new ObjectQuery(query)); @@ -198,7 +200,7 @@ namespace WMI ManagementBaseObject wmiArgs = _mc.GetMethodParameters(method.Name); for (int i = 0; i < args.Length; i++) - wmiArgs[Capitalize(methodArgs[i].Name)] = args[i]; + wmiArgs[Capitalize(methodArgs[i].Name!)] = args[i]; CheckError(_mc.InvokeMethod(method.Name, wmiArgs, null)); return null; diff --git a/src/Core/WinSWCore/WmiSchema.cs b/src/Core/WinSWCore/WmiSchema.cs index 4a241e3..bfa55a1 100755 --- a/src/Core/WinSWCore/WmiSchema.cs +++ b/src/Core/WinSWCore/WmiSchema.cs @@ -48,7 +48,7 @@ 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, StartMode 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); diff --git a/src/Plugins/RunawayProcessKiller/RunawayProcessKiller.csproj b/src/Plugins/RunawayProcessKiller/RunawayProcessKiller.csproj index 7bbde10..7618eac 100644 --- a/src/Plugins/RunawayProcessKiller/RunawayProcessKiller.csproj +++ b/src/Plugins/RunawayProcessKiller/RunawayProcessKiller.csproj @@ -2,6 +2,8 @@ net20;net40;net461;netcoreapp3.1 + latest + enable winsw.Plugins.RunawayProcessKiller true diff --git a/src/Plugins/RunawayProcessKiller/RunawayProcessKillerExtension.cs b/src/Plugins/RunawayProcessKiller/RunawayProcessKillerExtension.cs index 974cfa3..9906c62 100644 --- a/src/Plugins/RunawayProcessKiller/RunawayProcessKillerExtension.cs +++ b/src/Plugins/RunawayProcessKiller/RunawayProcessKillerExtension.cs @@ -40,12 +40,16 @@ namespace winsw.Plugins.RunawayProcessKiller private static readonly ILog Logger = LogManager.GetLogger(typeof(RunawayProcessKillerExtension)); +#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. public RunawayProcessKillerExtension() +#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. { // Default initializer } +#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. public RunawayProcessKillerExtension(string pidfile, int stopTimeoutMs = 5000, bool stopParentFirst = false, bool checkWinSWEnvironmentVariable = true) +#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. { this.Pidfile = pidfile; this.StopTimeout = TimeSpan.FromMilliseconds(stopTimeoutMs); @@ -57,9 +61,9 @@ namespace winsw.Plugins.RunawayProcessKiller { // We expect the upper logic to process any errors // TODO: a better parser API for types would be useful - Pidfile = XmlHelper.SingleElement(node, "pidfile", false); - StopTimeout = TimeSpan.FromMilliseconds(int.Parse(XmlHelper.SingleElement(node, "stopTimeout", false))); - StopParentProcessFirst = bool.Parse(XmlHelper.SingleElement(node, "stopParentFirst", false)); + Pidfile = XmlHelper.SingleElement(node, "pidfile", false)!; + StopTimeout = TimeSpan.FromMilliseconds(int.Parse(XmlHelper.SingleElement(node, "stopTimeout", false)!)); + StopParentProcessFirst = bool.Parse(XmlHelper.SingleElement(node, "stopParentFirst", false)!); ServiceId = descriptor.Id; // TODO: Consider making it documented var checkWinSWEnvironmentVariable = XmlHelper.SingleElement(node, "checkWinSWEnvironmentVariable", true); @@ -117,7 +121,7 @@ namespace winsw.Plugins.RunawayProcessKiller } // Ensure the process references the service - string affiliatedServiceId; + string? affiliatedServiceId; // TODO: This method is not ideal since it works only for vars explicitly mentioned in the start info // No Windows 10- compatible solution for EnvVars retrieval, see https://blog.gapotchenko.com/eazfuscator.net/reading-environment-variables StringDictionary previousProcessEnvVars = proc.StartInfo.EnvironmentVariables; diff --git a/src/Plugins/SharedDirectoryMapper/SharedDirectoryMapper.cs b/src/Plugins/SharedDirectoryMapper/SharedDirectoryMapper.cs index 27d6f74..d67a78b 100644 --- a/src/Plugins/SharedDirectoryMapper/SharedDirectoryMapper.cs +++ b/src/Plugins/SharedDirectoryMapper/SharedDirectoryMapper.cs @@ -28,12 +28,12 @@ namespace winsw.Plugins.SharedDirectoryMapper public override void Configure(ServiceDescriptor descriptor, XmlNode node) { - var nodes = XmlHelper.SingleNode(node, "mapping", false).SelectNodes("map"); - if (nodes != null) + var mapNodes = XmlHelper.SingleNode(node, "mapping", false)!.SelectNodes("map"); + if (mapNodes != null) { - foreach (XmlNode mapNode in nodes) + for (int i = 0; i < mapNodes.Count; i++) { - if (mapNode is XmlElement mapElement) + if (mapNodes[i] is XmlElement mapElement) { var config = SharedDirectoryMapperConfig.FromXml(mapElement); _entries.Add(config); diff --git a/src/Plugins/SharedDirectoryMapper/SharedDirectoryMapper.csproj b/src/Plugins/SharedDirectoryMapper/SharedDirectoryMapper.csproj index 6d1aebc..b3a5f9f 100644 --- a/src/Plugins/SharedDirectoryMapper/SharedDirectoryMapper.csproj +++ b/src/Plugins/SharedDirectoryMapper/SharedDirectoryMapper.csproj @@ -2,6 +2,8 @@ net20;net40;net461;netcoreapp3.1 + latest + enable winsw.Plugins.SharedDirectoryMapper true