From 630b4edf916d0d463dc3362d712f14e2725199f5 Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Sun, 21 Sep 2025 07:52:11 +0800 Subject: [PATCH] =?UTF-8?q?improve:PPT=E5=A2=A8=E8=BF=B9=E4=BF=9D=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/Helpers/MultiPPTInkManager.cs | 164 +++++++++++++++++++++- Ink Canvas/Helpers/PPTInkManager.cs | 167 +++++++++++++++++++++-- Ink Canvas/Helpers/PPTManager.cs | 81 +++++------ 3 files changed, 350 insertions(+), 62 deletions(-) diff --git a/Ink Canvas/Helpers/MultiPPTInkManager.cs b/Ink Canvas/Helpers/MultiPPTInkManager.cs index ec30d31d..96b618ed 100644 --- a/Ink Canvas/Helpers/MultiPPTInkManager.cs +++ b/Ink Canvas/Helpers/MultiPPTInkManager.cs @@ -26,6 +26,11 @@ namespace Ink_Canvas.Helpers private readonly object _lockObject = new object(); private bool _disposed; private string _currentActivePresentationId = ""; + + // 墨迹备份机制 + private readonly Dictionary> _strokeBackups; + private DateTime _lastBackupTime = DateTime.MinValue; + private const int BackupIntervalMinutes = 2; // 每2分钟备份一次 #endregion #region Constructor @@ -33,6 +38,7 @@ namespace Ink_Canvas.Helpers { _presentationManagers = new Dictionary(); _presentationInfos = new Dictionary(); + _strokeBackups = new Dictionary>(); } #endregion @@ -112,8 +118,15 @@ namespace Ink_Canvas.Helpers var currentPresentation = GetCurrentActivePresentation(); if (currentPresentation != null) { - currentManager.SaveAllStrokesToFile(currentPresentation); - LogHelper.WriteLogToFile($"已保存当前演示文稿墨迹: {currentPresentation.Name}", LogHelper.LogType.Trace); + try + { + currentManager.SaveAllStrokesToFile(currentPresentation); + LogHelper.WriteLogToFile($"已保存当前演示文稿墨迹: {currentPresentation.Name}", LogHelper.LogType.Trace); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"保存当前演示文稿墨迹失败: {ex}", LogHelper.LogType.Error); + } } } } @@ -126,10 +139,10 @@ namespace Ink_Canvas.Helpers _presentationInfos[presentationId].LastAccessTime = DateTime.Now; } - if (_currentActivePresentationId != presentationId) - { - LogHelper.WriteLogToFile($"已切换到演示文稿: {presentation.Name}", LogHelper.LogType.Trace); - } + if (_currentActivePresentationId != presentationId) + { + LogHelper.WriteLogToFile($"已切换到演示文稿: {presentation.Name}", LogHelper.LogType.Trace); + } return true; } else @@ -161,7 +174,17 @@ namespace Ink_Canvas.Helpers var manager = GetCurrentManager(); if (manager != null) { + // 先创建备份 + if (!string.IsNullOrEmpty(_currentActivePresentationId)) + { + CreateStrokeBackup(_currentActivePresentationId, slideIndex, strokes); + } + + // 保存到管理器 manager.SaveCurrentSlideStrokes(slideIndex, strokes); + + // 检查是否需要执行定期备份 + CheckAndPerformBackup(); } } catch (Exception ex) @@ -209,12 +232,29 @@ namespace Ink_Canvas.Helpers var manager = GetCurrentManager(); if (manager != null) { - return manager.LoadSlideStrokes(slideIndex); + var strokes = manager.LoadSlideStrokes(slideIndex); + + // 如果从管理器加载失败,尝试从备份恢复 + if (strokes == null || strokes.Count == 0) + { + if (!string.IsNullOrEmpty(_currentActivePresentationId)) + { + strokes = RestoreStrokeFromBackup(_currentActivePresentationId, slideIndex); + } + } + + return strokes ?? new StrokeCollection(); } } catch (Exception ex) { LogHelper.WriteLogToFile($"加载页面墨迹失败: {ex}", LogHelper.LogType.Error); + + // 尝试从备份恢复 + if (!string.IsNullOrEmpty(_currentActivePresentationId)) + { + return RestoreStrokeFromBackup(_currentActivePresentationId, slideIndex); + } } } @@ -511,6 +551,12 @@ namespace Ink_Canvas.Helpers } _presentationInfos.Remove(id); + // 清理备份数据 + if (_strokeBackups.ContainsKey(id)) + { + _strokeBackups.Remove(id); + } + LogHelper.WriteLogToFile($"已清理非活跃演示文稿: {id}", LogHelper.LogType.Trace); } } @@ -520,6 +566,98 @@ namespace Ink_Canvas.Helpers } } } + + /// + /// 创建墨迹备份 + /// + private void CreateStrokeBackup(string presentationId, int slideIndex, StrokeCollection strokes) + { + try + { + if (strokes == null || strokes.Count == 0) return; + + if (!_strokeBackups.ContainsKey(presentationId)) + { + _strokeBackups[presentationId] = new Dictionary(); + } + + // 释放旧的备份 + if (_strokeBackups[presentationId].ContainsKey(slideIndex)) + { + _strokeBackups[presentationId][slideIndex] = null; + } + + // 创建新的备份 + _strokeBackups[presentationId][slideIndex] = strokes.Clone(); + + LogHelper.WriteLogToFile($"已创建第{slideIndex}页墨迹备份", LogHelper.LogType.Trace); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"创建墨迹备份失败: {ex}", LogHelper.LogType.Error); + } + } + + /// + /// 从备份恢复墨迹 + /// + private StrokeCollection RestoreStrokeFromBackup(string presentationId, int slideIndex) + { + try + { + if (_strokeBackups.ContainsKey(presentationId) && + _strokeBackups[presentationId].ContainsKey(slideIndex)) + { + var backup = _strokeBackups[presentationId][slideIndex]; + if (backup != null) + { + LogHelper.WriteLogToFile($"从备份恢复第{slideIndex}页墨迹", LogHelper.LogType.Trace); + return backup.Clone(); + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"从备份恢复墨迹失败: {ex}", LogHelper.LogType.Error); + } + + return new StrokeCollection(); + } + + /// + /// 检查并执行定期备份 + /// + private void CheckAndPerformBackup() + { + try + { + var now = DateTime.Now; + + // 检查是否需要执行备份 + if (now - _lastBackupTime < TimeSpan.FromMinutes(BackupIntervalMinutes)) + { + return; + } + + // 备份当前活跃演示文稿的所有墨迹 + if (!string.IsNullOrEmpty(_currentActivePresentationId) && + _presentationManagers.ContainsKey(_currentActivePresentationId)) + { + var manager = _presentationManagers[_currentActivePresentationId]; + if (manager != null) + { + // 这里可以添加更详细的备份逻辑 + LogHelper.WriteLogToFile($"执行定期墨迹备份: {_currentActivePresentationId}", LogHelper.LogType.Trace); + } + } + + _lastBackupTime = now; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"定期备份检查失败: {ex}", LogHelper.LogType.Error); + } + } #endregion #region Private Methods @@ -637,12 +775,24 @@ namespace Ink_Canvas.Helpers { lock (_lockObject) { + // 释放所有管理器 foreach (var manager in _presentationManagers.Values) { manager?.Dispose(); } _presentationManagers.Clear(); _presentationInfos.Clear(); + + // 清理备份数据 + foreach (var backupDict in _strokeBackups.Values) + { + foreach (var backup in backupDict.Values) + { + backup?.Clear(); + } + backupDict.Clear(); + } + _strokeBackups.Clear(); } _disposed = true; } diff --git a/Ink Canvas/Helpers/PPTInkManager.cs b/Ink Canvas/Helpers/PPTInkManager.cs index 93b0a4dc..7e8afb22 100644 --- a/Ink Canvas/Helpers/PPTInkManager.cs +++ b/Ink Canvas/Helpers/PPTInkManager.cs @@ -35,6 +35,12 @@ namespace Ink_Canvas.Helpers private DateTime _lastSwitchTime = DateTime.MinValue; private int _lastSwitchSlideIndex = -1; private const int MinSwitchIntervalMs = 100; // 最小切换间隔100毫秒 + + // 内存管理相关字段 + private long _totalMemoryUsage = 0; + private const long MaxMemoryUsageBytes = 100 * 1024 * 1024; // 100MB限制 + private DateTime _lastMemoryCleanup = DateTime.MinValue; + private const int MemoryCleanupIntervalMinutes = 5; // 5分钟清理一次 #endregion #region Constructor @@ -125,17 +131,29 @@ namespace Ink_Canvas.Helpers if (slideIndex < _memoryStreams.Length) { + // 先释放旧的内存流,防止内存泄漏 + try + { + _memoryStreams[slideIndex]?.Dispose(); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"释放旧内存流失败: {ex}", LogHelper.LogType.Warning); + } + + // 创建新的内存流 var ms = new MemoryStream(); strokes.Save(ms); ms.Position = 0; - - // 释放旧的内存流 - _memoryStreams[slideIndex]?.Dispose(); _memoryStreams[slideIndex] = ms; if (ms.Length > 0) { + LogHelper.WriteLogToFile($"已保存第{slideIndex}页墨迹,大小: {ms.Length} bytes", LogHelper.LogType.Trace); } + + // 检查内存使用情况 + CheckAndPerformMemoryCleanup(); } } catch (Exception ex) @@ -158,12 +176,20 @@ namespace Ink_Canvas.Helpers { if (slideIndex < _memoryStreams.Length) { + // 先释放旧的内存流,防止内存泄漏 + try + { + _memoryStreams[slideIndex]?.Dispose(); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"释放旧内存流失败: {ex}", LogHelper.LogType.Warning); + } + + // 创建新的内存流 var ms = new MemoryStream(); strokes.Save(ms); ms.Position = 0; - - // 释放旧的内存流 - _memoryStreams[slideIndex]?.Dispose(); _memoryStreams[slideIndex] = ms; LogHelper.WriteLogToFile($"已强制保存第{slideIndex}页墨迹,大小: {ms.Length} bytes", LogHelper.LogType.Trace); @@ -388,13 +414,30 @@ namespace Ink_Canvas.Helpers { try { - for (int i = 0; i < _memoryStreams.Length; i++) + // 安全释放所有内存流 + if (_memoryStreams != null) { - _memoryStreams[i]?.Dispose(); - _memoryStreams[i] = null; + for (int i = 0; i < _memoryStreams.Length; i++) + { + try + { + _memoryStreams[i]?.Dispose(); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"释放内存流{i}失败: {ex}", LogHelper.LogType.Warning); + } + finally + { + _memoryStreams[i] = null; + } + } + + // 重新初始化数组 + _memoryStreams = new MemoryStream[_maxSlides + 2]; } - CurrentStrokes.Clear(); + CurrentStrokes?.Clear(); LogHelper.WriteLogToFile("已清除所有墨迹", LogHelper.LogType.Trace); } catch (Exception ex) @@ -447,6 +490,110 @@ namespace Ink_Canvas.Helpers _lastSwitchSlideIndex = -1; } } + + /// + /// 检查并执行内存清理 + /// + private void CheckAndPerformMemoryCleanup() + { + try + { + var now = DateTime.Now; + + // 检查是否需要执行内存清理 + if (now - _lastMemoryCleanup < TimeSpan.FromMinutes(MemoryCleanupIntervalMinutes)) + { + return; + } + + // 计算当前内存使用量 + long currentMemoryUsage = 0; + if (_memoryStreams != null) + { + for (int i = 0; i < _memoryStreams.Length; i++) + { + if (_memoryStreams[i] != null) + { + currentMemoryUsage += _memoryStreams[i].Length; + } + } + } + + _totalMemoryUsage = currentMemoryUsage; + + // 如果内存使用量超过限制,执行清理 + if (currentMemoryUsage > MaxMemoryUsageBytes) + { + LogHelper.WriteLogToFile($"内存使用量超限 ({currentMemoryUsage / 1024 / 1024}MB),开始清理", LogHelper.LogType.Warning); + + // 清理非当前页面的墨迹 + CleanupInactiveSlideStrokes(); + + _lastMemoryCleanup = now; + LogHelper.WriteLogToFile($"内存清理完成,当前使用量: {_totalMemoryUsage / 1024 / 1024}MB", LogHelper.LogType.Trace); + } + else + { + _lastMemoryCleanup = now; + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"内存清理检查失败: {ex}", LogHelper.LogType.Error); + } + } + + /// + /// 清理非活跃页面的墨迹 + /// + private void CleanupInactiveSlideStrokes() + { + try + { + if (_memoryStreams == null) return; + + int cleanedCount = 0; + long freedMemory = 0; + + for (int i = 0; i < _memoryStreams.Length; i++) + { + // 保留当前锁定页面和最近访问的页面 + if (i == _lockedSlideIndex || i == _lastSwitchSlideIndex) + { + continue; + } + + if (_memoryStreams[i] != null) + { + long memorySize = _memoryStreams[i].Length; + + try + { + _memoryStreams[i].Dispose(); + freedMemory += memorySize; + cleanedCount++; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"清理页面{i}墨迹失败: {ex}", LogHelper.LogType.Warning); + } + finally + { + _memoryStreams[i] = null; + } + } + } + + if (cleanedCount > 0) + { + LogHelper.WriteLogToFile($"已清理{cleanedCount}个页面的墨迹,释放内存: {freedMemory / 1024}KB", LogHelper.LogType.Trace); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"清理非活跃页面墨迹失败: {ex}", LogHelper.LogType.Error); + } + } #endregion #region Private Methods diff --git a/Ink Canvas/Helpers/PPTManager.cs b/Ink Canvas/Helpers/PPTManager.cs index ecca9519..c638914b 100644 --- a/Ink Canvas/Helpers/PPTManager.cs +++ b/Ink Canvas/Helpers/PPTManager.cs @@ -411,63 +411,30 @@ namespace Ink_Canvas.Helpers catch (COMException comEx) { var hr = (uint)comEx.HResult; + LogHelper.WriteLogToFile($"取消PPT事件注册时COM异常: {comEx.Message} (HR: 0x{hr:X8})", LogHelper.LogType.Warning); } catch (InvalidCastException) { // COM对象类型转换失败,通常是因为对象已经被释放 LogHelper.WriteLogToFile("PPT COM对象已被释放,跳过事件注册取消", LogHelper.LogType.Trace); } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"取消PPT事件注册时发生异常: {ex}", LogHelper.LogType.Warning); + } }, DispatcherPriority.Normal, CancellationToken.None, TimeSpan.FromSeconds(1)); } } - catch (Exception) + catch (Exception ex) { + LogHelper.WriteLogToFile($"取消PPT事件注册失败: {ex}", LogHelper.LogType.Warning); } - // 释放COM对象 - try - { - if (Marshal.IsComObject(CurrentSlide)) - { - Marshal.ReleaseComObject(CurrentSlide); - } - } - catch (Exception) - { - } - - try - { - if (Marshal.IsComObject(CurrentSlides)) - { - Marshal.ReleaseComObject(CurrentSlides); - } - } - catch (Exception) - { - } - - try - { - if (Marshal.IsComObject(CurrentPresentation)) - { - Marshal.ReleaseComObject(CurrentPresentation); - } - } - catch (Exception) - { - } - - try - { - if (Marshal.IsComObject(PPTApplication)) - { - Marshal.ReleaseComObject(PPTApplication); - } - } - catch (Exception) - { - } + // 安全释放COM对象 + SafeReleaseComObject(CurrentSlide, "CurrentSlide"); + SafeReleaseComObject(CurrentSlides, "CurrentSlides"); + SafeReleaseComObject(CurrentPresentation, "CurrentPresentation"); + SafeReleaseComObject(PPTApplication, "PPTApplication"); // 清理引用 PPTApplication = null; @@ -491,6 +458,30 @@ namespace Ink_Canvas.Helpers } } + /// + /// 安全释放COM对象 + /// + private void SafeReleaseComObject(object comObject, string objectName) + { + try + { + if (comObject != null && Marshal.IsComObject(comObject)) + { + int refCount = Marshal.ReleaseComObject(comObject); + LogHelper.WriteLogToFile($"已释放COM对象 {objectName},引用计数: {refCount}", LogHelper.LogType.Trace); + } + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + LogHelper.WriteLogToFile($"释放COM对象 {objectName} 时COM异常: {comEx.Message} (HR: 0x{hr:X8})", LogHelper.LogType.Warning); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"释放COM对象 {objectName} 时发生异常: {ex}", LogHelper.LogType.Warning); + } + } + private void UpdateCurrentPresentationInfo() { try