198 Zeilen
6.2 KiB
C#
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);
|
|
}
|
|
}
|