diff --git a/Ink Canvas/App.xaml.cs b/Ink Canvas/App.xaml.cs
index 8737e18f..9f78632c 100644
--- a/Ink Canvas/App.xaml.cs
+++ b/Ink Canvas/App.xaml.cs
@@ -19,6 +19,7 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
+using System.Windows.Interop;
using System.Windows.Threading;
using Application = System.Windows.Application;
using MessageBox = System.Windows.MessageBox;
@@ -77,6 +78,10 @@ namespace Ink_Canvas
private static string lastErrorMessage = string.Empty;
// 新增:是否已初始化崩溃监听器
private static bool crashListenersInitialized;
+ private IntPtr processDestroyHook = IntPtr.Zero;
+ private IntPtr monitoredMainWindowHandle = IntPtr.Zero;
+ private bool mainWindowDestroyedLogged;
+ private WinEventDelegate processDestroyHookCallback;
// 新增:启动画面相关
private static SplashScreen _splashScreen;
private static bool _isSplashScreenShown = false;
@@ -208,15 +213,13 @@ namespace Ink_Canvas
// 尝试注册Windows关闭消息监听
SetConsoleCtrlHandler(ConsoleCtrlHandler, true);
- // 如果系统支持,添加Windows Management Instrumentation监听器
try
{
- // 使用反射动态加载和调用WMI
- TrySetupWmiMonitoring();
+ TrySetupTerminationMonitoring();
}
- catch (Exception wmiEx)
+ catch (Exception monitorEx)
{
- LogHelper.WriteLogToFile($"设置WMI进程监控失败: {wmiEx.Message}", LogHelper.LogType.Warning);
+ LogHelper.WriteLogToFile($"设置终止监控失败: {monitorEx.Message}", LogHelper.LogType.Warning);
}
crashListenersInitialized = true;
@@ -228,113 +231,114 @@ namespace Ink_Canvas
}
}
- // 动态加载WMI监控
- private void TrySetupWmiMonitoring()
+ private void TrySetupTerminationMonitoring()
{
- object watcher = null;
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;
- }
+ processDestroyHookCallback = OnWinEventMainWindowDestroyed;
- // 使用反射创建WMI查询
- var watcherType = assembly.GetType("System.Management.ManagementEventWatcher");
- if (watcherType == null)
- {
- LogHelper.WriteLogToFile("未找到ManagementEventWatcher类型,跳过WMI监控", LogHelper.LogType.Warning);
- return;
- }
-
- // 构建WMI查询字符串
- string queryString = $"SELECT * FROM __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.ProcessId = {currentProcessId}";
-
- // 创建ManagementEventWatcher实例
- watcher = Activator.CreateInstance(watcherType, queryString);
-
- // 获取EventArrived事件信息
- var eventInfo = watcherType.GetEvent("EventArrived");
- if (eventInfo == null)
- {
- LogHelper.WriteLogToFile("未找到EventArrived事件,跳过WMI监控", LogHelper.LogType.Warning);
- return;
- }
-
- // 创建委托并订阅事件
- Type delegateType = eventInfo.EventHandlerType;
- var handler = Delegate.CreateDelegate(delegateType, this, GetType().GetMethod("WmiEventHandler", BindingFlags.NonPublic | BindingFlags.Instance));
- eventInfo.AddEventHandler(watcher, handler);
-
- // 启动监听
- var startMethod = watcherType.GetMethod("Start");
- if (startMethod == null)
- {
- LogHelper.WriteLogToFile("未找到ManagementEventWatcher.Start方法,跳过WMI监控", LogHelper.LogType.Warning);
- return;
- }
-
- try
- {
- startMethod.Invoke(watcher, null);
- }
- catch (TargetInvocationException tiex)
- {
- var root = tiex.InnerException ?? tiex;
- string rootType = root.GetType().FullName;
- LogHelper.WriteLogToFile($"WMI监控启动失败: {rootType}: {root.Message}", LogHelper.LogType.Warning);
- return;
- }
-
- LogHelper.WriteLogToFile("已成功启动WMI进程监控");
+ // 等主窗口句柄可用后再开始监听
+ Dispatcher.BeginInvoke(new Action(BindMainWindowLifecycle), DispatcherPriority.ApplicationIdle);
}
catch (Exception ex)
{
- var root = ex is TargetInvocationException outerTie && outerTie.InnerException != null
- ? outerTie.InnerException
- : ex;
- string rootType = root.GetType().FullName;
- LogHelper.WriteLogToFile($"动态加载WMI监控失败: {rootType}: {root.Message}", LogHelper.LogType.Warning);
- try
- {
- if (watcher != null)
- {
- var stopMethod = watcher.GetType().GetMethod("Stop");
- stopMethod?.Invoke(watcher, null);
- }
- }
- catch
- {
- // 忽略清理阶段异常,避免覆盖原始错误信息
- }
+ LogHelper.WriteLogToFile($"初始化终止监控失败: {ex.GetType().FullName}: {ex.Message}", LogHelper.LogType.Warning);
}
}
- // WMI事件处理方法
- private void WmiEventHandler(object sender, EventArgs e)
+ private void BindMainWindowLifecycle()
{
try
{
- // 尝试从事件参数中提取信息
- dynamic eventArgs = e;
- dynamic newEvent = eventArgs.NewEvent;
- if (newEvent != null)
+ if (Current?.MainWindow == null)
{
- dynamic targetInstance = newEvent["TargetInstance"];
- if (targetInstance != null)
- {
- string processName = targetInstance["Name"]?.ToString() ?? "未知进程";
- WriteCrashLog($"WMI检测到进程{processName}(ID:{currentProcessId})已终止");
- }
+ return;
}
+
+ Current.MainWindow.SourceInitialized -= MainWindow_SourceInitialized;
+ Current.MainWindow.SourceInitialized += MainWindow_SourceInitialized;
}
catch (Exception ex)
{
- LogHelper.WriteLogToFile($"处理WMI事件时出错: {ex.Message}", LogHelper.LogType.Warning);
+ }
+ }
+
+ private void MainWindow_SourceInitialized(object sender, EventArgs e)
+ {
+ try
+ {
+ if (!(sender is Window window))
+ {
+ return;
+ }
+
+ monitoredMainWindowHandle = new WindowInteropHelper(window).Handle;
+ if (monitoredMainWindowHandle == IntPtr.Zero)
+ {
+ return;
+ }
+
+ RegisterMainWindowDestroyHook();
+ }
+ catch (Exception ex)
+ {
+ }
+ }
+
+ private void RegisterMainWindowDestroyHook()
+ {
+ if (processDestroyHook != IntPtr.Zero || monitoredMainWindowHandle == IntPtr.Zero)
+ {
+ return;
+ }
+
+ processDestroyHook = SetWinEventHook(
+ EVENT_OBJECT_DESTROY,
+ EVENT_OBJECT_DESTROY,
+ IntPtr.Zero,
+ processDestroyHookCallback,
+ (uint)currentProcessId,
+ 0,
+ WINEVENT_OUTOFCONTEXT);
+
+ if (processDestroyHook == IntPtr.Zero)
+ {
+ return;
+ }
+ }
+
+ private void OnWinEventMainWindowDestroyed(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
+ {
+ if (eventType != EVENT_OBJECT_DESTROY || mainWindowDestroyedLogged)
+ {
+ return;
+ }
+
+ if (idObject != OBJID_WINDOW || idChild != CHILDID_SELF)
+ {
+ return;
+ }
+
+ if (hwnd != monitoredMainWindowHandle || hwnd == IntPtr.Zero)
+ {
+ return;
+ }
+
+ mainWindowDestroyedLogged = true;
+ }
+
+ private void CleanupTerminationMonitoring()
+ {
+ try
+ {
+ if (processDestroyHook != IntPtr.Zero)
+ {
+ UnhookWinEvent(processDestroyHook);
+ processDestroyHook = IntPtr.Zero;
+ }
+ }
+ catch
+ {
}
}
@@ -343,6 +347,19 @@ namespace Ink_Canvas
private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate handler, bool add);
private delegate bool ConsoleCtrlDelegate(int ctrlType);
+ private delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
+
+ private const uint EVENT_OBJECT_DESTROY = 0x8001;
+ private const uint WINEVENT_OUTOFCONTEXT = 0x0000;
+ private const int OBJID_WINDOW = 0;
+ private const int CHILDID_SELF = 0;
+
+ [DllImport("user32.dll")]
+ private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
+
+ [DllImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool UnhookWinEvent(IntPtr hWinEventHook);
private static bool ConsoleCtrlHandler(int ctrlType)
{
@@ -477,6 +494,7 @@ namespace Ink_Canvas
// 处理进程退出事件
private void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
+ CleanupTerminationMonitoring();
TimeSpan runDuration = DateTime.Now - appStartTime;
string durationText = FormatTimeSpan(runDuration);
WriteCrashLog($"应用程序退出,运行时长: {durationText}");
@@ -758,16 +776,16 @@ namespace Ink_Canvas
// 根据设置决定是否显示启动画面
if (ShouldShowSplashScreen() && !IsLaunchByFileOrUri(e.Args))
{
+ await Task.Delay(200);
ShowSplashScreen();
SetSplashMessage(Strings.GetString("Splash_Starting"));
SetSplashProgress(20);
- await Task.Delay(500);
// 强制刷新UI,确保启动画面显示
Application.Current.Dispatcher.Invoke(() => { }, DispatcherPriority.Render);
}
- await Task.Delay(500);
+ await Task.Delay(200);
RootPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
LogHelper.NewLog(string.Format("Ink Canvas Starting (Version: {0})", Assembly.GetExecutingAssembly().GetName().Version));
@@ -803,7 +821,7 @@ namespace Ink_Canvas
{
SetSplashMessage("正在初始化组件...");
SetSplashProgress(40);
- await Task.Delay(500);
+ await Task.Delay(200);
}
try
{
@@ -819,7 +837,6 @@ namespace Ink_Canvas
{
SetSplashMessage("正在初始化组件...");
SetSplashProgress(50);
- await Task.Delay(300);
}
try
{
@@ -835,7 +852,7 @@ namespace Ink_Canvas
{
SetSplashMessage("正在加载配置...");
SetSplashProgress(60);
- await Task.Delay(500);
+ await Task.Delay(200);
}
DeviceIdentifier.RecordAppLaunch();
try
@@ -1129,7 +1146,6 @@ namespace Ink_Canvas
{
SetSplashMessage("正在初始化主界面...");
SetSplashProgress(80);
- await Task.Delay(500);
}
var mainWindow = new MainWindow();
MainWindow = mainWindow;
@@ -1541,6 +1557,7 @@ namespace Ink_Canvas
private void App_Exit(object sender, ExitEventArgs e)
{
+ CleanupTerminationMonitoring();
// 卸载所有插件
try
{
diff --git a/Ink Canvas/Helpers/DeviceIdentifier.cs b/Ink Canvas/Helpers/DeviceIdentifier.cs
index a78e1a4d..17f3223c 100644
--- a/Ink Canvas/Helpers/DeviceIdentifier.cs
+++ b/Ink Canvas/Helpers/DeviceIdentifier.cs
@@ -22,6 +22,9 @@ namespace Ink_Canvas.Helpers
private static readonly string DeviceId;
private static readonly object fileLock = new object();
+ private static UsageStats usageStatsCache;
+ private static DateTime usageStatsCacheTime;
+ private static readonly TimeSpan UsageStatsCacheDuration = TimeSpan.FromMinutes(2);
static DeviceIdentifier()
{
@@ -654,7 +657,7 @@ namespace Ink_Canvas.Helpers
{
try
{
- var stats = LoadUsageStats();
+ var stats = GetUsageStatsCached();
return stats.SystemVersion;
}
catch (Exception ex)
@@ -773,7 +776,7 @@ namespace Ink_Canvas.Helpers
{
try
{
- var stats = LoadUsageStats();
+ var stats = GetUsageStatsCached();
return stats.UpdatePriority;
}
catch (Exception ex)
@@ -790,7 +793,7 @@ namespace Ink_Canvas.Helpers
{
try
{
- var stats = LoadUsageStats();
+ var stats = GetUsageStatsCached();
return stats.UsageFrequency;
}
catch (Exception ex)
@@ -892,6 +895,23 @@ namespace Ink_Canvas.Helpers
}
}
+ private static UsageStats GetUsageStatsCached(bool forceRefresh = false)
+ {
+ lock (fileLock)
+ {
+ if (!forceRefresh
+ && usageStatsCache != null
+ && (DateTime.Now - usageStatsCacheTime) < UsageStatsCacheDuration)
+ {
+ return usageStatsCache;
+ }
+
+ usageStatsCache = LoadUsageStats();
+ usageStatsCacheTime = DateTime.Now;
+ return usageStatsCache;
+ }
+ }
+
///
/// 保存使用统计
///
@@ -902,6 +922,9 @@ namespace Ink_Canvas.Helpers
// 保存到备份文件
SaveUsageStatsToFile(UsageStatsBackupPath, stats);
+
+ usageStatsCache = stats;
+ usageStatsCacheTime = DateTime.Now;
}
diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs
index 5f36f899..45719c36 100644
--- a/Ink Canvas/MainWindow.xaml.cs
+++ b/Ink Canvas/MainWindow.xaml.cs
@@ -1190,6 +1190,7 @@ namespace Ink_Canvas
private bool isLoaded;
private bool _suppressChickenSoupSourceSelectionChanged;
private bool forcePointEraser;
+ private bool _pendingStartupAutoUpdateCheck;
///
/// 在窗口加载完成后初始化应用的核心子系统、UI 状态和运行时监控组件。
@@ -1203,91 +1204,12 @@ namespace Ink_Canvas
//加载设置
LoadSettings(true);
ApplyLanguageFromSettings();
- AutoBackupManager.Initialize(Settings);
-
- // 初始化上传队列(恢复上次的上传队列)
- try
- {
- UploadQueueHelper.InitializeAllQueues();
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"[MainWindow] 初始化上传队列时出错: {ex.Message}", LogHelper.LogType.Error);
- // 继续执行其他初始化操作,不中断整个加载过程
- }
_ = TelemetryUploader.UploadTelemetryIfNeededAsync();
- // 检查保存路径是否可用,不可用则修正
- try
- {
- string savePath = Settings.Automation.AutoSavedStrokesLocation;
- bool needFix = false;
- if (string.IsNullOrWhiteSpace(savePath) || !Directory.Exists(savePath))
- {
- needFix = true;
- }
- else
- {
- // 检查是否可写
- try
- {
- string testFile = Path.Combine(savePath, "test.tmp");
- File.WriteAllText(testFile, "test");
- File.Delete(testFile);
- }
- catch
- {
- needFix = true;
- }
- }
- if (needFix)
- {
- string newPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Saves");
- Settings.Automation.AutoSavedStrokesLocation = newPath;
- if (!Directory.Exists(newPath))
- Directory.CreateDirectory(newPath);
- SaveSettingsToFile();
- LogHelper.WriteLogToFile($"自动修正保存路径为: {newPath}");
- }
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"检测或修正保存路径时出错: {ex.Message}", LogHelper.LogType.Error);
- }
-
- // 加载自定义背景颜色
LoadCustomBackgroundColor();
-
- // 设置窗口模式
SetWindowMode();
- // 初始化PPT管理器
- InitializePPTManagers();
-
- // 如果启用PPT支持,开始监控
- if (Settings.PowerPointSettings.PowerPointSupport)
- {
- StartPPTMonitoring();
- }
-
- // 初始化窗口概览模型
- try
- {
- _windowOverviewModel = new WindowOverviewModel();
- LogHelper.WriteLogToFile("窗口概览模型已初始化", LogHelper.LogType.Event);
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"初始化窗口概览模型失败: {ex.Message}", LogHelper.LogType.Error);
- }
-
- // 如果启用PowerPoint联动增强功能,启动进程守护
- if (Settings.PowerPointSettings.EnablePowerPointEnhancement)
- {
- StartPowerPointProcessMonitoring();
- }
-
// HasNewUpdateWindow hasNewUpdateWindow = new HasNewUpdateWindow();
// 根据设置应用主题
switch (Settings.Appearance.Theme)
@@ -1393,6 +1315,8 @@ namespace Ink_Canvas
ApplyUIAccessTopMost();
}
+ _ = RunDeferredStartupPhaseBAsync();
+
// 初始化剪贴板监控
InitializeClipboardMonitoring();
@@ -2466,6 +2390,95 @@ namespace Ink_Canvas
}
}
+ private async Task RunDeferredStartupPhaseBAsync()
+ {
+ await Task.Delay(1200);
+
+ try
+ {
+ AutoBackupManager.Initialize(Settings);
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"[MainWindow] 初始化自动备份管理器时出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+
+ try
+ {
+ UploadQueueHelper.InitializeAllQueues();
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"[MainWindow] 初始化上传队列时出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+
+ try
+ {
+ string savePath = Settings.Automation.AutoSavedStrokesLocation;
+ bool needFix = false;
+ if (string.IsNullOrWhiteSpace(savePath) || !Directory.Exists(savePath))
+ {
+ needFix = true;
+ }
+ else
+ {
+ try
+ {
+ string testFile = Path.Combine(savePath, "test.tmp");
+ File.WriteAllText(testFile, "test");
+ File.Delete(testFile);
+ }
+ catch
+ {
+ needFix = true;
+ }
+ }
+
+ if (needFix)
+ {
+ string newPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Saves");
+ Settings.Automation.AutoSavedStrokesLocation = newPath;
+ if (!Directory.Exists(newPath))
+ Directory.CreateDirectory(newPath);
+ SaveSettingsToFile();
+ LogHelper.WriteLogToFile($"自动修正保存路径为: {newPath}");
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"检测或修正保存路径时出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+
+ InitializePPTManagers();
+ if (Settings.PowerPointSettings.PowerPointSupport)
+ {
+ StartPPTMonitoring();
+ }
+
+ try
+ {
+ _windowOverviewModel = new WindowOverviewModel();
+ LogHelper.WriteLogToFile("窗口概览模型已初始化", LogHelper.LogType.Event);
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"初始化窗口概览模型失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+
+ if (Settings.PowerPointSettings.EnablePowerPointEnhancement)
+ {
+ StartPowerPointProcessMonitoring();
+ }
+
+ if (_pendingStartupAutoUpdateCheck && Settings.Startup?.IsAutoUpdate == true)
+ {
+ _pendingStartupAutoUpdateCheck = false;
+ await Task.Delay(5000);
+ LogHelper.WriteLogToFile("AutoUpdate | Running deferred auto-update check after startup");
+ AutoUpdate();
+ }
+ }
+
///
/// 窗口失去焦点时的处理
///
diff --git a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs
index 27ad2e27..a2ffd31c 100644
--- a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs
+++ b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs
@@ -216,8 +216,8 @@ namespace Ink_Canvas
{
if (isStartup)
{
- LogHelper.WriteLogToFile("AutoUpdate | Running auto-update check at startup");
- AutoUpdate();
+ _pendingStartupAutoUpdateCheck = true;
+ LogHelper.WriteLogToFile("AutoUpdate | Startup check deferred until UI is stable");
}
else
{