/// <summary> /// Start the service, auto detecting between Windows service and command line. Always blocks until service is stopped. /// </summary> public void Run(ServiceArguments argumentsOverride = null) { ServiceGracefullyStopped = new TaskCompletionSource <StopResult>(); try { Arguments = argumentsOverride ?? new ServiceArguments(System.Environment.GetCommandLineArgs().Skip(1).ToArray()); } catch (ArgumentException ex) { Console.WriteLine(ex.Message); Console.WriteLine(ServiceArguments.GetHelpDocumentation()); return; } if (argumentsOverride == null && ServiceArguments.IsHelpRequired(System.Environment.GetCommandLineArgs())) { Console.WriteLine(ServiceArguments.GetHelpDocumentation()); return; } if (Arguments.ServiceStartupMode == ServiceStartupMode.WindowsService) { Trace.WriteLine("Service starting as a Windows service..."); WindowsService = new DelegatingServiceBase(ServiceName, OnWindowsServiceStart, OnWindowsServiceStop); if (argumentsOverride == null) { Arguments = null; // Ensures OnWindowsServiceStart reloads parameters passed from Windows Service Manager. } ServiceBase.Run(WindowsService); // This calls OnWindowsServiceStart() on a different thread and blocks until the service stops. } else if (Arguments.ServiceStartupMode == ServiceStartupMode.VerifyConfigurations) { OnVerifyConfiguration(); } else { if (Arguments.ShutdownWhenPidExits != null) { try { MonitoredShutdownProcess = Process.GetProcessById(Arguments.ShutdownWhenPidExits.Value); } catch (ArgumentException e) { Console.WriteLine($"Service cannot start because monitored PID {Arguments.ShutdownWhenPidExits} is not running. Exception: {e}"); System.Environment.ExitCode = 1; ServiceGracefullyStopped.SetResult(StopResult.None); return; } Console.WriteLine($"Will perform graceful shutdown when PID {Arguments.ShutdownWhenPidExits} exits."); MonitoredShutdownProcess.Exited += (s, a) => { Console.WriteLine($"PID {Arguments.ShutdownWhenPidExits} has exited, shutting down..."); Stop(); }; MonitoredShutdownProcess.EnableRaisingEvents = true; } try { OnStart(); } catch (Exception e) { ServiceStartedEvent.TrySetException(e); throw; } if (Arguments.ServiceStartupMode == ServiceStartupMode.CommandLineInteractive) { Thread.Sleep(10); // Allow any startup log messages to flush to Console. Console.Title = ServiceName; if (Arguments.ConsoleOutputMode == ConsoleOutputMode.Color) { Console.ForegroundColor = ConsoleColor.Green; Console.Write("Service initialized in interactive mode (command line). Press "); Console.ForegroundColor = ConsoleColor.White; Console.BackgroundColor = ConsoleColor.DarkCyan; Console.Write("[Alt+S]"); Console.ForegroundColor = ConsoleColor.Green; Console.BackgroundColor = ConsoleColor.Black; Console.WriteLine(" to stop the service gracefully."); Console.ForegroundColor = ConsoleColor.Gray; } else { Console.WriteLine("Service initialized in interactive mode (command line). Press [Alt+S] to stop the service gracefully."); } Task.Factory.StartNew(() => { while (true) { var key = Console.ReadKey(true); if (key.Key == ConsoleKey.S && key.Modifiers == ConsoleModifiers.Alt) { Stop(); break; } } }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); } else { Console.WriteLine("Service initialized in non-interactive mode (command line). Waiting for stop request..."); } StopEvent.Reset(); ServiceStartedEvent.SetResult(null); StopEvent.WaitOne(); Console.WriteLine(" *** Shutting down... *** "); var maxShutdownTime = TimeSpan.FromSeconds((Arguments.OnStopWaitTimeSec ?? 0) + (Arguments.ServiceDrainTimeSec ?? 0)); bool isServiceGracefullyStopped = Task.Run(() => OnStop()).Wait(maxShutdownTime); if (isServiceGracefullyStopped == false) { Console.WriteLine($" *** Service failed to stop gracefully in the allotted time ({maxShutdownTime}), continuing with forced shutdown. *** "); } ServiceStartedEvent = new TaskCompletionSource <object>(); ServiceGracefullyStopped.SetResult(isServiceGracefullyStopped ? StopResult.Graceful : StopResult.Force); MonitoredShutdownProcess?.Dispose(); if (Arguments.ServiceStartupMode == ServiceStartupMode.CommandLineInteractive) { if (Arguments.ConsoleOutputMode == ConsoleOutputMode.Color) { Console.BackgroundColor = ConsoleColor.White; Console.ForegroundColor = ConsoleColor.Black; Console.WriteLine(" *** Shutdown complete. Press any key to exit. *** "); Console.BackgroundColor = ConsoleColor.Black; Console.ForegroundColor = ConsoleColor.Gray; } else { Console.WriteLine(" *** Shutdown complete. Press any key to exit. *** "); } Console.ReadKey(true); } } }