diff --git a/Directory.Build.targets b/Directory.Build.targets index 801993952..7cabbb043 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -33,6 +33,7 @@ + diff --git a/src/Moryx.Runtime.Kernel/KernelServiceCollectionExtensions.cs b/src/Moryx.Runtime.Kernel/KernelServiceCollectionExtensions.cs index 83bc5b833..4ca6b5931 100644 --- a/src/Moryx.Runtime.Kernel/KernelServiceCollectionExtensions.cs +++ b/src/Moryx.Runtime.Kernel/KernelServiceCollectionExtensions.cs @@ -74,6 +74,15 @@ public static void AddMoryxModules(this IServiceCollection serviceCollection) } } + /// + /// Register BackgroundService that controls that starts and stops the MORYX modules inside the lifetime of the host. + /// The MORYX Service can hook into the command line and gracefuly stop the modules when the user tries to close the window. + /// + public static void AddMoryxService(this IServiceCollection serviceCollection) + { + serviceCollection.AddHostedService(); + } + /// /// Use the moryx config manager /// diff --git a/src/Moryx.Runtime.Kernel/Moryx.Runtime.Kernel.csproj b/src/Moryx.Runtime.Kernel/Moryx.Runtime.Kernel.csproj index fdc0e91d4..e08ab3cbd 100644 --- a/src/Moryx.Runtime.Kernel/Moryx.Runtime.Kernel.csproj +++ b/src/Moryx.Runtime.Kernel/Moryx.Runtime.Kernel.csproj @@ -11,6 +11,7 @@ + @@ -18,6 +19,5 @@ - \ No newline at end of file diff --git a/src/Moryx.Runtime.Kernel/MoryxService.cs b/src/Moryx.Runtime.Kernel/MoryxService.cs new file mode 100644 index 000000000..b020ec1a8 --- /dev/null +++ b/src/Moryx.Runtime.Kernel/MoryxService.cs @@ -0,0 +1,81 @@ +using Castle.Core.Logging; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Moryx.Runtime.Modules; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Moryx.Runtime.Kernel +{ + internal class MoryxService : BackgroundService + { + private readonly IModuleManager moduleManager; + private readonly IHost lifeTime; + private readonly ILogger logger; + + // Console shutdown handling according to https://stackoverflow.com/questions/21751545/how-to-make-a-console-app-exit-gracefully-when-it-is-closed + [DllImport("Kernel32")] + public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add); + + // A delegate type to be used as the handler routine + // for SetConsoleCtrlHandler. + public delegate bool HandlerRoutine(CtrlTypes CtrlType); + + // An enumerated type for the control messages + // sent to the handler routine. + public enum CtrlTypes + { + CTRL_C_EVENT = 0, + CTRL_BREAK_EVENT, + CTRL_CLOSE_EVENT, + CTRL_LOGOFF_EVENT = 5, + CTRL_SHUTDOWN_EVENT + } + + /// + /// Callback that is called when a console event is raised. + /// + /// + /// + private bool ConsoleCtrlCheck(CtrlTypes ctrlType) + { + // Stop the host and await shutdown. + lifeTime.StopAsync(TimeSpan.FromSeconds(100)).ContinueWith(task => + { + if (task.IsFaulted) + { + logger.LogError(task.Exception, "Received failure on shutdown."); + } + }).Wait(); + return true; + } + + public MoryxService(IModuleManager moduleManager, IHost lifetTime, ILogger logger) + { + this.moduleManager = moduleManager; + this.lifeTime = lifetTime; + this.logger = logger; + } + + public override async Task StartAsync(CancellationToken cancellationToken) + { + SetConsoleCtrlHandler(ConsoleCtrlCheck, true); + moduleManager.StartModules(); + await base.StartAsync(cancellationToken); + } + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + return Task.CompletedTask; + } + + public override async Task StopAsync(CancellationToken cancellationToken) + { + moduleManager.StopModules(); + await base.StopAsync(cancellationToken); + } + } +}