Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 64 additions & 40 deletions Ink Canvas/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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());

// 记录到崩溃日志
Expand Down Expand Up @@ -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);
}

Expand Down
28 changes: 25 additions & 3 deletions Ink Canvas/Automation/Services/RulesetService.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Ink_Canvas.Helpers;
using Ink_Canvas.WorkflowAutomation.Abstractions;
using Ink_Canvas.WorkflowAutomation.Enums;
using Ink_Canvas.WorkflowAutomation.Models;
Expand Down Expand Up @@ -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);
}
}

/// <summary>
Expand Down Expand Up @@ -217,7 +232,14 @@ public void UnregisterRuleHandler(string id, RuleRegistryInfo.HandleDelegate han
/// </summary>
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()
Expand Down
19 changes: 17 additions & 2 deletions Ink Canvas/Automation/Services/SystemEventMonitor.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Ink_Canvas.Helpers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
Expand Down Expand Up @@ -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);
}
}
}

Expand All @@ -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)]
Expand Down
14 changes: 11 additions & 3 deletions Ink Canvas/Automation/Triggers/TimerTrigger.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Ink_Canvas.WorkflowAutomation.Abstractions;
using System;
using System.Timers;

namespace Ink_Canvas.WorkflowAutomation.Triggers
Expand Down Expand Up @@ -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}");
}
}
}
}
10 changes: 9 additions & 1 deletion Ink Canvas/Helpers/DelayActionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
12 changes: 10 additions & 2 deletions Ink Canvas/Helpers/WindowOverviewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

/// <summary>
Expand Down
66 changes: 57 additions & 9 deletions Ink Canvas/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
{
Expand Down Expand Up @@ -1926,6 +1973,7 @@ private void Window_Closed(object sender, EventArgs e)
{
RealtimeInkFrameScheduler.Clear();
SystemEvents.DisplaySettingsChanged -= SystemEventsOnDisplaySettingsChanged;
SystemEvents.UserPreferenceChanged -= SystemEvents_UserPreferenceChanged;
// 玻璃浮动栏刻意不设 Owner,必须显式关闭,否则残留窗口会挡住进程退出
HideLiquidGlassBar();

Expand Down
Loading