2025-08-31 11:43:52 +08:00
using Hardcodet.Wpf.TaskbarNotification ;
using Ink_Canvas.Helpers ;
2025-10-01 18:17:39 +08:00
using Ink_Canvas.Windows ;
2025-08-31 11:43:52 +08:00
using iNKORE.UI.WPF.Modern.Controls ;
using Microsoft.Win32 ;
using Newtonsoft.Json ;
2025-05-25 09:29:48 +08:00
using System ;
2025-07-28 14:40:44 +08:00
using System.Collections.Generic ;
2025-06-12 10:34:29 +08:00
using System.Diagnostics ;
using System.IO ;
2025-05-25 09:29:48 +08:00
using System.Linq ;
2025-07-28 14:40:44 +08:00
using System.Net ;
2025-05-25 09:29:48 +08:00
using System.Reflection ;
2025-07-28 14:40:44 +08:00
using System.Runtime.InteropServices ;
using System.Security ;
using System.Text ;
using System.Threading ;
2025-10-01 18:17:39 +08:00
using System.Threading.Tasks ;
2025-05-25 09:29:48 +08:00
using System.Windows ;
2025-07-28 14:40:44 +08:00
using System.Windows.Forms ;
using System.Windows.Input ;
using System.Windows.Threading ;
using Application = System . Windows . Application ;
2025-09-13 21:41:34 +08:00
using MessageBox = System . Windows . MessageBox ;
2025-10-01 18:17:39 +08:00
using SplashScreen = Ink_Canvas . Windows . SplashScreen ;
2025-07-28 14:40:44 +08:00
using Timer = System . Threading . Timer ;
2025-05-25 09:29:48 +08:00
namespace Ink_Canvas
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
2025-07-28 14:40:44 +08:00
Mutex mutex ;
2025-05-25 09:29:48 +08:00
2025-07-28 14:40:44 +08:00
public static string [ ] StartArgs ;
2025-05-25 09:29:48 +08:00
public static string RootPath = Environment . GetEnvironmentVariable ( "APPDATA" ) + "\\Ink Canvas\\" ;
2025-09-13 14:59:47 +08:00
// 新增:标记是否通过--board参数启动
2025-10-01 00:53:22 +08:00
public static bool StartWithBoardMode = false ;
2025-06-12 10:34:29 +08:00
// 新增:保存看门狗进程对象
2025-07-28 14:40:44 +08:00
private static Process watchdogProcess ;
2025-06-12 10:34:29 +08:00
// 新增:标记是否为软件内主动退出
2025-07-28 14:40:44 +08:00
public static bool IsAppExitByUser ;
2025-06-12 10:34:29 +08:00
// 新增:退出信号文件路径
2025-07-28 14:40:44 +08:00
private static string watchdogExitSignalFile = Path . Combine ( Path . GetTempPath ( ) , "icc_watchdog_exit_" + Process . GetCurrentProcess ( ) . Id + ".flag" ) ;
2025-07-15 16:06:29 +08:00
// 新增:崩溃日志文件路径
private static string crashLogFile = Path . Combine ( Environment . GetEnvironmentVariable ( "APPDATA" ) , "Ink Canvas" , "crash_logs" ) ;
// 新增:进程ID
private static int currentProcessId = Process . GetCurrentProcess ( ) . Id ;
// 新增:应用启动时间
2025-08-18 18:01:23 +08:00
internal static DateTime appStartTime { get ; private set ; }
2025-07-15 16:06:29 +08:00
// 新增:最后一次错误信息
private static string lastErrorMessage = string . Empty ;
// 新增:是否已初始化崩溃监听器
2025-07-28 14:40:44 +08:00
private static bool crashListenersInitialized ;
2025-10-01 18:17:39 +08:00
// 新增:启动画面相关
private static SplashScreen _splashScreen ;
private static bool _isSplashScreenShown = false ;
2025-06-12 10:34:29 +08:00
2025-05-25 09:29:48 +08:00
public App ( )
{
2025-07-19 16:51:03 +08:00
// 配置TLS协议以支持Windows 7
ConfigureTlsForWindows7 ( ) ;
2025-07-06 15:39:37 +08:00
// 如果是看门狗子进程,直接进入看门狗主循环并终止主流程
var args = Environment . GetCommandLineArgs ( ) ;
if ( args . Length > = 2 & & args [ 1 ] = = "--watchdog" )
{
RunWatchdogIfNeeded ( ) ;
Environment . Exit ( 0 ) ;
return ;
}
// 启动时优先同步设置,确保CrashAction为最新
SyncCrashActionFromSettings ( ) ;
2025-07-28 14:40:44 +08:00
Startup + = App_Startup ;
DispatcherUnhandledException + = App_DispatcherUnhandledException ;
2025-06-12 10:34:29 +08:00
StartHeartbeatMonitor ( ) ;
2025-07-06 15:39:37 +08:00
2025-10-01 18:45:26 +08:00
// 初始化全局异常和进程结束处理
2025-07-15 16:06:29 +08:00
InitializeCrashListeners ( ) ;
2025-07-06 15:39:37 +08:00
// 仅在崩溃后操作为静默重启时才启动看门狗
2025-08-30 15:49:37 +08:00
// 在更新模式下不启动看门狗,避免干扰更新流程
args = Environment . GetCommandLineArgs ( ) ;
bool isUpdateMode = args . Contains ( "--update-mode" ) ;
bool isFinalApp = args . Contains ( "--final-app" ) ;
2025-08-31 11:43:52 +08:00
2025-08-30 15:49:37 +08:00
if ( CrashAction = = CrashActionType . SilentRestart & & ! isUpdateMode & & ! isFinalApp )
2025-07-06 15:39:37 +08:00
{
StartWatchdogIfNeeded ( ) ;
}
2025-07-28 14:40:44 +08:00
Exit + = App_Exit ; // 注册退出事件
2025-05-25 09:29:48 +08:00
}
2025-10-01 18:45:26 +08:00
// 配置TLS协议以支持Windows 7
2025-07-19 16:51:03 +08:00
private void ConfigureTlsForWindows7 ( )
{
try
{
// 检测操作系统版本
var osVersion = Environment . OSVersion ;
bool isWindows7 = osVersion . Version . Major = = 6 & & osVersion . Version . Minor = = 1 ;
2025-08-03 16:46:33 +08:00
2025-07-19 16:51:03 +08:00
if ( isWindows7 )
{
2025-07-28 14:40:44 +08:00
LogHelper . WriteLogToFile ( "检测到Windows 7系统,配置TLS协议支持" ) ;
2025-08-03 16:46:33 +08:00
2025-07-19 16:51:03 +08:00
// 启用所有TLS版本以支持Windows 7
ServicePointManager . SecurityProtocol = SecurityProtocolType . Tls12 | SecurityProtocolType . Tls11 | SecurityProtocolType . Tls ;
2025-08-03 16:46:33 +08:00
2025-07-19 16:51:03 +08:00
// 配置ServicePointManager以支持Windows 7
ServicePointManager . DefaultConnectionLimit = 10 ;
ServicePointManager . Expect100Continue = false ;
ServicePointManager . UseNagleAlgorithm = false ;
2025-08-03 16:46:33 +08:00
2025-07-28 14:40:44 +08:00
LogHelper . WriteLogToFile ( "TLS协议配置完成,已启用TLS 1.2/1.1/1.0支持" ) ;
2025-07-19 16:51:03 +08:00
}
else
{
// 对于更新的Windows版本,不进行任何TLS配置,使用系统默认设置
2025-07-28 14:40:44 +08:00
LogHelper . WriteLogToFile ( $"检测到Windows版本: {osVersion.VersionString},使用系统默认TLS配置" ) ;
2025-07-19 16:51:03 +08:00
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"配置TLS协议时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-10-01 18:45:26 +08:00
// 初始化崩溃监听器
2025-07-15 16:06:29 +08:00
private void InitializeCrashListeners ( )
{
if ( crashListenersInitialized ) return ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
try
{
// 确保崩溃日志目录存在
if ( ! Directory . Exists ( crashLogFile ) )
{
Directory . CreateDirectory ( crashLogFile ) ;
}
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 注册非UI线程未处理异常处理程序
AppDomain . CurrentDomain . UnhandledException + = CurrentDomain_UnhandledException ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 注册控制台Ctrl+C等终止信号处理
Console . CancelKeyPress + = Console_CancelKeyPress ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 注册系统会话结束事件(关机、注销等)
SystemEvents . SessionEnding + = SystemEvents_SessionEnding ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 注册进程退出处理程序
AppDomain . CurrentDomain . ProcessExit + = CurrentDomain_ProcessExit ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 尝试注册Windows关闭消息监听
SetConsoleCtrlHandler ( ConsoleCtrlHandler , true ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 如果系统支持,添加Windows Management Instrumentation监听器
try
{
// 使用反射动态加载和调用WMI
TrySetupWmiMonitoring ( ) ;
}
catch ( Exception wmiEx )
{
LogHelper . WriteLogToFile ( $"设置WMI进程监控失败: {wmiEx.Message}" , LogHelper . LogType . Warning ) ;
}
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
crashListenersInitialized = true ;
2025-07-28 14:40:44 +08:00
LogHelper . WriteLogToFile ( "已初始化崩溃监听器" ) ;
2025-07-15 16:06:29 +08:00
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"初始化崩溃监听器失败: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-08-03 16:46:33 +08:00
2025-10-01 18:45:26 +08:00
// 动态加载WMI监控
2025-07-15 16:06:29 +08:00
private void TrySetupWmiMonitoring ( )
{
try
{
// 检查System.Management程序集是否可用
var assemblyName = "System.Management, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" ;
var assembly = Assembly . Load ( assemblyName ) ;
if ( assembly = = null )
{
LogHelper . WriteLogToFile ( "未找到System.Management程序集,跳过WMI监控" , LogHelper . LogType . Warning ) ;
return ;
}
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 使用反射创建WMI查询
var watcherType = assembly . GetType ( "System.Management.ManagementEventWatcher" ) ;
if ( watcherType = = null )
{
LogHelper . WriteLogToFile ( "未找到ManagementEventWatcher类型,跳过WMI监控" , LogHelper . LogType . Warning ) ;
return ;
}
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 构建WMI查询字符串
string queryString = $"SELECT * FROM __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.ProcessId = {currentProcessId}" ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 创建ManagementEventWatcher实例
object watcher = Activator . CreateInstance ( watcherType , queryString ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 获取EventArrived事件信息
var eventInfo = watcherType . GetEvent ( "EventArrived" ) ;
if ( eventInfo = = null )
{
LogHelper . WriteLogToFile ( "未找到EventArrived事件,跳过WMI监控" , LogHelper . LogType . Warning ) ;
return ;
}
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 创建委托并订阅事件
Type delegateType = eventInfo . EventHandlerType ;
var handler = Delegate . CreateDelegate ( delegateType , this , GetType ( ) . GetMethod ( "WmiEventHandler" , BindingFlags . NonPublic | BindingFlags . Instance ) ) ;
eventInfo . AddEventHandler ( watcher , handler ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 启动监听
var startMethod = watcherType . GetMethod ( "Start" ) ;
startMethod . Invoke ( watcher , null ) ;
2025-08-03 16:46:33 +08:00
2025-07-28 14:40:44 +08:00
LogHelper . WriteLogToFile ( "已成功启动WMI进程监控" ) ;
2025-07-15 16:06:29 +08:00
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"动态加载WMI监控失败: {ex.Message}" , LogHelper . LogType . Warning ) ;
}
}
2025-08-03 16:46:33 +08:00
2025-10-01 18:45:26 +08:00
// WMI事件处理方法
2025-07-15 16:06:29 +08:00
private void WmiEventHandler ( object sender , EventArgs e )
{
try
{
// 尝试从事件参数中提取信息
dynamic eventArgs = e ;
dynamic newEvent = eventArgs . NewEvent ;
if ( newEvent ! = null )
{
dynamic targetInstance = newEvent [ "TargetInstance" ] ;
if ( targetInstance ! = null )
{
string processName = targetInstance [ "Name" ] ? . ToString ( ) ? ? "未知进程" ;
WriteCrashLog ( $"WMI检测到进程{processName}(ID:{currentProcessId})已终止" ) ;
}
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"处理WMI事件时出错: {ex.Message}" , LogHelper . LogType . Warning ) ;
}
}
2025-10-01 18:45:26 +08:00
// Windows控制台控制处理程序
2025-07-15 16:06:29 +08:00
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCtrlHandler ( ConsoleCtrlDelegate handler , bool add ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
private delegate bool ConsoleCtrlDelegate ( int ctrlType ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
private static bool ConsoleCtrlHandler ( int ctrlType )
{
string eventType = "未知控制类型" ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 使用传统switch语句替代switch表达式
switch ( ctrlType )
{
case 0 :
eventType = "CTRL_C_EVENT" ;
break ;
case 1 :
eventType = "CTRL_BREAK_EVENT" ;
break ;
case 2 :
eventType = "CTRL_CLOSE_EVENT" ;
break ;
case 5 :
eventType = "CTRL_LOGOFF_EVENT" ;
break ;
case 6 :
eventType = "CTRL_SHUTDOWN_EVENT" ;
break ;
default :
eventType = $"未知控制类型({ctrlType})" ;
break ;
}
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
WriteCrashLog ( $"接收到系统控制信号: {eventType}" ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 返回true表示已处理该事件
return false ;
}
2025-08-03 16:46:33 +08:00
2025-10-01 18:45:26 +08:00
// 系统会话结束事件处理
2025-07-15 16:06:29 +08:00
private void SystemEvents_SessionEnding ( object sender , SessionEndingEventArgs e )
{
string reason = e . Reason = = SessionEndReasons . Logoff ? "用户注销" : "系统关机" ;
WriteCrashLog ( $"系统会话即将结束: {reason}" ) ;
2025-09-13 10:48:33 +08:00
2025-10-02 11:50:00 +08:00
// 清理PowerPoint进程守护和悬浮窗拦截器
2025-09-13 10:48:33 +08:00
try
{
2025-10-02 11:50:00 +08:00
// 获取主窗口实例
2025-09-30 19:15:03 +08:00
var mainWindow = Current . MainWindow as MainWindow ;
if ( mainWindow ! = null )
2025-09-13 10:48:33 +08:00
{
2025-10-02 11:50:00 +08:00
// 清理PowerPoint进程守护
2025-09-13 10:48:33 +08:00
var method = mainWindow . GetType ( ) . GetMethod ( "StopPowerPointProcessMonitoring" ,
2025-09-13 21:38:03 +08:00
BindingFlags . NonPublic | BindingFlags . Instance ) ;
2025-09-13 10:48:33 +08:00
method ? . Invoke ( mainWindow , null ) ;
WriteCrashLog ( "PowerPoint进程守护已在系统关机时清理" ) ;
2025-10-02 11:50:00 +08:00
// 清理悬浮窗拦截器
var interceptorField = mainWindow . GetType ( ) . GetField ( "_floatingWindowInterceptorManager" ,
BindingFlags . NonPublic | BindingFlags . Instance ) ;
var interceptorManager = interceptorField ? . GetValue ( mainWindow ) ;
if ( interceptorManager ! = null )
{
var disposeMethod = interceptorManager . GetType ( ) . GetMethod ( "Dispose" ) ;
disposeMethod ? . Invoke ( interceptorManager , null ) ;
}
2025-09-13 10:48:33 +08:00
}
}
catch ( Exception ex )
{
2025-10-02 11:50:00 +08:00
WriteCrashLog ( $"清理资源失败: {ex.Message}" ) ;
2025-09-13 10:48:33 +08:00
}
2025-08-18 17:57:56 +08:00
DeviceIdentifier . SaveUsageStatsOnShutdown ( ) ;
2025-07-15 16:06:29 +08:00
}
2025-08-03 16:46:33 +08:00
2025-10-01 18:45:26 +08:00
// 控制台取消事件处理
2025-07-15 16:06:29 +08:00
private void Console_CancelKeyPress ( object sender , ConsoleCancelEventArgs e )
{
WriteCrashLog ( $"接收到控制台中断信号: {e.SpecialKey}" ) ;
e . Cancel = true ; // 取消默认处理
}
2025-08-03 16:46:33 +08:00
2025-10-01 18:45:26 +08:00
// 处理非UI线程的未处理异常
2025-07-15 16:06:29 +08:00
private void CurrentDomain_UnhandledException ( object sender , UnhandledExceptionEventArgs e )
{
try
{
var exception = e . ExceptionObject as Exception ;
string errorMessage = exception ? . ToString ( ) ? ? "未知异常" ;
lastErrorMessage = errorMessage ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
WriteCrashLog ( $"捕获到未处理的异常: {errorMessage}" ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
if ( e . IsTerminating )
{
WriteCrashLog ( "应用程序即将终止" ) ;
}
}
catch ( Exception ex )
{
// 尝试在最后时刻记录错误
try
{
File . AppendAllText (
Path . Combine ( crashLogFile , $"critical_error_{DateTime.Now:yyyyMMdd_HHmmss}.log" ) ,
$"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 记录未处理异常时发生错误: {ex.Message}\r\n"
) ;
}
catch { }
}
}
2025-08-03 16:46:33 +08:00
2025-10-01 18:45:26 +08:00
// 处理进程退出事件
2025-07-15 16:06:29 +08:00
private void CurrentDomain_ProcessExit ( object sender , EventArgs e )
{
TimeSpan runDuration = DateTime . Now - appStartTime ;
2025-08-24 01:46:48 +08:00
string durationText = FormatTimeSpan ( runDuration ) ;
WriteCrashLog ( $"应用程序退出,运行时长: {durationText}" ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 如果有最后错误消息,记录到日志
if ( ! string . IsNullOrEmpty ( lastErrorMessage ) )
{
WriteCrashLog ( $"最后错误信息: {lastErrorMessage}" ) ;
}
}
2025-08-03 16:46:33 +08:00
2025-10-01 18:45:26 +08:00
// 格式化时间跨度
2025-08-24 01:46:48 +08:00
private static string FormatTimeSpan ( TimeSpan timeSpan )
{
if ( timeSpan . TotalDays > = 1 )
{
return $"{timeSpan.Days}天 {timeSpan.Hours}小时 {timeSpan.Minutes}分钟" ;
}
2025-08-31 09:54:13 +08:00
if ( timeSpan . TotalHours > = 1 )
2025-08-24 01:46:48 +08:00
{
return $"{timeSpan.Hours}小时 {timeSpan.Minutes}分钟" ;
}
2025-08-31 09:54:13 +08:00
if ( timeSpan . TotalMinutes > = 1 )
2025-08-24 01:46:48 +08:00
{
return $"{timeSpan.Minutes}分钟 {timeSpan.Seconds}秒" ;
}
2025-08-31 09:54:13 +08:00
return $"{timeSpan.Seconds}秒" ;
2025-08-24 01:46:48 +08:00
}
2025-10-01 18:17:39 +08:00
public static void ShowSplashScreen ( )
{
if ( _isSplashScreenShown )
{
LogHelper . WriteLogToFile ( "启动画面已经显示,跳过重复显示" ) ;
return ;
}
try
{
LogHelper . WriteLogToFile ( "开始创建启动画面..." ) ;
_splashScreen = new SplashScreen ( ) ;
LogHelper . WriteLogToFile ( "启动画面对象创建成功,准备显示..." ) ;
_splashScreen . Show ( ) ;
_isSplashScreenShown = true ;
LogHelper . WriteLogToFile ( "启动画面已显示" ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"显示启动画面失败: {ex.Message}" , LogHelper . LogType . Error ) ;
LogHelper . WriteLogToFile ( $"异常堆栈: {ex.StackTrace}" , LogHelper . LogType . Error ) ;
}
}
2025-10-01 18:38:01 +08:00
// 关闭启动画面
2025-10-01 18:17:39 +08:00
public static void CloseSplashScreen ( )
{
if ( ! _isSplashScreenShown | | _splashScreen = = null ) return ;
try
{
_splashScreen . CloseSplashScreen ( ) ;
_isSplashScreenShown = false ;
LogHelper . WriteLogToFile ( "启动画面已关闭" ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"关闭启动画面失败: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-10-01 18:38:01 +08:00
// 设置启动画面进度
2025-10-01 18:17:39 +08:00
public static void SetSplashProgress ( int progress )
{
if ( _splashScreen ! = null )
{
_splashScreen . SetProgress ( progress ) ;
}
}
2025-10-01 18:38:01 +08:00
// 设置启动画面消息
2025-10-01 18:17:39 +08:00
public static void SetSplashMessage ( string message )
{
if ( _splashScreen ! = null )
{
_splashScreen . SetLoadingMessage ( message ) ;
}
}
2025-10-01 18:38:01 +08:00
private static bool ShouldShowSplashScreen ( )
{
try
{
// 检查设置文件中的启动动画开关
var settingsPath = Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "Configs" , "Settings.json" ) ;
if ( File . Exists ( settingsPath ) )
{
var json = File . ReadAllText ( settingsPath ) ;
dynamic obj = JsonConvert . DeserializeObject ( json ) ;
if ( obj ? [ "appearance" ] ? [ "enableSplashScreen" ] ! = null )
{
return ( bool ) obj [ "appearance" ] [ "enableSplashScreen" ] ;
}
}
// 如果设置文件不存在或没有该设置,返回默认值false
return false ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"检查启动动画设置失败: {ex.Message}" , LogHelper . LogType . Warning ) ;
return false ;
}
}
// 记录崩溃日志
2025-07-15 16:06:29 +08:00
private static void WriteCrashLog ( string message )
{
try
{
// 确保目录存在
if ( ! Directory . Exists ( crashLogFile ) )
{
Directory . CreateDirectory ( crashLogFile ) ;
}
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
string logFileName = Path . Combine ( crashLogFile , $"crash_{DateTime.Now:yyyyMMdd}.log" ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 收集系统状态信息
2025-07-28 14:40:44 +08:00
string memoryUsage = ( Process . GetCurrentProcess ( ) . WorkingSet64 / ( 1024 * 1024 ) ) + " MB" ;
2025-07-15 16:06:29 +08:00
string cpuTime = Process . GetCurrentProcess ( ) . TotalProcessorTime . ToString ( ) ;
2025-08-24 01:46:48 +08:00
string processUptime = FormatTimeSpan ( DateTime . Now - Process . GetCurrentProcess ( ) . StartTime ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
string statusInfo = $"[内存: {memoryUsage}, CPU时间: {cpuTime}, 运行时长: {processUptime}]" ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 写入日志
File . AppendAllText (
logFileName ,
$"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [PID:{currentProcessId}] {message}\r\n{statusInfo}\r\n\r\n"
) ;
2025-08-03 16:46:33 +08:00
2025-07-15 16:06:29 +08:00
// 同时记录到主日志
LogHelper . WriteLogToFile ( message , LogHelper . LogType . Error ) ;
}
catch { }
}
2025-06-12 10:13:47 +08:00
// 增加字段保存崩溃后操作设置
public static CrashActionType CrashAction = CrashActionType . SilentRestart ;
2025-07-06 15:39:37 +08:00
public static void SyncCrashActionFromSettings ( )
{
try
{
// 优先从 Settings.json 直接读取
2025-09-13 21:10:36 +08:00
var settingsPath = Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "Configs" , "Settings.json" ) ;
2025-07-06 15:39:37 +08:00
if ( File . Exists ( settingsPath ) )
{
var json = File . ReadAllText ( settingsPath ) ;
2025-07-28 14:40:44 +08:00
dynamic obj = JsonConvert . DeserializeObject ( json ) ;
2025-07-06 15:39:37 +08:00
int crashAction = 0 ;
try { crashAction = ( int ) ( obj [ "startup" ] [ "crashAction" ] ? ? 0 ) ; } catch { }
CrashAction = ( CrashActionType ) crashAction ;
}
2025-10-02 00:05:09 +08:00
// 从主窗口同步
2025-07-06 15:39:37 +08:00
else if ( Ink_Canvas . MainWindow . Settings ! = null & & Ink_Canvas . MainWindow . Settings . Startup ! = null )
{
CrashAction = ( CrashActionType ) Ink_Canvas . MainWindow . Settings . Startup . CrashAction ;
}
}
catch { }
}
2025-07-28 14:40:44 +08:00
private void App_DispatcherUnhandledException ( object sender , DispatcherUnhandledExceptionEventArgs e )
2025-05-25 09:29:48 +08:00
{
2025-07-28 14:40:44 +08:00
Ink_Canvas . MainWindow . ShowNewMessage ( "抱歉,出现未预期的异常,可能导致 InkCanvasForClass 运行不稳定。\n建议保存墨迹后重启应用。" ) ;
2025-05-25 09:29:48 +08:00
LogHelper . NewLog ( e . Exception . ToString ( ) ) ;
2025-08-03 16:46:33 +08:00
2025-10-02 00:05:09 +08:00
// 记录到崩溃日志
2025-07-15 16:06:29 +08:00
lastErrorMessage = e . Exception . ToString ( ) ;
WriteCrashLog ( $"UI线程未处理异常: {e.Exception}" ) ;
2025-08-03 16:46:33 +08:00
2025-05-25 09:29:48 +08:00
e . Handled = true ;
2025-06-12 10:13:47 +08:00
2025-10-02 00:05:09 +08:00
SyncCrashActionFromSettings ( ) ; // 崩溃时同步最新设置
2025-07-06 15:39:37 +08:00
2025-06-13 09:19:44 +08:00
if ( CrashAction = = CrashActionType . SilentRestart & & ! IsAppExitByUser )
2025-06-12 10:13:47 +08:00
{
2025-06-23 14:33:58 +08:00
StartupCount . Increment ( ) ;
if ( StartupCount . GetCount ( ) > = 5 )
{
MessageBox . Show ( "检测到程序已连续重启5次,已停止自动重启。请联系开发者或检查系统环境。" , "重启次数过多" , MessageBoxButton . OK , MessageBoxImage . Error ) ;
StartupCount . Reset ( ) ;
Environment . Exit ( 1 ) ;
}
2025-06-12 10:13:47 +08:00
try
{
2025-07-28 14:40:44 +08:00
string exePath = Process . GetCurrentProcess ( ) . MainModule . FileName ;
2025-07-29 02:33:19 +08:00
Process . Start ( exePath ) ;
2025-06-12 10:13:47 +08:00
}
catch { }
Environment . Exit ( 1 ) ;
}
// CrashActionType.NoAction 时不做处理
2025-05-25 09:29:48 +08:00
}
private TaskbarIcon _taskbar ;
2025-10-01 18:17:39 +08:00
async void App_Startup ( object sender , StartupEventArgs e )
2025-05-25 09:29:48 +08:00
{
2025-08-24 01:46:48 +08:00
// 初始化应用启动时间
appStartTime = DateTime . Now ;
2025-08-31 11:43:52 +08:00
2025-10-01 18:38:01 +08:00
// 根据设置决定是否显示启动画面
if ( ShouldShowSplashScreen ( ) )
{
ShowSplashScreen ( ) ;
SetSplashMessage ( "正在启动 Ink Canvas..." ) ;
2025-10-02 01:51:10 +08:00
SetSplashProgress ( 20 ) ;
await Task . Delay ( 500 ) ;
2025-10-01 18:17:39 +08:00
2025-10-01 18:38:01 +08:00
// 强制刷新UI,确保启动画面显示
Application . Current . Dispatcher . Invoke ( ( ) = > { } , DispatcherPriority . Render ) ;
}
2025-10-01 18:17:39 +08:00
2025-10-02 01:51:10 +08:00
System . Threading . Thread . Sleep ( 500 ) ;
2025-08-03 16:46:33 +08:00
RootPath = AppDomain . CurrentDomain . SetupInformation . ApplicationBase ;
2025-05-25 09:29:48 +08:00
2025-07-28 14:40:44 +08:00
LogHelper . NewLog ( string . Format ( "Ink Canvas Starting (Version: {0})" , Assembly . GetExecutingAssembly ( ) . GetName ( ) . Version ) ) ;
2025-08-31 11:43:52 +08:00
2025-08-30 15:49:37 +08:00
// 检查是否为最终应用启动(更新后的应用)
bool isFinalApp = e . Args . Contains ( "--final-app" ) ;
bool skipMutexCheck = e . Args . Contains ( "--skip-mutex-check" ) ;
2025-09-13 14:59:47 +08:00
// 检查是否通过--board参数启动
bool hasBoardArg = e . Args . Contains ( "--board" ) ;
if ( hasBoardArg )
{
StartWithBoardMode = true ;
LogHelper . WriteLogToFile ( "App | 检测到--board参数,将直接进入白板模式" ) ;
}
2025-08-31 11:43:52 +08:00
2025-08-30 15:49:37 +08:00
// 记录最终应用启动状态
if ( isFinalApp )
{
LogHelper . WriteLogToFile ( "App | 检测到最终应用启动(更新后的应用)" ) ;
}
2025-07-29 18:37:00 +08:00
// 在应用启动时自动释放IACore相关DLL
2025-10-01 18:38:01 +08:00
if ( _isSplashScreenShown )
{
SetSplashMessage ( "正在初始化组件..." ) ;
2025-10-02 01:51:10 +08:00
SetSplashProgress ( 40 ) ;
2025-10-01 18:38:01 +08:00
await Task . Delay ( 500 ) ;
}
2025-07-29 18:37:00 +08:00
try
{
2025-08-30 19:40:14 +08:00
IACoreDllExtractor . ExtractIACoreDlls ( ) ;
2025-07-29 18:37:00 +08:00
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"释放IACore DLL时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
2025-07-28 19:08:06 +08:00
2025-07-26 14:29:24 +08:00
// 记录应用启动(设备标识符)
2025-10-01 18:38:01 +08:00
if ( _isSplashScreenShown )
{
SetSplashMessage ( "正在加载配置..." ) ;
2025-10-02 01:51:10 +08:00
SetSplashProgress ( 60 ) ;
2025-10-01 18:38:01 +08:00
await Task . Delay ( 500 ) ;
}
2025-07-26 14:29:24 +08:00
DeviceIdentifier . RecordAppLaunch ( ) ;
LogHelper . WriteLogToFile ( $"App | 设备ID: {DeviceIdentifier.GetDeviceId()}" ) ;
LogHelper . WriteLogToFile ( $"App | 使用频率: {DeviceIdentifier.GetUsageFrequency()}" ) ;
LogHelper . WriteLogToFile ( $"App | 更新优先级: {DeviceIdentifier.GetUpdatePriority()}" ) ;
2025-08-30 14:11:39 +08:00
// 处理更新模式启动
bool isUpdateMode = AutoUpdateHelper . HandleUpdateModeStartup ( e . Args ) ;
2025-08-31 11:43:52 +08:00
2025-08-30 14:11:39 +08:00
// 如果是更新模式,不显示主窗口但保持应用运行
if ( isUpdateMode )
{
LogHelper . WriteLogToFile ( "App | 检测到更新模式,跳过主窗口显示,保持应用运行" ) ;
return ;
}
2025-05-25 09:29:48 +08:00
2025-08-30 14:11:39 +08:00
// 检查是否存在更新标记文件
2025-08-30 19:40:14 +08:00
string updateMarkerFile = Path . Combine ( RootPath , "update_in_progress.tmp" ) ;
2025-08-30 14:11:39 +08:00
bool isUpdateInProgress = false ;
2025-08-31 11:43:52 +08:00
2025-08-30 14:11:39 +08:00
// 检查是否以更新模式启动
isUpdateMode = e . Args . Contains ( "--update-mode" ) ;
2025-08-31 11:43:52 +08:00
2025-08-30 15:49:37 +08:00
// 如果是最终应用启动,立即清理更新标记文件
if ( isFinalApp )
{
try
{
if ( File . Exists ( updateMarkerFile ) )
{
File . Delete ( updateMarkerFile ) ;
LogHelper . WriteLogToFile ( "App | 最终应用启动,清理更新标记文件" ) ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"App | 清理更新标记文件失败: {ex.Message}" , LogHelper . LogType . Warning ) ;
}
}
2025-08-31 11:43:52 +08:00
2025-08-30 15:49:37 +08:00
// 如果不是最终应用启动,才检查更新标记文件
if ( ! isFinalApp & & File . Exists ( updateMarkerFile ) )
2025-05-25 09:29:48 +08:00
{
2025-08-03 16:46:33 +08:00
try
{
2025-08-30 14:11:39 +08:00
string updateProcessIdStr = File . ReadAllText ( updateMarkerFile ) . Trim ( ) ;
if ( int . TryParse ( updateProcessIdStr , out int updateProcessId ) )
2025-07-06 15:39:37 +08:00
{
2025-08-30 14:11:39 +08:00
LogHelper . WriteLogToFile ( $"App | 检测到更新标记文件,更新进程ID: {updateProcessId}" ) ;
2025-08-31 11:43:52 +08:00
2025-08-30 14:11:39 +08:00
// 检查更新进程是否还在运行
try
{
Process updateProcess = Process . GetProcessById ( updateProcessId ) ;
if ( ! updateProcess . HasExited )
{
LogHelper . WriteLogToFile ( "App | 更新进程仍在运行,等待更新完成" ) ;
isUpdateInProgress = true ;
2025-08-31 11:43:52 +08:00
2025-08-30 14:11:39 +08:00
// 等待更新进程完成
int waitCount = 0 ;
const int maxWaitCount = 10 ; // 减少等待时间到10秒
2025-08-31 11:43:52 +08:00
2025-08-30 14:11:39 +08:00
while ( waitCount < maxWaitCount & & ! updateProcess . HasExited )
{
Thread . Sleep ( 500 ) ; // 减少等待间隔到500ms
waitCount + + ;
LogHelper . WriteLogToFile ( $"App | 等待更新进程完成... ({waitCount}/{maxWaitCount})" ) ;
}
2025-08-31 11:43:52 +08:00
2025-08-30 14:11:39 +08:00
if ( updateProcess . HasExited )
{
LogHelper . WriteLogToFile ( "App | 更新进程已结束" ) ;
}
else
{
LogHelper . WriteLogToFile ( "App | 等待更新进程超时,强制清理" , LogHelper . LogType . Warning ) ;
// 超时后强制清理标记文件
try
{
if ( File . Exists ( updateMarkerFile ) )
{
File . Delete ( updateMarkerFile ) ;
LogHelper . WriteLogToFile ( "App | 强制清理更新标记文件" ) ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"App | 强制清理更新标记文件失败: {ex.Message}" , LogHelper . LogType . Warning ) ;
}
}
}
else
{
LogHelper . WriteLogToFile ( "App | 更新进程已结束" ) ;
}
}
catch ( ArgumentException )
{
LogHelper . WriteLogToFile ( "App | 更新进程已不存在" ) ;
}
2025-08-31 11:43:52 +08:00
2025-08-30 14:11:39 +08:00
// 无论更新进程是否还在运行,都清理标记文件
try
{
if ( File . Exists ( updateMarkerFile ) )
{
File . Delete ( updateMarkerFile ) ;
LogHelper . WriteLogToFile ( "App | 清理更新标记文件" ) ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"App | 清理更新标记文件失败: {ex.Message}" , LogHelper . LogType . Warning ) ;
}
2025-07-06 15:39:37 +08:00
}
2025-08-03 16:46:33 +08:00
}
2025-08-30 14:11:39 +08:00
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"App | 读取更新标记文件失败: {ex.Message}" , LogHelper . LogType . Warning ) ;
// 如果读取失败,也尝试删除标记文件
try
{
if ( File . Exists ( updateMarkerFile ) )
{
File . Delete ( updateMarkerFile ) ;
LogHelper . WriteLogToFile ( "App | 清理损坏的更新标记文件" ) ;
}
}
catch { }
}
}
2025-08-30 15:49:37 +08:00
// 如果是更新过程、更新模式、最终应用或跳过Mutex检查,跳过Mutex检查
if ( ! isUpdateInProgress & & ! isUpdateMode & & ! isFinalApp & & ! skipMutexCheck )
2025-08-30 14:11:39 +08:00
{
bool ret ;
mutex = new Mutex ( true , "InkCanvasForClass CE" , out ret ) ;
if ( ! ret & & ! e . Args . Contains ( "-m" ) ) //-m multiple
{
LogHelper . NewLog ( "Detected existing instance" ) ;
2025-09-07 13:30:46 +08:00
2025-08-31 11:49:00 +08:00
// 检查是否有.icstk文件参数
string icstkFile = FileAssociationManager . GetIcstkFileFromArgs ( e . Args ) ;
if ( ! string . IsNullOrEmpty ( icstkFile ) )
{
LogHelper . WriteLogToFile ( $"检测到已运行实例,尝试通过IPC发送文件: {icstkFile}" , LogHelper . LogType . Event ) ;
2025-09-07 13:30:46 +08:00
2025-08-31 11:49:00 +08:00
// 尝试通过IPC发送文件路径给已运行实例
if ( FileAssociationManager . TrySendFileToExistingInstance ( icstkFile ) )
{
LogHelper . WriteLogToFile ( "文件路径已通过IPC发送给已运行实例" , LogHelper . LogType . Event ) ;
}
else
{
LogHelper . WriteLogToFile ( "通过IPC发送文件路径失败" , LogHelper . LogType . Warning ) ;
}
}
2025-09-13 14:59:47 +08:00
// 检查是否有--board参数
else if ( hasBoardArg )
{
LogHelper . WriteLogToFile ( "检测到已运行实例且有--board参数,尝试通过IPC发送白板模式命令" , LogHelper . LogType . Event ) ;
// 尝试通过IPC发送白板模式命令给已运行实例
if ( FileAssociationManager . TrySendBoardModeCommandToExistingInstance ( ) )
{
LogHelper . WriteLogToFile ( "白板模式命令已通过IPC发送给已运行实例" , LogHelper . LogType . Event ) ;
}
else
{
LogHelper . WriteLogToFile ( "通过IPC发送白板模式命令失败" , LogHelper . LogType . Warning ) ;
}
}
2025-08-31 11:49:00 +08:00
else
{
LogHelper . WriteLogToFile ( "检测到已运行实例,但无文件参数" , LogHelper . LogType . Event ) ;
}
2025-09-07 13:30:46 +08:00
2025-08-30 14:11:39 +08:00
LogHelper . NewLog ( "Ink Canvas automatically closed" ) ;
IsAppExitByUser = true ; // 多开时标记为用户主动退出
// 写入退出信号,确保看门狗不会重启
try
{
StartupCount . Reset ( ) ;
File . WriteAllText ( watchdogExitSignalFile , "exit" ) ;
if ( watchdogProcess ! = null & & ! watchdogProcess . HasExited )
{
watchdogProcess . Kill ( ) ;
}
}
catch { }
Environment . Exit ( 0 ) ;
}
}
else
{
if ( isUpdateMode )
{
LogHelper . WriteLogToFile ( "App | 更新模式启动,跳过重复运行检测" ) ;
}
2025-08-30 15:49:37 +08:00
else if ( isFinalApp )
{
LogHelper . WriteLogToFile ( "App | 最终应用启动,跳过重复运行检测" ) ;
}
else if ( skipMutexCheck )
{
LogHelper . WriteLogToFile ( "App | 跳过Mutex检查模式启动,跳过重复运行检测" ) ;
}
2025-08-30 14:11:39 +08:00
else
{
LogHelper . WriteLogToFile ( "App | 更新过程中,跳过重复运行检测" ) ;
}
2025-08-31 11:43:52 +08:00
2025-08-30 15:49:37 +08:00
// 在特殊模式下,创建一个临时的Mutex以避免其他检查出错
string mutexName = isFinalApp ? "InkCanvasForClass CE Final" : "InkCanvasForClass CE Update" ;
mutex = new Mutex ( true , mutexName , out bool tempRet ) ;
2025-08-31 11:43:52 +08:00
2025-08-30 14:11:39 +08:00
// 额外等待一小段时间确保更新进程完全退出
Thread . Sleep ( 1000 ) ;
2025-08-30 15:49:37 +08:00
LogHelper . WriteLogToFile ( "App | 特殊模式等待完成,继续启动" ) ;
2025-05-25 09:29:48 +08:00
}
_taskbar = ( TaskbarIcon ) FindResource ( "TaskbarTrayIcon" ) ;
StartArgs = e . Args ;
2025-08-31 11:43:52 +08:00
2025-08-30 14:11:39 +08:00
// 在非更新模式下创建主窗口
2025-10-01 18:38:01 +08:00
if ( _isSplashScreenShown )
{
SetSplashMessage ( "正在初始化主界面..." ) ;
SetSplashProgress ( 80 ) ;
await Task . Delay ( 500 ) ;
}
2025-08-30 14:11:39 +08:00
var mainWindow = new MainWindow ( ) ;
MainWindow = mainWindow ;
2025-10-01 18:17:39 +08:00
// 主窗口加载完成后关闭启动画面
mainWindow . Loaded + = ( s , args ) = >
{
2025-10-01 18:38:01 +08:00
if ( _isSplashScreenShown )
2025-10-01 18:17:39 +08:00
{
2025-10-01 18:38:01 +08:00
SetSplashMessage ( "启动完成!" ) ;
2025-10-01 18:45:26 +08:00
SetSplashProgress ( 100 ) ;
// 延迟关闭启动画面,让用户看到完成消息
2025-10-02 00:05:09 +08:00
Task . Delay ( 500 ) . ContinueWith ( _ = >
2025-10-01 18:38:01 +08:00
{
Dispatcher . Invoke ( ( ) = > CloseSplashScreen ( ) ) ;
} ) ;
}
2025-10-01 18:17:39 +08:00
} ;
2025-08-30 14:11:39 +08:00
mainWindow . Show ( ) ;
2025-06-17 13:20:06 +08:00
2025-10-01 18:45:26 +08:00
// 注册.icstk文件关联
2025-08-31 11:49:00 +08:00
try
{
LogHelper . WriteLogToFile ( "开始注册.icstk文件关联" ) ;
FileAssociationManager . RegisterFileAssociation ( ) ;
FileAssociationManager . ShowFileAssociationStatus ( ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"注册文件关联时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
2025-10-01 18:45:26 +08:00
// 启动IPC监听器
2025-08-31 11:49:00 +08:00
try
{
LogHelper . WriteLogToFile ( "启动IPC监听器" ) ;
FileAssociationManager . StartIpcListener ( ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"启动IPC监听器时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
2025-05-25 09:29:48 +08:00
}
2025-07-28 14:40:44 +08:00
private void ScrollViewer_PreviewMouseWheel ( object sender , MouseWheelEventArgs e )
2025-05-25 09:29:48 +08:00
{
try
{
2025-07-28 14:40:44 +08:00
if ( SystemInformation . MouseWheelScrollLines = = - 1 )
2025-05-25 09:29:48 +08:00
e . Handled = false ;
else
try
{
ScrollViewerEx SenderScrollViewer = ( ScrollViewerEx ) sender ;
2025-07-28 14:40:44 +08:00
SenderScrollViewer . ScrollToVerticalOffset ( SenderScrollViewer . VerticalOffset - e . Delta * 10 * SystemInformation . MouseWheelScrollLines / ( double ) 120 ) ;
2025-05-25 09:29:48 +08:00
e . Handled = true ;
}
2025-08-03 16:46:33 +08:00
catch { }
2025-05-25 09:29:48 +08:00
}
2025-08-03 16:46:33 +08:00
catch { }
2025-05-25 09:29:48 +08:00
}
2025-06-12 10:13:47 +08:00
2025-10-01 18:45:26 +08:00
// 用于设置崩溃后操作类型
2025-06-12 10:13:47 +08:00
public enum CrashActionType
{
SilentRestart ,
NoAction
}
2025-06-12 10:34:29 +08:00
// 心跳相关
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 ( "检测到主线程无响应,自动重启。" ) ;
2025-07-06 15:39:37 +08:00
SyncCrashActionFromSettings ( ) ; // 新增:心跳检测时同步最新设置
2025-06-12 10:34:29 +08:00
if ( CrashAction = = CrashActionType . SilentRestart )
{
2025-06-23 14:33:58 +08:00
StartupCount . Increment ( ) ;
if ( StartupCount . GetCount ( ) > = 5 )
{
MessageBox . Show ( "检测到程序已连续重启5次,已停止自动重启。请联系开发者或检查系统环境。" , "重启次数过多" , MessageBoxButton . OK , MessageBoxImage . Error ) ;
StartupCount . Reset ( ) ;
Environment . Exit ( 1 ) ;
}
2025-06-12 10:34:29 +08:00
try
{
string exePath = Process . GetCurrentProcess ( ) . MainModule . FileName ;
2025-07-29 02:33:19 +08:00
Process . Start ( exePath ) ;
2025-06-12 10:34:29 +08:00
}
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 ) ;
}
2025-10-01 18:45:26 +08:00
// 看门狗主逻辑
2025-06-12 10:34:29 +08:00
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 ) ;
}
2025-07-06 15:39:37 +08:00
// 主进程异常退出,自动重启前判断崩溃后操作
SyncCrashActionFromSettings ( ) ; // 新增:同步设置
if ( CrashAction = = CrashActionType . SilentRestart )
2025-06-23 14:33:58 +08:00
{
2025-07-06 15:39:37 +08:00
StartupCount . Increment ( ) ;
if ( StartupCount . GetCount ( ) > = 5 )
{
MessageBox . Show ( "检测到程序已连续重启5次,已停止自动重启。请联系开发者或检查系统环境。" , "重启次数过多" , MessageBoxButton . OK , MessageBoxImage . Error ) ;
StartupCount . Reset ( ) ;
Environment . Exit ( 1 ) ;
}
string exePath = Process . GetCurrentProcess ( ) . MainModule . FileName ;
2025-07-29 02:33:19 +08:00
Process . Start ( exePath ) ;
2025-06-23 14:33:58 +08:00
}
2025-07-06 15:39:37 +08:00
// CrashActionType.NoAction 时不重启,直接退出
2025-06-12 10:34:29 +08:00
}
catch { }
Environment . Exit ( 0 ) ;
}
}
private void App_Exit ( object sender , ExitEventArgs e )
{
// 仅在软件内主动退出时关闭看门狗,并写入退出信号
try
{
2025-10-01 18:45:26 +08:00
// 记录应用退出状态
2025-07-15 16:06:29 +08:00
string exitType = IsAppExitByUser ? "用户主动退出" : "应用程序退出" ;
WriteCrashLog ( $"{exitType},退出代码: {e.ApplicationExitCode}" ) ;
2025-08-03 16:46:33 +08:00
2025-07-26 14:29:24 +08:00
// 记录应用退出(设备标识符)
try
{
DeviceIdentifier . RecordAppExit ( ) ;
LogHelper . WriteLogToFile ( $"App | 应用运行时长: {(DateTime.Now - appStartTime).TotalMinutes:F1}分钟" ) ;
}
catch ( Exception deviceEx )
{
LogHelper . WriteLogToFile ( $"记录设备标识符退出信息失败: {deviceEx.Message}" , LogHelper . LogType . Error ) ;
}
2025-08-03 16:46:33 +08:00
2025-06-12 10:34:29 +08:00
if ( IsAppExitByUser )
{
// 写入退出信号文件,通知看门狗正常退出
2025-06-23 14:33:58 +08:00
StartupCount . Reset ( ) ;
2025-06-12 10:34:29 +08:00
File . WriteAllText ( watchdogExitSignalFile , "exit" ) ;
if ( watchdogProcess ! = null & & ! watchdogProcess . HasExited )
{
watchdogProcess . Kill ( ) ;
}
}
}
2025-08-03 16:46:33 +08:00
catch ( Exception ex )
2025-07-15 16:06:29 +08:00
{
// 尝试记录最后的错误
try
{
LogHelper . WriteLogToFile ( $"退出处理时发生错误: {ex.Message}" , LogHelper . LogType . Error ) ;
}
catch { }
}
2025-06-12 10:34:29 +08:00
}
2025-05-25 09:29:48 +08:00
}
}