From 3909764972588a6b448de46dcda1724f32be64ef Mon Sep 17 00:00:00 2001 From: home-PC Date: Wed, 27 May 2026 17:43:22 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E9=9A=90=E8=97=8F=E4=B8=8E=E6=B7=B1=E8=89=B2=E4=B8=BB=E9=A2=98?= =?UTF-8?q?=E7=95=8C=E9=9D=A2=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复托盘退出卡死、启动隐藏闪窗和隐藏实例再次启动无法唤醒的问题。 统一深色主题资源与控件模板,补齐卡片、内置图标、右键菜单和弹窗背景样式。 验证:dotnet build PersonalToolbox.sln;dotnet run --project tests\PersonalToolbox.Tests\PersonalToolbox.Tests.csproj。 --- src/PersonalToolbox/App.xaml | 329 +++++++++++++++++- src/PersonalToolbox/App.xaml.cs | 135 +++++++ src/PersonalToolbox/MainWindow.xaml | 126 ++++--- src/PersonalToolbox/MainWindow.xaml.cs | 94 +++-- .../Services/AppearanceService.cs | 10 +- src/PersonalToolbox/Services/IconService.cs | 80 ++--- .../ViewModels/ToolCardViewModel.cs | 12 +- .../Views/CategoryPickerWindow.xaml | 5 +- .../Views/CombinationEditorWindow.xaml | 16 +- .../Views/HotkeyCaptureWindow.xaml | 17 +- .../Views/IconPickerWindow.xaml | 18 +- src/PersonalToolbox/Views/PromptWindow.xaml | 5 +- src/PersonalToolbox/Views/SettingsWindow.xaml | 9 +- .../Views/ToolEditorWindow.xaml | 16 +- 14 files changed, 720 insertions(+), 152 deletions(-) diff --git a/src/PersonalToolbox/App.xaml b/src/PersonalToolbox/App.xaml index b2c7b87..bc0452e 100644 --- a/src/PersonalToolbox/App.xaml +++ b/src/PersonalToolbox/App.xaml @@ -1,14 +1,14 @@ - + xmlns:views="clr-namespace:PersonalToolbox.Views"> + @@ -16,23 +16,344 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/PersonalToolbox/App.xaml.cs b/src/PersonalToolbox/App.xaml.cs index a85a807..b971cca 100644 --- a/src/PersonalToolbox/App.xaml.cs +++ b/src/PersonalToolbox/App.xaml.cs @@ -1,6 +1,10 @@ using System.Configuration; using System.Data; +using System.IO; +using System.IO.Pipes; +using System.Text; using System.Windows; +using System.Threading; namespace PersonalToolbox; @@ -9,5 +13,136 @@ namespace PersonalToolbox; /// 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) + { + } + } + } } diff --git a/src/PersonalToolbox/MainWindow.xaml b/src/PersonalToolbox/MainWindow.xaml index 184e0ab..3c062ad 100644 --- a/src/PersonalToolbox/MainWindow.xaml +++ b/src/PersonalToolbox/MainWindow.xaml @@ -1,4 +1,4 @@ - @@ -20,8 +19,8 @@ - @@ -78,8 +77,8 @@ - @@ -93,7 +92,7 @@ + FontFamily="Segoe MDL2 Assets" + FontSize="16" + Foreground="{DynamicResource PrimaryBrush}" /> @@ -179,12 +178,23 @@ + + + - - - + + + + + + +