Merge pull request #356 from NextTurn/nrt

Annotate for nullable reference types
pull/367/head
Oleg Nenashev 2020-01-20 05:54:50 +01:00 committed by GitHub
commit 33da176920
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 370 additions and 328 deletions

View File

@ -7,11 +7,11 @@ namespace winsw.Logging
/// </summary>
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;

View File

@ -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<string, string> _envs;
private Dictionary<string, string>? _envs;
internal WinSWExtensionManager ExtensionManager { get; private set; }
@ -52,7 +53,7 @@ namespace winsw
/// <remarks>
/// The version will be taken from <see cref="AssemblyInfo"/>
/// </remarks>
public static Version Version => Assembly.GetExecutingAssembly().GetName().Version;
public static Version Version => Assembly.GetExecutingAssembly().GetName().Version!;
/// <summary>
/// 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
/// </summary>
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
/// <param name="descriptor">Service descriptor. If null, it will be initialized within the method.
/// In such case configs will be loaded from the XML Configuration File.</param>
/// <exception cref="Exception">Any unhandled exception</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;
}

View File

@ -0,0 +1,8 @@
#if !NETCOREAPP
namespace System.Diagnostics.CodeAnalysis
{
/// <summary>Applied to a method that will never return under any circumstance.</summary>
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
internal sealed class DoesNotReturnAttribute : Attribute { }
}
#endif

View File

@ -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);

View File

@ -3,6 +3,8 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net20;net40;net461;netcoreapp3.1</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<Version><!-- Populated by AppVeyor --></Version>
<AssemblyTitle>Windows Service Wrapper</AssemblyTitle>
<Description>Allows arbitrary process to run as a Windows service by wrapping it.</Description>

View File

@ -13,36 +13,29 @@ namespace winsw.Configuration
/// </summary>
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);
}
}
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<Native.SC_ACTION> FailureActions => new List<Native.SC_ACTION>();
public List<Native.SC_ACTION> FailureActions => new List<Native.SC_ACTION>(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<Download> Downloads => new List<Download>();
public Dictionary<string, string> EnvironmentVariables => new Dictionary<string, string>();
public List<Download> Downloads => new List<Download>(0);
public Dictionary<string, string> EnvironmentVariables => new Dictionary<string, string>(0);
// Misc
public bool BeepOnShutdown => false;
// Extensions
public XmlNode ExtensionsConfiguration => null;
public XmlNode? ExtensionsConfiguration => null;
}
}

View File

@ -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<Native.SC_ACTION> 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; }
}
}

View File

@ -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:

View File

@ -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
/// <param name="method">The method info that can be used to invoke the actual method on the object implementation</param>
/// <param name="parameters">Parameters to pass to the method</param>
/// <returns>Object</returns>
object Invoke(object proxy, MethodInfo method, object[] parameters);
object? Invoke(object proxy, MethodInfo method, object[] parameters);
}
/// <summary>
@ -23,7 +23,7 @@ namespace DynamicProxy
/// </summary>
public class MetaDataFactory
{
private static Hashtable typeMap = new Hashtable();
private static readonly Dictionary<string, Type> typeMap = new Dictionary<string, Type>();
/// <summary>
/// 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
///<returns>MethodInfo</returns>
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
/// </summary>
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<string, Type> _typeMap = new Dictionary<string, Type>();
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<Type, OpCode> OpCodeTypeMapper = new Dictionary<Type, OpCode>
{
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);
}
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)
@ -165,16 +170,12 @@ namespace DynamicProxy
}
private Type CreateType(IProxyInvocationHandler handler, Type[] interfaces, string dynamicTypeName)
{
Type retVal = null;
if (handler != null && interfaces != null)
{
Type objType = typeof(object);
Type handlerType = typeof(IProxyInvocationHandler);
AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = ASSEMBLY_NAME;
assemblyName.Name = AssemblyName;
assemblyName.Version = new Version(1, 0, 0, 0);
// create a new assembly for this proxy, one that isn't presisted on the file system
@ -187,7 +188,7 @@ namespace DynamicProxy
assemblyName, AssemblyBuilderAccess.Run);
// create a new module for this proxy
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(MODULE_NAME);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(ModuleName);
// Set the class to be public and sealed
TypeAttributes typeAttributes =
@ -200,15 +201,14 @@ namespace DynamicProxy
// Define a member variable to hold the delegate
FieldBuilder handlerField = typeBuilder.DefineField(
HANDLER_NAME, handlerType, FieldAttributes.Private);
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]);
ConstructorInfo baseConstructor = objType.GetConstructor(Type.EmptyTypes)!;
ConstructorBuilder delegateConstructor = typeBuilder.DefineConstructor(
MethodAttributes.Public, CallingConventions.Standard, new Type[] { handlerType });
#region( "Constructor IL Code" )
#region( "Constructor IL Code" )
ILGenerator constructorIL = delegateConstructor.GetILGenerator();
// Load "this"
@ -220,10 +220,10 @@ namespace DynamicProxy
// Load "this"
constructorIL.Emit(OpCodes.Ldarg_0);
// Call the super constructor
constructorIL.Emit(OpCodes.Call, superConstructor);
constructorIL.Emit(OpCodes.Call, baseConstructor);
// Constructor return
constructorIL.Emit(OpCodes.Ret);
#endregion
#endregion
// for every method that the interfaces define, build a corresponding
// method in the dynamic type that calls the handlers invoke method.
@ -232,22 +232,17 @@ namespace DynamicProxy
GenerateMethod(interfaceType, handlerField, typeBuilder);
}
retVal = typeBuilder.CreateType();
// assemblyBuilder.Save(dynamicTypeName + ".dll");
return typeBuilder.CreateType()!;
}
return retVal;
}
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++)

