|
@@ -0,0 +1,150 @@
|
|
|
|
|
+using System;
|
|
|
|
|
+using System.IO;
|
|
|
|
|
+using System.IO.Pipes;
|
|
|
|
|
+using System.Threading;
|
|
|
|
|
+using System.Threading.Tasks;
|
|
|
|
|
+using Avalonia;
|
|
|
|
|
+using Avalonia.Controls;
|
|
|
|
|
+using Avalonia.Controls.ApplicationLifetimes;
|
|
|
|
|
+using Avalonia.Threading;
|
|
|
|
|
+using Serilog;
|
|
|
|
|
+
|
|
|
|
|
+namespace YZWater.Avalonia;
|
|
|
|
|
+
|
|
|
|
|
+/// <summary>
|
|
|
|
|
+/// 单实例管理:确保程序只运行一个实例,新启动时激活已有窗口
|
|
|
|
|
+/// </summary>
|
|
|
|
|
+internal static class SingleInstanceHelper
|
|
|
|
|
+{
|
|
|
|
|
+ private const string MutexName = "Global\\YZWater3_SingleInstance";
|
|
|
|
|
+ private const string PipeName = "YZWater3_SingleInstance_Pipe";
|
|
|
|
|
+
|
|
|
|
|
+ private static Mutex? _mutex;
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 尝试获取单实例权限
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <returns>true 表示这是第一个实例;false 表示已有实例运行</returns>
|
|
|
|
|
+ public static bool TryAcquire()
|
|
|
|
|
+ {
|
|
|
|
|
+ _mutex = new Mutex(true, MutexName, out bool createdNew);
|
|
|
|
|
+ return createdNew;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 通知已有实例激活窗口
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public static void ActivateExistingInstance()
|
|
|
|
|
+ {
|
|
|
|
|
+ try
|
|
|
|
|
+ {
|
|
|
|
|
+ using var client = new NamedPipeClientStream(".", PipeName, PipeDirection.Out);
|
|
|
|
|
+ client.Connect(1000); // 1秒超时
|
|
|
|
|
+ using var writer = new StreamWriter(client);
|
|
|
|
|
+ writer.WriteLine("ACTIVATE");
|
|
|
|
|
+ writer.Flush();
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (Exception ex)
|
|
|
|
|
+ {
|
|
|
|
|
+ Log.Warning(ex, "通知已有实例失败");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 启动监听,等待新实例的激活请求
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public static void StartListening()
|
|
|
|
|
+ {
|
|
|
|
|
+ Task.Run(() =>
|
|
|
|
|
+ {
|
|
|
|
|
+ while (true)
|
|
|
|
|
+ {
|
|
|
|
|
+ try
|
|
|
|
|
+ {
|
|
|
|
|
+ using var server = new NamedPipeServerStream(
|
|
|
|
|
+ PipeName, PipeDirection.In, 1,
|
|
|
|
|
+ PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
|
|
|
|
|
+
|
|
|
|
|
+ // 等待连接(阻塞)
|
|
|
|
|
+ var ar = server.BeginWaitForConnection(null, null);
|
|
|
|
|
+ server.EndWaitForConnection(ar);
|
|
|
|
|
+
|
|
|
|
|
+ if (!server.IsConnected) continue;
|
|
|
|
|
+
|
|
|
|
|
+ // 读取命令
|
|
|
|
|
+ using var reader = new StreamReader(server);
|
|
|
|
|
+ var command = reader.ReadLine();
|
|
|
|
|
+
|
|
|
|
|
+ if (command == "ACTIVATE")
|
|
|
|
|
+ {
|
|
|
|
|
+ // 在 UI 线程激活主窗口
|
|
|
|
|
+ Dispatcher.UIThread.Post(() =>
|
|
|
|
|
+ {
|
|
|
|
|
+ ActivateMainWindow();
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 断开后继续等待下一次
|
|
|
|
|
+ server.Disconnect();
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (Exception ex)
|
|
|
|
|
+ {
|
|
|
|
|
+ Log.Warning(ex, "单实例监听异常");
|
|
|
|
|
+ Thread.Sleep(500); // 避免异常风暴
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 激活主窗口(带到前台并聚焦)
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private static void ActivateMainWindow()
|
|
|
|
|
+ {
|
|
|
|
|
+ try
|
|
|
|
|
+ {
|
|
|
|
|
+ if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
|
|
|
|
+ {
|
|
|
|
|
+ var mainWindow = desktop.MainWindow;
|
|
|
|
|
+ if (mainWindow == null) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 如果窗口最小化,恢复正常状态
|
|
|
|
|
+ if (mainWindow.WindowState == WindowState.Minimized)
|
|
|
|
|
+ {
|
|
|
|
|
+ mainWindow.WindowState = WindowState.Normal;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 激活窗口(带到前台)
|
|
|
|
|
+ mainWindow.Activate();
|
|
|
|
|
+ mainWindow.Focus();
|
|
|
|
|
+
|
|
|
|
|
+ // 尝试置顶再取消(某些系统需要这个技巧才能真正到前台)
|
|
|
|
|
+ var oldTopmost = mainWindow.Topmost;
|
|
|
|
|
+ mainWindow.Topmost = true;
|
|
|
|
|
+ Task.Delay(100).ContinueWith(_ =>
|
|
|
|
|
+ {
|
|
|
|
|
+ Dispatcher.UIThread.Post(() =>
|
|
|
|
|
+ {
|
|
|
|
|
+ mainWindow.Topmost = oldTopmost;
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ Log.Information("已激活主窗口");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (Exception ex)
|
|
|
|
|
+ {
|
|
|
|
|
+ Log.Warning(ex, "激活主窗口失败");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 释放单实例资源
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public static void Release()
|
|
|
|
|
+ {
|
|
|
|
|
+ _mutex?.ReleaseMutex();
|
|
|
|
|
+ _mutex?.Dispose();
|
|
|
|
|
+ _mutex = null;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|