diff --git a/Ink Canvas/App.xaml.cs b/Ink Canvas/App.xaml.cs index ee4cde10..96ebe191 100644 --- a/Ink Canvas/App.xaml.cs +++ b/Ink Canvas/App.xaml.cs @@ -2,6 +2,9 @@ using Ink_Canvas.Helpers; using iNKORE.UI.WPF.Modern.Controls; using System; +using System.Diagnostics; +using System.Threading; +using System.IO; using System.Linq; using System.Reflection; using System.Windows; @@ -21,10 +24,20 @@ namespace Ink_Canvas public static string[] StartArgs = null; public static string RootPath = Environment.GetEnvironmentVariable("APPDATA") + "\\Ink Canvas\\"; + // 新增:保存看门狗进程对象 + private static Process watchdogProcess = null; + // 新增:标记是否为软件内主动退出 + public static bool IsAppExitByUser = false; + // 新增:退出信号文件路径 + private static string watchdogExitSignalFile = Path.Combine(Path.GetTempPath(), "icc_watchdog_exit_" + System.Diagnostics.Process.GetCurrentProcess().Id + ".flag"); + public App() { this.Startup += new StartupEventHandler(App_Startup); this.DispatcherUnhandledException += App_DispatcherUnhandledException; + StartHeartbeatMonitor(); + StartWatchdogIfNeeded(); + this.Exit += App_Exit; // 注册退出事件 } // 增加字段保存崩溃后操作设置 @@ -55,6 +68,7 @@ namespace Ink_Canvas void App_Startup(object sender, StartupEventArgs e) { + RunWatchdogIfNeeded(); /*if (!StoreHelper.IsStoreApp) */RootPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase; LogHelper.NewLog(string.Format("Ink Canvas Starting (Version: {0})", Assembly.GetExecutingAssembly().GetName().Version.ToString())); @@ -99,5 +113,100 @@ namespace Ink_Canvas SilentRestart, NoAction } + + // 心跳相关 + private static Timer heartbeatTimer; + private static DateTime lastHeartbeat = DateTime.Now; + private static Timer watchdogTimer; + + private void StartHeartbeatMonitor() + { + // 主线程定时更新心跳 + heartbeatTimer = new Timer(_ => lastHeartbeat = DateTime.Now, null, 0, 1000); + // 辅助线程检测心跳超时 + watchdogTimer = new Timer(_ => + { + if ((DateTime.Now - lastHeartbeat).TotalSeconds > 10) + { + LogHelper.NewLog("检测到主线程无响应,自动重启。"); + if (CrashAction == CrashActionType.SilentRestart) + { + try + { + string exePath = Process.GetCurrentProcess().MainModule.FileName; + Process.Start(exePath); + } + catch { } + Environment.Exit(1); + } + } + }, null, 0, 3000); + } + + // 看门狗进程 + private void StartWatchdogIfNeeded() + { + // 避免递归启动 + if (Environment.GetCommandLineArgs().Contains("--watchdog")) return; + // 启动看门狗进程 + string exePath = Process.GetCurrentProcess().MainModule.FileName; + var psi = new ProcessStartInfo + { + FileName = exePath, + Arguments = "--watchdog " + Process.GetCurrentProcess().Id + " \"" + watchdogExitSignalFile + "\"", + CreateNoWindow = true, + UseShellExecute = false, + WindowStyle = ProcessWindowStyle.Hidden + }; + watchdogProcess = Process.Start(psi); + } + + // 看门狗主逻辑(在 Main 函数或 App_Startup 入口前加判断) + public static void RunWatchdogIfNeeded() + { + var args = Environment.GetCommandLineArgs(); + if (args.Length >= 4 && args[1] == "--watchdog") + { + int pid = int.Parse(args[2]); + string exitSignalFile = args[3]; + try + { + var proc = Process.GetProcessById(pid); + while (!proc.HasExited) + { + // 检查退出信号文件 + if (File.Exists(exitSignalFile)) + { + try { File.Delete(exitSignalFile); } catch { } + Environment.Exit(0); + } + Thread.Sleep(2000); + } + // 主进程异常退出,自动重启 + string exePath = Process.GetCurrentProcess().MainModule.FileName; + Process.Start(exePath); + } + catch { } + Environment.Exit(0); + } + } + + private void App_Exit(object sender, ExitEventArgs e) + { + // 仅在软件内主动退出时关闭看门狗,并写入退出信号 + try + { + if (IsAppExitByUser) + { + // 写入退出信号文件,通知看门狗正常退出 + File.WriteAllText(watchdogExitSignalFile, "exit"); + if (watchdogProcess != null && !watchdogProcess.HasExited) + { + watchdogProcess.Kill(); + } + } + } + catch { } + } } } diff --git a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs index 61d5c7e4..b0db516d 100644 --- a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs +++ b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs @@ -1577,8 +1577,10 @@ namespace Ink_Canvas { public static bool CloseIsFromButton = false; public void BtnExit_Click(object sender, RoutedEventArgs e) { - CloseIsFromButton = true; - Close(); + App.IsAppExitByUser = true; + Application.Current.Shutdown(); + // CloseIsFromButton = true; + // Close(); } public void BtnRestart_Click(object sender, RoutedEventArgs e) { diff --git a/Ink Canvas/MainWindow_cs/MW_TrayIcon.cs b/Ink Canvas/MainWindow_cs/MW_TrayIcon.cs index e4c943a0..a9f2a84a 100644 --- a/Ink Canvas/MainWindow_cs/MW_TrayIcon.cs +++ b/Ink Canvas/MainWindow_cs/MW_TrayIcon.cs @@ -54,7 +54,11 @@ namespace Ink_Canvas private void CloseAppTrayIconMenuItem_Clicked(object sender, RoutedEventArgs e) { var mainWin = (MainWindow)Application.Current.MainWindow; - if (mainWin.IsLoaded) mainWin.BtnExit_Click(null,null); + if (mainWin.IsLoaded) { + App.IsAppExitByUser = true; + Application.Current.Shutdown(); + // mainWin.BtnExit_Click(null,null); + } } private void RestartAppTrayIconMenuItem_Clicked(object sender, RoutedEventArgs e) {