using System.Configuration; using System.Data; using System.IO; using System.IO.Pipes; using System.Text; using System.Windows; using System.Threading; namespace PersonalToolbox; /// /// Interaction logic for App.xaml /// public partial class App : System.Windows.Application { private const string ActivatePipeName = "PersonalToolbox.Activate"; private FileStream? _instanceLockFile; private CancellationTokenSource? _activationPipeCancellation; private MainWindow? _mainWindow; protected override async void OnStartup(StartupEventArgs e) { base.OnStartup(e); ShutdownMode = ShutdownMode.OnExplicitShutdown; if (!TryAcquireInstanceLock()) { SignalExistingInstance(); Environment.Exit(0); return; } var mainWindow = new MainWindow(); _mainWindow = mainWindow; MainWindow = mainWindow; StartActivationPipe(); await mainWindow.InitializeShellAsync(); if (!mainWindow.StartHiddenToTray) { mainWindow.ShowMainWindow(); } else { mainWindow.RefreshTrayMenu(); } await mainWindow.RunStartupTasksAsync(); } protected override void OnExit(ExitEventArgs e) { _activationPipeCancellation?.Cancel(); _activationPipeCancellation?.Dispose(); _instanceLockFile?.Dispose(); base.OnExit(e); } private bool TryAcquireInstanceLock() { try { var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); var configDirectory = Path.Combine(appData, "PersonalToolbox"); Directory.CreateDirectory(configDirectory); var lockPath = Path.Combine(configDirectory, "PersonalToolbox.lock"); _instanceLockFile = new FileStream(lockPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); _instanceLockFile.SetLength(0); var processId = Encoding.UTF8.GetBytes(Environment.ProcessId.ToString()); _instanceLockFile.Write(processId, 0, processId.Length); _instanceLockFile.Flush(); return true; } catch (IOException) { return false; } catch (UnauthorizedAccessException) { return false; } } private static void SignalExistingInstance() { for (var attempt = 0; attempt < 3; attempt++) { try { using var pipe = new NamedPipeClientStream(".", ActivatePipeName, PipeDirection.Out); pipe.Connect(800); using var writer = new StreamWriter(pipe, Encoding.UTF8) { AutoFlush = true }; writer.WriteLine("show"); return; } catch (IOException) { } catch (TimeoutException) { } Thread.Sleep(150); } } private void StartActivationPipe() { _activationPipeCancellation = new CancellationTokenSource(); _ = Task.Run(() => ListenForActivationAsync(_activationPipeCancellation.Token)); } private async Task ListenForActivationAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { await using var pipe = new NamedPipeServerStream( ActivatePipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); await pipe.WaitForConnectionAsync(cancellationToken); using var reader = new StreamReader(pipe, Encoding.UTF8); _ = await reader.ReadLineAsync(cancellationToken); await Dispatcher.BeginInvoke(() => _mainWindow?.ShowMainWindow()); } catch (OperationCanceledException) { return; } catch (IOException) { } } } }