using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Data; using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Navigation; using KronaStaffTerminal.Services; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. namespace KronaStaffTerminal; /// /// Provides application-specific behavior to supplement the default Application class. /// public partial class App : Application { private const string InstanceMutexName = @"Local\KronaStaffTerminal.Singleton"; private const string ActivationEventName = @"Local\KronaStaffTerminal.Activate"; private TrayService? _trayService; private Microsoft.UI.Dispatching.DispatcherQueueTimer? _autostartTimer; private Mutex? _instanceMutex; private EventWaitHandle? _activationEvent; private CancellationTokenSource? _activationCancellation; /// /// The main application window. Use App.Window from any class that needs /// the window reference (for dialogs, pickers, interop, etc.). /// public static Window Window { get; private set; } = null!; /// /// The UI thread dispatcher. Use App.DispatcherQueue to marshal calls /// to the UI thread. Fully qualified to avoid CS0104 ambiguity with /// . /// public static Microsoft.UI.Dispatching.DispatcherQueue DispatcherQueue { get; private set; } = null!; /// /// The native window handle (HWND). Use for file pickers, /// DataTransferManager, and any WinRT interop that requires /// InitializeWithWindow. /// public static nint WindowHandle => WinRT.Interop.WindowNative.GetWindowHandle(Window); /// /// Initializes the singleton application object. /// public App() { InitializeComponent(); UnhandledException += (_, eventArgs) => CrashLogService.Write(eventArgs.Exception); AppDomain.CurrentDomain.UnhandledException += (_, eventArgs) => { if (eventArgs.ExceptionObject is Exception exception) CrashLogService.Write(exception); }; TaskScheduler.UnobservedTaskException += (_, eventArgs) => { CrashLogService.Write(eventArgs.Exception.Flatten()); eventArgs.SetObserved(); }; } /// /// Invoked when the application is launched. /// /// Details about the launch request and process. protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) { _instanceMutex = new Mutex(true, InstanceMutexName, out var isFirstInstance); if (!isFirstInstance) { try { EventWaitHandle.OpenExisting(ActivationEventName).Set(); } catch (WaitHandleCannotBeOpenedException) { // The first instance is still starting; exiting avoids competing for the scanner. } Environment.Exit(0); return; } Window = new MainWindow(); DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread(); _trayService = new TrayService(ShowWindow, RequestExit); _activationEvent = new EventWaitHandle(false, EventResetMode.AutoReset, ActivationEventName); _activationCancellation = new CancellationTokenSource(); _ = Task.Run(() => ListenForActivation(_activationCancellation.Token)); Window.Activate(); if (Environment.GetCommandLineArgs().Any(argument => argument.Equals("--autostart", StringComparison.OrdinalIgnoreCase))) { _autostartTimer = DispatcherQueue.CreateTimer(); _autostartTimer.Interval = TimeSpan.FromSeconds(1); _autostartTimer.IsRepeating = false; _autostartTimer.Tick += (_, _) => { if (Window is MainWindow mainWindow) { mainWindow.HideToTray(); } _autostartTimer = null; }; _autostartTimer.Start(); } } private void ListenForActivation(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { if (_activationEvent?.WaitOne(TimeSpan.FromSeconds(1)) == true) { DispatcherQueue.TryEnqueue(ShowWindow); } } } public void NotifyMinimizedToTray() => _trayService?.ShowRunningBalloon(); private static void ShowWindow() { if (Window is MainWindow mainWindow) { mainWindow.ShowWindow(); } } private static void RequestExit() { if (Window is MainWindow mainWindow) { mainWindow.ShowWindow(); _ = mainWindow.RequestExitAsync(); } } public void CompleteExit() { _activationCancellation?.Cancel(); _activationCancellation?.Dispose(); _activationCancellation = null; _activationEvent?.Dispose(); _activationEvent = null; _instanceMutex?.ReleaseMutex(); _instanceMutex?.Dispose(); _instanceMutex = null; _trayService?.Dispose(); _trayService = null; if (Window is MainWindow mainWindow) { mainWindow.ExitApplication(); } } }