diff --git a/Ink Canvas/App.xaml.cs b/Ink Canvas/App.xaml.cs
index 80caedd9d..d41170e9e 100644
--- a/Ink Canvas/App.xaml.cs
+++ b/Ink Canvas/App.xaml.cs
@@ -979,7 +979,14 @@ private void App_DispatcherUnhandledException(object sender, DispatcherUnhandled
}
}
- Ink_Canvas.MainWindow.ShowNewMessage(MainWindowStrings.Main_App_UnexpectedError);
+ try
+ {
+ Ink_Canvas.MainWindow.ShowNewMessage(MainWindowStrings.Main_App_UnexpectedError);
+ }
+ catch (Exception notifyEx)
+ {
+ System.Diagnostics.Debug.WriteLine(notifyEx);
+ }
LogHelper.NewLog(e.Exception.ToString());
// 记录到崩溃日志
@@ -1861,57 +1868,74 @@ private void StartHeartbeatMonitor()
watchdogTimer = new Timer(_ =>
{
- if (isAppExiting)
- return;
- if (IsOobeShowing)
- return;
-
- if (!isStartupComplete && appStartupStartTime != DateTime.MinValue)
+ try
{
- DateTime startTime = _isSplashScreenShown && splashScreenStartTime != DateTime.MinValue
- ? splashScreenStartTime
- : appStartupStartTime;
- TimeSpan elapsedSinceStart = DateTime.Now - startTime;
- if (elapsedSinceStart.TotalMinutes >= 2)
+ if (isAppExiting)
+ return;
+ if (IsOobeShowing)
+ return;
+
+ if (!isStartupComplete && appStartupStartTime != DateTime.MinValue)
{
- string timeType = _isSplashScreenShown ? "启动画面已显示" : "应用启动开始";
- string restartReason = $"检测到启动假死:{timeType}{elapsedSinceStart.TotalMinutes:F2}分钟,但未收到启动完成心跳,自动重启。";
- LogHelper.WriteLogToFile(restartReason, LogHelper.LogType.Error);
- WriteCrashLog(restartReason);
- SyncCrashActionFromSettings();
- if (CrashAction == CrashActionType.SilentRestart)
+ DateTime startTime = _isSplashScreenShown && splashScreenStartTime != DateTime.MinValue
+ ? splashScreenStartTime
+ : appStartupStartTime;
+ TimeSpan elapsedSinceStart = DateTime.Now - startTime;
+ if (elapsedSinceStart.TotalMinutes >= 2)
{
- TryRestartWithBreaker(restartReason);
+ string timeType = _isSplashScreenShown ? "启动画面已显示" : "应用启动开始";
+ string restartReason = $"检测到启动假死:{timeType}{elapsedSinceStart.TotalMinutes:F2}分钟,但未收到启动完成心跳,自动重启。";
+ LogHelper.WriteLogToFile(restartReason, LogHelper.LogType.Error);
+ WriteCrashLog(restartReason);
+ SyncCrashActionFromSettings();
+ if (CrashAction == CrashActionType.SilentRestart)
+ {
+ TryRestartWithBreaker(restartReason);
+ }
+ return;
}
- return;
}
- }
-
- if (isStartupComplete)
- {
- var now = DateTime.Now;
- var sinceHeartbeat = now - lastHeartbeat;
- var sinceStartupComplete = startupCompleteHeartbeat == DateTime.MinValue
- ? TimeSpan.Zero
- : now - startupCompleteHeartbeat;
- if (sinceStartupComplete.TotalSeconds < 30)
+ if (isStartupComplete)
{
- return;
- }
+ var now = DateTime.Now;
+ var sinceHeartbeat = now - lastHeartbeat;
+ var sinceStartupComplete = startupCompleteHeartbeat == DateTime.MinValue
+ ? TimeSpan.Zero
+ : now - startupCompleteHeartbeat;
- if (sinceHeartbeat.TotalSeconds > 10)
- {
- string restartReason = $"检测到主线程无响应,自动重启。心跳超时 {sinceHeartbeat.TotalSeconds:F1} 秒。";
- LogHelper.NewLog(restartReason);
- WriteCrashLog(restartReason);
- SyncCrashActionFromSettings();
- if (CrashAction == CrashActionType.SilentRestart)
+ if (sinceStartupComplete.TotalSeconds < 30)
{
- TryRestartWithBreaker(restartReason);
+ return;
+ }
+
+ if (sinceHeartbeat.TotalSeconds > 10)
+ {
+ string restartReason = $"检测到主线程无响应,自动重启。心跳超时 {sinceHeartbeat.TotalSeconds:F1} 秒。";
+ LogHelper.NewLog(restartReason);
+ WriteCrashLog(restartReason);
+ SyncCrashActionFromSettings();
+ if (CrashAction == CrashActionType.SilentRestart)
+ {
+ TryRestartWithBreaker(restartReason);
+ }
}
}
}
+ catch (Exception ex)
+ {
+ // 看门狗回调运行在 ThreadPool 上,任何未捕获异常都会直接终止进程。
+ // 这里兜底记录,避免看门狗自身成为闪退源。
+ try
+ {
+ LogHelper.WriteLogToFile($"心跳看门狗回调异常: {ex.Message}", LogHelper.LogType.Error);
+ WriteCrashLog($"心跳看门狗回调异常: {ex}");
+ }
+ catch
+ {
+ System.Diagnostics.Debug.WriteLine(ex);
+ }
+ }
}, null, 0, 3000);
}
diff --git a/Ink Canvas/Automation/Services/RulesetService.cs b/Ink Canvas/Automation/Services/RulesetService.cs
index aa184da02..7d92c47a2 100644
--- a/Ink Canvas/Automation/Services/RulesetService.cs
+++ b/Ink Canvas/Automation/Services/RulesetService.cs
@@ -1,3 +1,4 @@
+using Ink_Canvas.Helpers;
using Ink_Canvas.WorkflowAutomation.Abstractions;
using Ink_Canvas.WorkflowAutomation.Enums;
using Ink_Canvas.WorkflowAutomation.Models;
@@ -52,12 +53,26 @@ public RulesetService()
private void OnStatusMayHaveChanged(object sender, EventArgs e)
{
- NotifyStatusChanged();
+ try
+ {
+ NotifyStatusChanged();
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"规则状态变化事件处理失败: {ex.Message}", LogHelper.LogType.Warning);
+ }
}
private void OnFallbackTimerElapsed(object sender, ElapsedEventArgs e)
{
- NotifyStatusChanged();
+ try
+ {
+ NotifyStatusChanged();
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"规则状态兜底轮询处理失败: {ex.Message}", LogHelper.LogType.Warning);
+ }
}
///
@@ -217,7 +232,14 @@ public void UnregisterRuleHandler(string id, RuleRegistryInfo.HandleDelegate han
///
public void NotifyStatusChanged()
{
- StatusUpdated?.Invoke(this, EventArgs.Empty);
+ try
+ {
+ StatusUpdated?.Invoke(this, EventArgs.Empty);
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"规则状态更新通知处理失败: {ex.Message}", LogHelper.LogType.Warning);
+ }
}
public void Dispose()
diff --git a/Ink Canvas/Automation/Services/SystemEventMonitor.cs b/Ink Canvas/Automation/Services/SystemEventMonitor.cs
index ae62a06aa..398f057b4 100644
--- a/Ink Canvas/Automation/Services/SystemEventMonitor.cs
+++ b/Ink Canvas/Automation/Services/SystemEventMonitor.cs
@@ -1,3 +1,4 @@
+using Ink_Canvas.Helpers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -198,7 +199,14 @@ private void OnProcessTimerElapsed(object sender, ElapsedEventArgs e)
if (anyChanged)
{
- ProcessChanged?.Invoke(this, EventArgs.Empty);
+ try
+ {
+ ProcessChanged?.Invoke(this, EventArgs.Empty);
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"进程状态变化事件处理失败: {ex.Message}", LogHelper.LogType.Warning);
+ }
}
}
@@ -221,7 +229,14 @@ private static bool CheckProcessRunning(string processName)
private void OnForegroundWindowEvent(HWINEVENTHOOK hWinEventHook, uint eventType,
HWND hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
- ForegroundWindowChanged?.Invoke(this, EventArgs.Empty);
+ try
+ {
+ ForegroundWindowChanged?.Invoke(this, EventArgs.Empty);
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"前台窗口变化事件处理失败: {ex.Message}", LogHelper.LogType.Warning);
+ }
}
//[DllImport("user32.dll", SetLastError = true)]
diff --git a/Ink Canvas/Automation/Triggers/TimerTrigger.cs b/Ink Canvas/Automation/Triggers/TimerTrigger.cs
index bfe56d375..be4a02e39 100644
--- a/Ink Canvas/Automation/Triggers/TimerTrigger.cs
+++ b/Ink Canvas/Automation/Triggers/TimerTrigger.cs
@@ -1,4 +1,5 @@
using Ink_Canvas.WorkflowAutomation.Abstractions;
+using System;
using System.Timers;
namespace Ink_Canvas.WorkflowAutomation.Triggers
@@ -50,9 +51,16 @@ public override void UnLoaded()
private void OnTimerElapsed(object sender, ElapsedEventArgs e)
{
- if (Settings.TriggerOnce && _hasTriggered) return;
- _hasTriggered = true;
- Trigger();
+ try
+ {
+ if (Settings.TriggerOnce && _hasTriggered) return;
+ _hasTriggered = true;
+ Trigger();
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"TimerTrigger.OnTimerElapsed: {ex.Message}");
+ }
}
}
}
diff --git a/Ink Canvas/Helpers/DelayActionHelper.cs b/Ink Canvas/Helpers/DelayActionHelper.cs
index 667862481..a47f42263 100644
--- a/Ink Canvas/Helpers/DelayActionHelper.cs
+++ b/Ink Canvas/Helpers/DelayActionHelper.cs
@@ -25,7 +25,15 @@ public void DebounceAction(int timeMs, ISynchronizeInvoke inv, Action action)
// 解除订阅,打破 timer.Elapsed → lambda → timer 循环引用
_timerDebounce.Elapsed -= elapsedHandler;
_timerDebounce.Stop(); _timerDebounce.Close(); _timerDebounce = null;
- InvokeAction(action, inv);
+ try
+ {
+ InvokeAction(action, inv);
+ }
+ catch (Exception ex)
+ {
+ // 回调运行在 System.Timers.Timer 线程上,未捕获异常会直接终止进程。
+ System.Diagnostics.Debug.WriteLine($"DelayAction 回调异常: {ex.Message}");
+ }
};
_timerDebounce.Elapsed += elapsedHandler;
}
diff --git a/Ink Canvas/Helpers/WindowOverviewModel.cs b/Ink Canvas/Helpers/WindowOverviewModel.cs
index e3c14e49e..cc43f02bc 100644
--- a/Ink Canvas/Helpers/WindowOverviewModel.cs
+++ b/Ink Canvas/Helpers/WindowOverviewModel.cs
@@ -432,8 +432,16 @@ public void UpdateWindows()
_windows = windows;
}
- // 触发更新事件
- WindowsUpdated?.Invoke(this, windows);
+ // 触发更新事件。订阅方(插件等)可能抛异常,而本方法通常运行在
+ // System.Threading.Timer 的后台线程上,未捕获的异常会直接终止进程。
+ try
+ {
+ WindowsUpdated?.Invoke(this, windows);
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"窗口概览更新事件处理失败: {ex.Message}", LogHelper.LogType.Warning);
+ }
}
///
diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs
index 9845abea3..11e1efdf1 100644
--- a/Ink Canvas/MainWindow.xaml.cs
+++ b/Ink Canvas/MainWindow.xaml.cs
@@ -1696,9 +1696,31 @@ private void Window_Loaded(object sender, RoutedEventArgs e)
private void SystemEventsOnDisplaySettingsChanged(object sender, EventArgs e)
{
- if (!Settings.Advanced.IsEnableResolutionChangeDetection) return;
- ShowNotification(string.Format(Properties.MainWindowStrings.Main_DisplayChanged, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height));
- HandleFloatingBarRecovery();
+ // SystemEvents 事件在系统事件专用线程上触发,不能直接访问 WPF UI 对象;
+ // 必须封送到主线程,否则会因跨线程访问 UI 抛出未处理异常导致进程闪退。
+ try
+ {
+ if (Dispatcher == null || Dispatcher.HasShutdownStarted || Dispatcher.HasShutdownFinished)
+ return;
+
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ try
+ {
+ if (Settings?.Advanced == null || !Settings.Advanced.IsEnableResolutionChangeDetection) return;
+ ShowNotification(string.Format(Properties.MainWindowStrings.Main_DisplayChanged, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height));
+ HandleFloatingBarRecovery();
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"显示器配置变化处理失败: {ex.Message}", LogHelper.LogType.Warning);
+ }
+ }), DispatcherPriority.Normal);
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"调度显示器配置变化处理失败: {ex.Message}", LogHelper.LogType.Warning);
+ }
}
private void MainWindow_OnDpiChanged(object sender, DpiChangedEventArgs e)
@@ -1724,14 +1746,39 @@ private void HandleFloatingBarRecovery()
isFloatingBarOutsideScreen = IsOutsideOfScreenHelper.IsOutsideOfScreen(ViewboxFloatingBar);
isInPPTPresentationMode = IsInPPTPresentationMode;
}, DispatcherPriority.Normal, TimeSpan.FromSeconds(5));
- if (isFloatingBarOutsideScreen) dpiChangedDelayAction.DebounceAction(3000, null, () =>
+ if (isFloatingBarOutsideScreen)
{
- if (!isFloatingBarFolded)
+ // DelayAction 在 null 同步对象时会在 System.Timers.Timer 线程直接执行回调;
+ // 该回调若直接访问 WPF 控件会因跨线程访问 UI 导致未处理异常。这里只负责封送。
+ dpiChangedDelayAction.DebounceAction(3000, null, () =>
{
- if (isInPPTPresentationMode) ViewboxFloatingBarMarginAnimation(60);
- else ViewboxFloatingBarMarginAnimation(100, true);
- }
- });
+ try
+ {
+ if (Dispatcher == null || Dispatcher.HasShutdownStarted || Dispatcher.HasShutdownFinished)
+ return;
+
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ try
+ {
+ if (!isFloatingBarFolded)
+ {
+ if (isInPPTPresentationMode) ViewboxFloatingBarMarginAnimation(60);
+ else ViewboxFloatingBarMarginAnimation(100, true);
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"浮动工具栏恢复动画失败: {ex.Message}", LogHelper.LogType.Warning);
+ }
+ }), DispatcherPriority.Normal);
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"调度浮动工具栏恢复失败: {ex.Message}", LogHelper.LogType.Warning);
+ }
+ });
+ }
}
catch (Exception ex)
{
@@ -1926,6 +1973,7 @@ private void Window_Closed(object sender, EventArgs e)
{
RealtimeInkFrameScheduler.Clear();
SystemEvents.DisplaySettingsChanged -= SystemEventsOnDisplaySettingsChanged;
+ SystemEvents.UserPreferenceChanged -= SystemEvents_UserPreferenceChanged;
// 玻璃浮动栏刻意不设 Owner,必须显式关闭,否则残留窗口会挡住进程退出
HideLiquidGlassBar();
diff --git a/Ink Canvas/MainWindow_cs/MW_AutoTheme.cs b/Ink Canvas/MainWindow_cs/MW_AutoTheme.cs
index 8ecc6dbbc..885920feb 100644
--- a/Ink Canvas/MainWindow_cs/MW_AutoTheme.cs
+++ b/Ink Canvas/MainWindow_cs/MW_AutoTheme.cs
@@ -282,19 +282,43 @@ void SetFloatingBarButtonBrush(ToolbarImageButton btn)
private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
{
- switch (Settings.Appearance.Theme)
+ // SystemEvents 事件在系统事件专用线程上触发,不能直接访问 WPF UI 对象;
+ // 必须封送到主线程,否则会因跨线程访问 UI 抛出未处理异常导致进程闪退。
+ try
+ {
+ if (Dispatcher == null || Dispatcher.HasShutdownStarted || Dispatcher.HasShutdownFinished)
+ return;
+
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ try
+ {
+ if (Settings?.Appearance == null) return;
+
+ switch (Settings.Appearance.Theme)
+ {
+ case 0:
+ SetTheme(ThemeLight);
+ break;
+ case 1:
+ SetTheme(ThemeDark);
+ break;
+ case 2:
+ // 与 IsCurrentThemeDark / GetEffectiveTheme / 浮动栏一致,统一读 AppsUseLightTheme,
+ // 否则 SystemUsesLightTheme 与 AppsUseLightTheme 可独立取值时主题会混搭
+ SetTheme(ThemeHelper.IsSystemThemeLight() ? ThemeLight : ThemeDark);
+ break;
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"用户偏好变化(主题)处理失败: {ex.Message}", LogHelper.LogType.Warning);
+ }
+ }), DispatcherPriority.Normal);
+ }
+ catch (Exception ex)
{
- case 0:
- SetTheme(ThemeLight);
- break;
- case 1:
- SetTheme(ThemeDark);
- break;
- case 2:
- // 与 IsCurrentThemeDark / GetEffectiveTheme / 浮动栏一致,统一读 AppsUseLightTheme,
- // 否则 SystemUsesLightTheme 与 AppsUseLightTheme 可独立取值时主题会混搭
- SetTheme(ThemeHelper.IsSystemThemeLight() ? ThemeLight : ThemeDark);
- break;
+ LogHelper.WriteLogToFile($"调度用户偏好变化(主题)失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
diff --git a/Ink Canvas/MainWindow_cs/MW_Timer.cs b/Ink Canvas/MainWindow_cs/MW_Timer.cs
index d323206f7..15e8eacfc 100644
--- a/Ink Canvas/MainWindow_cs/MW_Timer.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Timer.cs
@@ -431,51 +431,58 @@ private async Task TimerNtpSync_ElapsedAsync()
///
private void TimerDisplayTime_Elapsed(object sender, ElapsedEventArgs e)
{
- DateTime localTime = DateTime.Now;
- DateTime displayTime = localTime;
+ try
+ {
+ DateTime localTime = DateTime.Now;
+ DateTime displayTime = localTime;
- TimeSpan timeJump = localTime - lastLocalTime;
- double timeJumpMinutes = Math.Abs(timeJump.TotalMinutes);
+ TimeSpan timeJump = localTime - lastLocalTime;
+ double timeJumpMinutes = Math.Abs(timeJump.TotalMinutes);
- if (timeJumpMinutes > 3 && !isNtpSyncing)
- {
- Task.Run(async () =>
+ if (timeJumpMinutes > 3 && !isNtpSyncing)
{
- try
- {
- await TimerNtpSync_ElapsedAsync();
- }
- catch (Exception ex)
+ Task.Run(async () =>
{
- LogHelper.WriteLogToFile($"时间跳跃触发的NTP同步失败: {ex.Message}", LogHelper.LogType.Error);
- }
- });
- }
- lastLocalTime = localTime;
-
- if (useNetworkTime && networkTimeOffset != TimeSpan.Zero)
- {
- displayTime = localTime + networkTimeOffset;
- }
+ try
+ {
+ await TimerNtpSync_ElapsedAsync();
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"时间跳跃触发的NTP同步失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ });
+ }
+ lastLocalTime = localTime;
- string timeString;
- if (Settings.Appearance.Use24HourTimeFormat)
- {
- timeString = displayTime.ToString("HH:mm:ss");
- }
- else
- {
- timeString = displayTime.ToString("tt hh'时'mm'分'ss'秒'");
- }
+ if (useNetworkTime && networkTimeOffset != TimeSpan.Zero)
+ {
+ displayTime = localTime + networkTimeOffset;
+ }
- if (timeString != lastDisplayedTime)
- {
- lastDisplayedTime = timeString;
+ string timeString;
+ if (Settings.Appearance.Use24HourTimeFormat)
+ {
+ timeString = displayTime.ToString("HH:mm:ss");
+ }
+ else
+ {
+ timeString = displayTime.ToString("tt hh'时'mm'分'ss'秒'");
+ }
- Dispatcher.BeginInvoke(new Action(() =>
+ if (timeString != lastDisplayedTime)
{
- nowTimeVM.nowTime = timeString;
- }));
+ lastDisplayedTime = timeString;
+
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ nowTimeVM.nowTime = timeString;
+ }));
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"时间显示定时器回调异常: {ex.Message}", LogHelper.LogType.Warning);
}
}
@@ -490,11 +497,18 @@ private void TimerDisplayTime_Elapsed(object sender, ElapsedEventArgs e)
///
private void TimerDisplayDate_Elapsed(object sender, ElapsedEventArgs e)
{
- // 使用BeginInvoke异步更新UI,避免阻塞
- Dispatcher.BeginInvoke(new Action(() =>
+ try
{
- nowTimeVM.nowDate = DateTime.Now.ToString("yyyy'年'MM'月'dd'日' dddd");
- }));
+ // 使用BeginInvoke异步更新UI,避免阻塞
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ nowTimeVM.nowDate = DateTime.Now.ToString("yyyy'年'MM'月'dd'日' dddd");
+ }));
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"日期显示定时器回调异常: {ex.Message}", LogHelper.LogType.Warning);
+ }
}
private DispatcherTimer _dispatcherTimerForTime;