View File

@ -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)
{

View File

@ -1,5 +1,4 @@
using System;
using System.Xml;
using System.Xml;
namespace winsw.Extensions
{

View File

@ -1,5 +1,4 @@
using System;
using System.Xml;
using System.Xml;
using winsw.Util;
namespace winsw.Extensions

View File

@ -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)
{

View File

@ -30,7 +30,9 @@ namespace winsw
/// <summary>
/// Error and information about logging should be reported here.
/// </summary>
#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.
/// <summary>
/// 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);

View File

@ -11,6 +11,6 @@ namespace winsw.Logging
/// Locates Event Log for the service.
/// </summary>
/// <returns>Event Log or null if it is not avilable</returns>
EventLog locate();
EventLog? locate();
}
}

View File

@ -10,16 +10,16 @@ namespace winsw.Logging
/// </summary>
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)
{
EventLog? eventLog = provider.locate();
// We write the event iff the provider is ready
eventLog.WriteEntry(loggingEvent.RenderedMessage, toEventLogEntryType(loggingEvent.Level));
}
eventLog?.WriteEntry(loggingEvent.RenderedMessage, toEventLogEntryType(loggingEvent.Level));
}
private static EventLogEntryType toEventLogEntryType(Level level)

View File

@ -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);

View File

@ -13,10 +13,15 @@ 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,
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);

View File

@ -0,0 +1,12 @@
#if !NETCOREAPP
namespace System.Diagnostics.CodeAnalysis
{
/// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
internal sealed class AllowNullAttribute : Attribute { }
/// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
internal sealed class MaybeNullAttribute : Attribute { }
}
#endif

View File

