improve:启动速度

This commit is contained in:
2026-04-30 14:50:57 +08:00
parent 6ac34ba8aa
commit 7d52573595
4 changed files with 237 additions and 184 deletions
+117 -100
View File
@@ -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
{
+26 -3
View File
@@ -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;
}
}
/// <summary>
/// 保存使用统计
/// </summary>
@@ -902,6 +922,9 @@ namespace Ink_Canvas.Helpers
// 保存到备份文件
SaveUsageStatsToFile(UsageStatsBackupPath, stats);
usageStatsCache = stats;
usageStatsCacheTime = DateTime.Now;
}
+92 -79
View File
@@ -1190,6 +1190,7 @@ namespace Ink_Canvas
private bool isLoaded;
private bool _suppressChickenSoupSourceSelectionChanged;
private bool forcePointEraser;
private bool _pendingStartupAutoUpdateCheck;
/// <summary>
/// 在窗口加载完成后初始化应用的核心子系统、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();
}
}
/// <summary>
/// 窗口失去焦点时的处理
/// </summary>
@@ -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
{