improve:优化进程崩溃检测机制

This commit is contained in:
CJK_mkp
2025-06-12 10:34:29 +08:00
parent 0e5b31a8e4
commit c3159c61ee
3 changed files with 118 additions and 3 deletions
+109
View File
@@ -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 { }
}
}
}
@@ -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) {
+5 -1
View File
@@ -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) {