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);
+ }
+ }
+}