From cec8b91ebd32250feba494a58dbb5c5ddd0ed785 Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 15 Sep 2026 11:09:43 +0200 Subject: [PATCH] Desktop beendet sich auf SIGTERM, statt bis zum SIGKILL weiterzulaufen #600: Die ConsoleLifetime des Hosts faengt SIGTERM ab und ruft StopApplication. Die Avalonia-Oberflaeche hing nicht daran - Main wartete weiter auf sie, und systemctl stop, Abmelden oder Herunterfahren endeten mit SIGKILL. App.axaml.cs haengt AvaloniaUiHost.ShutdownFromHost an ApplicationStopping: Die Oberflaeche schliesst sich ohne Sicherheitsabfrage (der Stopp von aussen ist selbst die Anweisung), danach laeuft der geordnete Dienst-Stopp aus Program.Main. Auf der Linux-VM geprueft: SIGTERM -> beendet nach 1 s, "Dienste in 0,1 s beendet"; Gegenprobe "Beenden" in der Oberflaeche ebenso. Co-Authored-By: Claude Opus 5 --- src/PolyTrader.App.Avalonia/App.axaml.cs | 13 +++++++++++++ .../Shell/AvaloniaUiHost.cs | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/PolyTrader.App.Avalonia/App.axaml.cs b/src/PolyTrader.App.Avalonia/App.axaml.cs index ea5581f..a813be6 100644 --- a/src/PolyTrader.App.Avalonia/App.axaml.cs +++ b/src/PolyTrader.App.Avalonia/App.axaml.cs @@ -2,7 +2,9 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; +using Avalonia.Threading; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using PolyTrader.App.Avalonia.Shell; using PolyTrader.App.Avalonia.Views; @@ -50,6 +52,17 @@ namespace PolyTrader.App.Avalonia // Schliessen der Shell laeuft ueber die Sicherheitsabfrage (wie bisher das X). desktop.ShutdownMode = global::Avalonia.Controls.ShutdownMode.OnMainWindowClose; + // Stopp von aussen (SIGTERM, systemctl stop, Abmelden): Die ConsoleLifetime des Hosts faengt das + // Signal ab und ruft StopApplication - die Oberflaeche hing daran bisher nicht, Main wartete weiter + // auf sie, und der Prozess endete erst mit SIGKILL. Jetzt schliesst sie sich, danach laeuft der + // geordnete Stopp in Program.Main. Beim eigenen "Beenden" feuert ApplicationStopping erst, wenn die + // Oberflaeche schon zu ist; der Aufruf findet dann ShutdownConfirmed gesetzt vor. + Services.GetService()?.ApplicationStopping.Register(() => + { + try { Dispatcher.UIThread.Post(uiHost.ShutdownFromHost); } + catch { /* Dispatcher schon beendet - dann ist die Oberflaeche ohnehin zu */ } + }); + // Erste Seite: die Startseite (frühere LauncherForm). uiHost.ActivateMain(); } diff --git a/src/PolyTrader.App.Avalonia/Shell/AvaloniaUiHost.cs b/src/PolyTrader.App.Avalonia/Shell/AvaloniaUiHost.cs index 33d3260..43b0471 100644 --- a/src/PolyTrader.App.Avalonia/Shell/AvaloniaUiHost.cs +++ b/src/PolyTrader.App.Avalonia/Shell/AvaloniaUiHost.cs @@ -102,6 +102,23 @@ namespace PolyTrader.App.Avalonia.Shell /// public void RequestShutdown() => RequestShutdown(onConfirmed: null); + /// + /// Schließt die Oberfläche OHNE Sicherheitsabfrage – nur für einen Stopp von außen (SIGTERM, systemctl + /// stop, Abmelden, Herunterfahren). Der ist selbst schon die ausdrückliche Anweisung; eine Abfrage, die + /// niemand beantwortet, hielt den Prozess bis zum SIGKILL am Leben. Nach dem Schließen läuft derselbe + /// geordnete Stopp der Dienste wie bei „Beenden" (Program.Main). Muss auf dem UI-Thread laufen. + /// + public void ShutdownFromHost() + { + if (ShutdownConfirmed) return; + ShutdownConfirmed = true; + if (global::Avalonia.Application.Current?.ApplicationLifetime + is global::Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.Shutdown(); + } + } + /// /// Wie , mit einem Schritt zwischen Bestätigung und Herunterfahren. ///