mirror of https://github.com/winsw/winsw
commit
33da176920
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
// 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<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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
_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++)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
using System.Xml;
|
||||
|
||||
namespace winsw.Extensions
|
||||
{
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
using System.Xml;
|
||||
using winsw.Util;
|
||||
|
||||
namespace winsw.Extensions
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
// 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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
|
@ -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,65 +228,65 @@ 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)
|
||||
{
|
||||
XmlElement extension = (XmlElement)e;
|
||||
string extensionId = XmlHelper.SingleAttribute<string>(extension, "id");
|
||||
res.Add(extensionId);
|
||||
}
|
||||
return new List<string>(0);
|
||||
}
|
||||
|
||||
return res;
|
||||
List<string> result = new List<string>(extensions.Count);
|
||||
for (int i = 0; i < extensions.Count; i++)
|
||||
{
|
||||
result.Add(XmlHelper.SingleAttribute<string>((XmlElement)extensions[i], "id"));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/// <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,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<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);
|
||||
}
|
||||
|
||||
XmlAttribute delay = n.Attributes["delay"];
|
||||
r.Add(new SC_ACTION(type, delay != null ? ParseTimeSpan(delay.Value) : TimeSpan.Zero));
|
||||
}
|
||||
return new List<SC_ACTION>(0);
|
||||
}
|
||||
|
||||
return r;
|
||||
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 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");
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue