Du hast auf den Branch log-test-branch 2026-04-12 20:57:35 +00:00 gepusht
Neuer Pull-Request
Du hast auf den Branch anon-test2 2026-04-12 20:50:35 +00:00 gepusht
Neuer Pull-Request
Dateien
Soft-LTS/App.xaml.cs
2026-04-12 17:22:31 +02:00

198 Zeilen
6.2 KiB
C#

using System.IO;
using System.Reflection;
using System.Text.Json;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
using PrinterMonitor.Configuration;
using PrinterMonitor.Services;
using PrinterMonitor.ViewModels;
using PrinterMonitor.Views;
namespace PrinterMonitor;
public partial class App : System.Windows.Application
{
private static Mutex? _singleInstanceMutex;
private AppSettings? _settings;
private PrinterService? _printerService;
private NotifyIcon? _notifyIcon;
private MainWindow? _mainWindow;
protected override async void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// Sicherstellen, dass nur eine Instanz läuft
_singleInstanceMutex = new Mutex(true, "DS-Soft-LTS-PrinterMonitor", out bool isNewInstance);
if (!isNewInstance)
{
System.Windows.MessageBox.Show(
"DS Soft-LTS läuft bereits.", "Bereits gestartet",
MessageBoxButton.OK, MessageBoxImage.Information);
Shutdown();
return;
}
// Konfiguration laden
var configPath = Path.Combine(AppContext.BaseDirectory, "appsettings.json");
try
{
if (File.Exists(configPath))
{
var json = await File.ReadAllTextAsync(configPath);
_settings = JsonSerializer.Deserialize<AppSettings>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
}
}
catch (Exception ex)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Fehler beim Laden der Konfiguration: {ex.Message}");
}
if (_settings == null)
{
System.Windows.MessageBox.Show("Konfigurationsdatei nicht gefunden/lesbar");
Shutdown();
return;
}
var validationErrors = _settings.Validate();
if (validationErrors.Count > 0)
{
var msg = "Konfigurationsfehler:\n\n" + string.Join("\n", validationErrors);
System.Windows.MessageBox.Show(msg, "Konfigurationsfehler",
MessageBoxButton.OK, MessageBoxImage.Warning);
}
// PrinterService starten (Monitoring + TCP-Push)
_printerService = new PrinterService(_settings);
await _printerService.StartAsync();
// Tray-Icon erstellen
CreateTrayIcon();
}
private void CreateTrayIcon()
{
System.Drawing.Icon? icon = null;
try
{
// Stream explizit disposen: Icon lädt Daten sofort, Stream danach nicht mehr nötig
using var stream = GetResourceStream(new Uri("pack://application:,,,/Resources/app.ico"))?.Stream;
if (stream != null)
icon = new System.Drawing.Icon(stream);
}
catch (Exception ex)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Tray-Icon konnte nicht geladen werden: {ex.Message}");
}
_notifyIcon = new System.Windows.Forms.NotifyIcon
{
Icon = icon,
Text = $"DS Soft-LTS v{MainViewModel.VersionString}",
Visible = true,
ContextMenuStrip = CreateTrayMenu()
};
_notifyIcon.DoubleClick += (s, e) => ShowWindow();
}
private ContextMenuStrip CreateTrayMenu()
{
var menu = new ContextMenuStrip();
var showItem = new ToolStripMenuItem("Anzeigen");
showItem.Click += (s, e) => ShowWindow();
menu.Items.Add(showItem);
menu.Items.Add(new ToolStripSeparator());
var exitItem = new ToolStripMenuItem("Beenden");
exitItem.Click += (s, e) => ExitApplication();
menu.Items.Add(exitItem);
return menu;
}
private void ShowWindow()
{
if (_mainWindow == null)
{
var mainVm = new MainViewModel(_printerService!, _settings!);
_mainWindow = new MainWindow { DataContext = mainVm };
_mainWindow.Closed += MainWindowClosed;
}
_mainWindow.Show();
_mainWindow.WindowState = WindowState.Normal;
_mainWindow.Activate();
}
private void ExitApplication()
{
// Fenster schließen ohne Hide-Abfangen
if (_mainWindow != null)
{
_mainWindow.Closed -= MainWindowClosed;
_mainWindow.Close();
_mainWindow = null;
}
// Tray-Icon entfernen (wird in OnExit erneut geprüft)
if (_notifyIcon != null)
{
_notifyIcon.Visible = false;
_notifyIcon.Dispose();
_notifyIcon = null;
}
// Shutdown() loest OnExit aus, das den Service synchron disposed.
Shutdown();
}
private void MainWindowClosed(object? sender, EventArgs e)
{
// DashboardViewModel disposen: stoppt den DispatcherTimer (verhindert Timer-Leak)
if (_mainWindow?.DataContext is MainViewModel vm)
vm.Dashboard.Dispose();
_mainWindow = null;
}
protected override void OnExit(ExitEventArgs e)
{
if (_notifyIcon != null)
{
_notifyIcon.Visible = false;
_notifyIcon.Dispose();
_notifyIcon = null;
}
try
{
// Timeout von 3 Sek.: SelectEndpoint (OPC UA) ist ein synchron-blockierender
// Netzwerkaufruf ohne CancellationToken – GetAwaiter().GetResult() würde
// ewig warten und Environment.Exit(0) wird nie erreicht.
// Task.Wait(TimeSpan) gibt nach spätestens 3 Sek. auf.
var disposeTask = _printerService?.DisposeAsync().AsTask() ?? Task.CompletedTask;
if (!disposeTask.Wait(TimeSpan.FromSeconds(3)))
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Warnung: PrinterService-Dispose nach 3 Sek. abgebrochen");
_printerService = null;
}
catch (Exception ex)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Fehler beim Beenden des PrinterService: {ex.Message}");
}
base.OnExit(e);
// Alle verbleibenden Foreground-Threads (z. B. OPC UA intern, Sockets)
// hart beenden - verhindert Zombie-Prozesse im Task-Manager.
Environment.Exit(0);
}
}