From 7d5257359547d4b54093c1ec962f9bc77cbf0a4a Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Thu, 30 Apr 2026 14:50:57 +0800 Subject: [PATCH] =?UTF-8?q?improve:=E5=90=AF=E5=8A=A8=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/App.xaml.cs | 217 ++++++++++-------- Ink Canvas/Helpers/DeviceIdentifier.cs | 29 ++- Ink Canvas/MainWindow.xaml.cs | 171 +++++++------- Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs | 4 +- 4 files changed, 237 insertions(+), 184 deletions(-) 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 {