@ -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
/// <summary>
/// Loads descriptor from existing DOM
/// </summary>
#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
/// <summary>
/// Optionally specify a different Path to an executable to shutdown the service.
/// </summary>
public string StopExecutable => SingleElement("stopexecutable", true);
public string? StopExecutable => SingleElement("stopexecutable", true);
/// <summary>
/// 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
/// <summary>
/// Multiple optional startargument elements.
/// </summary>
public string Startarguments => AppendTags("startargument", Defaults.Startarguments);
public string? Startarguments => AppendTags("startargument", Defaults.Startarguments);
/// <summary>
/// Multiple optional stopargument elements.
/// </summary>
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,45 +228,45 @@ namespace winsw
{
get
{
List<string> res = new List<string>();
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)
return new List<string>(0);
}
List<string> result = new List<string>(extensions.Count);
for (int i = 0; i < extensions.Count; i++)
{
XmlElement extension = (XmlElement)e;
string extensionId = XmlHelper.SingleAttribute<string>(extension, "id");
res.Add(extensionId);
result.Add(XmlHelper.SingleAttribute<string>((XmlElement)extensions[i], "id"));
}
return result;
}
}
return res;
}
}
public XmlNode ExtensionsConfiguration => dom.SelectSingleNode("//extensions");
public XmlNode? ExtensionsConfiguration => dom.SelectSingleNode("//extensions");
/// <summary>
/// Combines the contents of all the elements of the given name,
/// or return null if no element exists. Handles whitespace quotation.
/// </summary>
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
{
string arguments = string.Empty;
foreach (XmlElement argument in dom.SelectNodes("//" + tagName))
StringBuilder arguments = new StringBuilder();
XmlNodeList argumentNodeList = dom.SelectNodes("//" + tagName);
for (int i = 0; i < argumentNodeList.Count; i++)
{
string token = Environment.ExpandEnvironmentVariables(argument.InnerText);
arguments.Append(' ');
string token = Environment.ExpandEnvironmentVariables(argumentNodeList[i].InnerText);
if (token.StartsWith("\"") && token.EndsWith("\""))
{
@ -276,15 +278,15 @@ namespace winsw
{
if (token.Contains(" "))
{
token = '"' + token + '"';
arguments.Append('"').Append(token).Append('"');
continue;
}
}
arguments += " " + token;
arguments.Append(token);
}
return arguments;
}
return arguments.ToString();
}
/// <summary>
@ -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,20 +471,20 @@ 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;
}
string[] serviceDependencies = new string[nodeList.Count];
for (int i = 0; i < nodeList.Count; i++)
{
serviceDependencies[i] = nodeList[i].InnerText;
}
return serviceDependencies;
}
}
public string Id => SingleElement("id");
@ -558,10 +560,12 @@ namespace winsw
get
{
Dictionary<string, string> map = new Dictionary<string, string>();
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<Download> r = new List<Download>();
foreach (XmlNode n in xmlNodeList)
List<Download> result = new List<Download>(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<SC_ACTION> r = new List<SC_ACTION>();
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);
return new List<SC_ACTION>(0);
}
XmlAttribute delay = n.Attributes["delay"];
r.Add(new SC_ACTION(type, delay != null ? ParseTimeSpan(delay.Value) : TimeSpan.Zero));
}
List<SC_ACTION> result = new List<SC_ACTION>(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 r;
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");

View File

@ -131,8 +131,17 @@ namespace winsw.Util
/// <param name="callback">Completion callback. If null, the completion won't be monitored</param>
/// <param name="logHandler">Log handler. If enabled, logs will be redirected to the process and then reported</param>
/// <param name="redirectStdin">Redirect standard input</param>
public static void StartProcessAndCallbackForExit(Process processToStart, string executable = null, string arguments = null, Dictionary<string, string> 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<string, string>? 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;

View File

@ -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);

View File

@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Xml;
@ -11,10 +12,10 @@ namespace winsw.Util
/// </summary>
/// <param name="node">Parent node</param>
/// <param name="tagName">Element name</param>
/// <param name="optional">If optional, don't throw an exception if the elemen is missing</param>
/// <param name="optional">If optional, don't throw an exception if the element is missing</param>
/// <returns>String value or null</returns>
/// <exception cref="InvalidDataException">The required element is missing</exception>
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
/// </summary>
/// <param name="node">Parent node</param>
/// <param name="tagName">Element name</param>
/// <param name="optional">If otional, don't throw an exception if the elemen is missing</param>
/// <param name="optional">If otional, don't throw an exception if the element is missing</param>
/// <returns>String value or null</returns>
/// <exception cref="InvalidDataException">The required element is missing</exception>
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<TAttributeType>(node, attributeName, default);
}
/// <summary>
@ -64,7 +65,8 @@ namespace winsw.Util
/// <param name="attributeName">Attribute name</param>
/// <param name="defaultValue">Default value</param>
/// <returns>Attribute value (or default)</returns>
public static TAttributeType SingleAttribute<TAttributeType>(XmlElement node, string attributeName, TAttributeType defaultValue)
[return: MaybeNull]
public static TAttributeType SingleAttribute<TAttributeType>(XmlElement node, string attributeName, [AllowNull] TAttributeType defaultValue)
{
if (!node.HasAttribute(attributeName))
return defaultValue;

View File

@ -2,6 +2,8 @@
<PropertyGroup>
<TargetFrameworks>net20;net40;net461;netcoreapp3.1</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<Version><!-- Populated by AppVeyor --></Version>
<RootNamespace>winsw</RootNamespace>
<SignAssembly>true</SignAssembly>

View File

@ -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;

View File

@ -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);

View File

@ -2,6 +2,8 @@
<PropertyGroup>
<TargetFrameworks>net20;net40;net461;netcoreapp3.1</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<Version><!-- Populated by AppVeyor --></Version>
<RootNamespace>winsw.Plugins.RunawayProcessKiller</RootNamespace>
<SignAssembly>true</SignAssembly>

View File

@ -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;

View File

@ -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);

View File

@ -2,6 +2,8 @@
<PropertyGroup>
<TargetFrameworks>net20;net40;net461;netcoreapp3.1</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<Version><!-- Populated by AppVeyor --></Version>
<RootNamespace>winsw.Plugins.SharedDirectoryMapper</RootNamespace>
<SignAssembly>true</SignAssembly>