fix: 修复启动隐藏与深色主题界面问题
修复托盘退出卡死、启动隐藏闪窗和隐藏实例再次启动无法唤醒的问题。 统一深色主题资源与控件模板,补齐卡片、内置图标、右键菜单和弹窗背景样式。 验证:dotnet build PersonalToolbox.sln;dotnet run --project tests\PersonalToolbox.Tests\PersonalToolbox.Tests.csproj。
This commit is contained in:
@@ -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;
|
||||
/// </summary>
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user