From 2b3d1c11deb21f8602554f8024fb52742ddbf9f5 Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Sat, 21 Feb 2026 17:44:30 +0800 Subject: [PATCH] =?UTF-8?q?improve:PPT=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/Helpers/HashHelper.cs | 35 +++++++ Ink Canvas/Helpers/PPTInkManager.cs | 44 +++++---- Ink Canvas/Helpers/ROTPPTManager.cs | 8 +- Ink Canvas/MainWindow_cs/MW_PPT.cs | 94 ++++++++++--------- .../MainWindow_cs/MW_Save&OpenStrokes.cs | 4 +- 5 files changed, 118 insertions(+), 67 deletions(-) create mode 100644 Ink Canvas/Helpers/HashHelper.cs diff --git a/Ink Canvas/Helpers/HashHelper.cs b/Ink Canvas/Helpers/HashHelper.cs new file mode 100644 index 00000000..1dca67bc --- /dev/null +++ b/Ink Canvas/Helpers/HashHelper.cs @@ -0,0 +1,35 @@ +using System; +using System.Security.Cryptography; +using System.Text; + +namespace Ink_Canvas.Helpers +{ + /// + /// 哈希计算辅助类,用于路径/标识等短字符串的 MD5 前缀哈希。 + /// + internal static class HashHelper + { + /// + /// 对给定路径字符串计算 MD5 哈希,返回前 8 位十六进制字符串。 + /// + /// 文件路径或任意字符串 + /// 8 位十六进制字符串;异常或空输入时返回 "error" 或 "unknown" + public static string GetFileHash(string filePath) + { + try + { + if (string.IsNullOrEmpty(filePath)) return "unknown"; + using (var md5 = MD5.Create()) + { + byte[] hash = md5.ComputeHash(Encoding.UTF8.GetBytes(filePath)); + return BitConverter.ToString(hash).Replace("-", "").Substring(0, 8); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"计算文件哈希失败: {ex}", LogHelper.LogType.Error); + return "error"; + } + } + } +} diff --git a/Ink Canvas/Helpers/PPTInkManager.cs b/Ink Canvas/Helpers/PPTInkManager.cs index 32ddb053..118832b8 100644 --- a/Ink Canvas/Helpers/PPTInkManager.cs +++ b/Ink Canvas/Helpers/PPTInkManager.cs @@ -53,6 +53,13 @@ namespace Ink_Canvas.Helpers private void InitializeMemoryStreams(int capacity) { + if (_memoryStreams != null) + { + for (int i = 0; i < _memoryStreams.Length; i++) + { + try { _memoryStreams[i]?.Dispose(); } catch (Exception ex) { LogHelper.WriteLogToFile($"InitializeMemoryStreams 释放内存流 {i} 失败: {ex}", LogHelper.LogType.Warning); } + } + } _memoryStreams = new MemoryStream[Math.Max(2, capacity)]; } #endregion @@ -64,6 +71,7 @@ namespace Ink_Canvas.Helpers /// public void InitializePresentation(Presentation presentation) { + ThrowIfDisposed(); if (presentation == null) return; lock (_lockObject) @@ -109,6 +117,7 @@ namespace Ink_Canvas.Helpers /// public void SaveCurrentSlideStrokes(int slideIndex, StrokeCollection strokes) { + ThrowIfDisposed(); if (slideIndex <= 0 || strokes == null) return; lock (_lockObject) @@ -133,6 +142,7 @@ namespace Ink_Canvas.Helpers /// public void ForceSaveSlideStrokes(int slideIndex, StrokeCollection strokes) { + ThrowIfDisposed(); if (slideIndex <= 0 || strokes == null) return; lock (_lockObject) @@ -155,6 +165,7 @@ namespace Ink_Canvas.Helpers /// public StrokeCollection LoadSlideStrokes(int slideIndex) { + ThrowIfDisposed(); if (slideIndex <= 0) return new StrokeCollection(); lock (_lockObject) @@ -181,6 +192,7 @@ namespace Ink_Canvas.Helpers /// public StrokeCollection SwitchToSlide(int slideIndex, StrokeCollection currentStrokes = null) { + ThrowIfDisposed(); lock (_lockObject) { try @@ -213,6 +225,7 @@ namespace Ink_Canvas.Helpers /// 当前播放的页码,如果提供则使用此值保存位置,否则使用_lockedSlideIndex public void SaveAllStrokesToFile(Presentation presentation, int currentSlideIndex = -1) { + ThrowIfDisposed(); if (!IsAutoSaveEnabled || string.IsNullOrEmpty(AutoSaveLocation) || presentation == null) return; lock (_lockObject) @@ -277,6 +290,7 @@ namespace Ink_Canvas.Helpers /// public void LoadSavedStrokes() { + ThrowIfDisposed(); if (!IsAutoSaveEnabled || string.IsNullOrEmpty(AutoSaveLocation)) return; lock (_lockObject) @@ -325,6 +339,7 @@ namespace Ink_Canvas.Helpers /// public void ClearAllStrokes() { + ThrowIfDisposed(); lock (_lockObject) { ClearAllStrokesInternal(); @@ -333,12 +348,14 @@ namespace Ink_Canvas.Helpers public void LockInkForSlide(int slideIndex) { + ThrowIfDisposed(); _inkLockUntil = DateTime.Now.AddMilliseconds(InkLockMilliseconds); _lockedSlideIndex = slideIndex; } public bool CanWriteInk(int currentSlideIndex) { + ThrowIfDisposed(); if (DateTime.Now >= _inkLockUntil) return true; if (currentSlideIndex == _lockedSlideIndex) return true; if (DateTime.Now - (_inkLockUntil.AddMilliseconds(-InkLockMilliseconds)) < TimeSpan.FromMilliseconds(50)) return true; @@ -347,6 +364,7 @@ namespace Ink_Canvas.Helpers public void ResetLockState() { + ThrowIfDisposed(); lock (_lockObject) { _inkLockUntil = DateTime.MinValue; @@ -446,7 +464,7 @@ namespace Ink_Canvas.Helpers try { string path = presentation.FullName; - string hash = GetFileHash(path); + string hash = HashHelper.GetFileHash(path); return $"{presentation.Name}_{presentation.Slides.Count}_{hash}"; } catch (Exception ex) @@ -456,24 +474,6 @@ namespace Ink_Canvas.Helpers } } - private static string GetFileHash(string filePath) - { - try - { - if (string.IsNullOrEmpty(filePath)) return "unknown"; - using (var md5 = MD5.Create()) - { - byte[] hash = md5.ComputeHash(Encoding.UTF8.GetBytes(filePath)); - return BitConverter.ToString(hash).Replace("-", "").Substring(0, 8); - } - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"计算文件哈希失败: {ex}", LogHelper.LogType.Error); - return "error"; - } - } - private string GetPresentationFolderPath() { return Path.Combine(AutoSaveLocation, "Auto Saved - Presentations", _currentPresentationId); @@ -488,6 +488,12 @@ namespace Ink_Canvas.Helpers if (_disposed) return; lock (_lockObject) { ClearAllStrokesInternal(); } _disposed = true; + GC.SuppressFinalize(this); + } + + private void ThrowIfDisposed() + { + if (_disposed) throw new ObjectDisposedException(nameof(PPTInkManager)); } #endregion diff --git a/Ink Canvas/Helpers/ROTPPTManager.cs b/Ink Canvas/Helpers/ROTPPTManager.cs index 8873b80e..e57307a0 100644 --- a/Ink Canvas/Helpers/ROTPPTManager.cs +++ b/Ink Canvas/Helpers/ROTPPTManager.cs @@ -205,14 +205,14 @@ namespace Ink_Canvas.Helpers public void StopMonitoring() { _unifiedRotTimer?.Stop(); - DisconnectFromPPT(); + DisconnectFromPPT(restartMonitoring: false); } public void ReloadConnection() { if (_disposed) return; LogHelper.WriteLogToFile("[ROT] 执行热重载:强制断开并重新连接", LogHelper.LogType.Event); - DisconnectFromPPT(); + DisconnectFromPPT(restartMonitoring: true); } #endregion @@ -371,7 +371,7 @@ namespace Ink_Canvas.Helpers } } - private void DisconnectFromPPT() + private void DisconnectFromPPT(bool restartMonitoring = true) { object appToRelease = null; try @@ -461,7 +461,7 @@ namespace Ink_Canvas.Helpers lock (_lockObject) { _isModuleUnloading = false; - if (!_disposed) + if (restartMonitoring && !_disposed) _unifiedRotTimer?.Start(); } } diff --git a/Ink Canvas/MainWindow_cs/MW_PPT.cs b/Ink Canvas/MainWindow_cs/MW_PPT.cs index 557c7f4c..830d92dc 100644 --- a/Ink Canvas/MainWindow_cs/MW_PPT.cs +++ b/Ink Canvas/MainWindow_cs/MW_PPT.cs @@ -663,6 +663,17 @@ namespace Ink_Canvas } } + private string GetPresentationStrokeFolderPath(Presentation presentation, string presentationName, int totalSlides) + { + string basePath = Path.Combine(Settings.Automation.AutoSavedStrokesLocation, "Auto Saved - Presentations"); + if (presentation != null && !string.IsNullOrEmpty(presentation.FullName)) + { + string hash = HashHelper.GetFileHash(presentation.FullName); + return Path.Combine(basePath, $"{presentation.Name}_{presentation.Slides.Count}_{hash}"); + } + return Path.Combine(basePath, $"{presentationName ?? ""}_{totalSlides}"); + } + private void OnPPTPresentationClose(Presentation pres) { try @@ -757,15 +768,16 @@ namespace Ink_Canvas _currentSlideShowPosition = currentSlide; _previousSlideID = currentSlide; - foreach (var stream in _memoryStreams.Values) + lock (_memoryStreams) { - stream?.Dispose(); + foreach (var stream in _memoryStreams.Values) + stream?.Dispose(); + _memoryStreams.Clear(); } - _memoryStreams.Clear(); if (Settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint && !string.IsNullOrEmpty(presentationName)) { - string strokePath = Path.Combine(Settings.Automation.AutoSavedStrokesLocation, "Auto Saved - Presentations", presentationName + "_" + totalSlides); + string strokePath = GetPresentationStrokeFolderPath(activePresentation, presentationName, totalSlides); if (Directory.Exists(strokePath)) { await Task.Run(() => @@ -955,9 +967,12 @@ namespace Ink_Canvas var ms = new MemoryStream(); inkCanvas.Strokes.Save(ms); ms.Position = 0; - if (_memoryStreams.ContainsKey(prev)) - _memoryStreams[prev]?.Dispose(); - _memoryStreams[prev] = ms; + lock (_memoryStreams) + { + if (_memoryStreams.ContainsKey(prev)) + _memoryStreams[prev]?.Dispose(); + _memoryStreams[prev] = ms; + } ClearStrokes(true); timeMachine.ClearStrokeHistory(); @@ -966,15 +981,20 @@ namespace Ink_Canvas _singlePPTInkManager?.LockInkForSlide(currentSlide); _pptUIManager?.UpdateCurrentSlideNumber(currentSlide, totalSlides); - if (_memoryStreams.ContainsKey(currentSlide) && _memoryStreams[currentSlide] != null) + byte[] bytesToLoad = null; + lock (_memoryStreams) + { + if (_memoryStreams.ContainsKey(currentSlide) && _memoryStreams[currentSlide] != null) + bytesToLoad = _memoryStreams[currentSlide].ToArray(); + } + if (bytesToLoad != null) { - byte[] bytes = _memoryStreams[currentSlide].ToArray(); int loadingPage = currentSlide; Task.Run(() => { try { - return new StrokeCollection(new MemoryStream(bytes)); + return new StrokeCollection(new MemoryStream(bytesToLoad)); } catch (Exception ex) { @@ -1041,9 +1061,12 @@ namespace Ink_Canvas var ms = new MemoryStream(); inkCanvas.Strokes.Save(ms); ms.Position = 0; - if (_memoryStreams.ContainsKey(currentPage)) - _memoryStreams[currentPage]?.Dispose(); - _memoryStreams[currentPage] = ms; + lock (_memoryStreams) + { + if (_memoryStreams.ContainsKey(currentPage)) + _memoryStreams[currentPage]?.Dispose(); + _memoryStreams[currentPage] = ms; + } } }); @@ -1052,13 +1075,13 @@ namespace Ink_Canvas if (Settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint && !string.IsNullOrEmpty(presentationNameForSave) && totalSlidesForSave > 0) { + string folderPathForSave = GetPresentationStrokeFolderPath(pres, presentationNameForSave, totalSlidesForSave); await Task.Run(() => { try { - string folderPath = Path.Combine(Settings.Automation.AutoSavedStrokesLocation, "Auto Saved - Presentations", presentationNameForSave + "_" + totalSlidesForSave); - if (!Directory.Exists(folderPath)) - Directory.CreateDirectory(folderPath); + if (!Directory.Exists(folderPathForSave)) + Directory.CreateDirectory(folderPathForSave); lock (_memoryStreams) { @@ -1069,7 +1092,7 @@ namespace Ink_Canvas try { byte[] allBytes = value.ToArray(); - string filePath = Path.Combine(folderPath, i.ToString("0000") + ".icstk"); + string filePath = Path.Combine(folderPathForSave, i.ToString("0000") + ".icstk"); if (allBytes.Length > 8) File.WriteAllBytes(filePath, allBytes); else if (File.Exists(filePath)) @@ -1231,10 +1254,7 @@ namespace Ink_Canvas { if (pres == null) return; - var presentationPath = pres.FullName; - var fileHash = GetFileHash(presentationPath); - var folderName = pres.Name + "_" + pres.Slides.Count + "_" + fileHash; - var folderPath = Path.Combine(Settings.Automation.AutoSavedStrokesLocation, "Auto Saved - Presentations", folderName); + var folderPath = GetPresentationStrokeFolderPath(pres, pres.Name, pres.Slides.Count); var positionFile = Path.Combine(folderPath, "Position"); if (!File.Exists(positionFile)) return; @@ -1384,12 +1404,20 @@ namespace Ink_Canvas ClearStrokes(true); timeMachine.ClearStrokeHistory(); - if (_memoryStreams.ContainsKey(slideIndex) && _memoryStreams[slideIndex] != null) + byte[] bytes = null; + lock (_memoryStreams) + { + if (_memoryStreams.TryGetValue(slideIndex, out var ms) && ms != null && ms.Length > 0) + { + ms.Position = 0; + bytes = ms.ToArray(); + } + } + if (bytes != null) { try { - _memoryStreams[slideIndex].Position = 0; - inkCanvas.Strokes.Add(new StrokeCollection(_memoryStreams[slideIndex])); + inkCanvas.Strokes.Add(new StrokeCollection(new MemoryStream(bytes))); } catch (Exception ex) { @@ -1457,24 +1485,6 @@ namespace Ink_Canvas } } - private string GetFileHash(string filePath) - { - try - { - if (string.IsNullOrEmpty(filePath)) return "unknown"; - - using (var md5 = MD5.Create()) - { - byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(filePath)); - return BitConverter.ToString(hashBytes).Replace("-", "").Substring(0, 8); - } - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"计算文件哈希值失败: {ex}", LogHelper.LogType.Error); - return "error"; - } - } #endregion private void BtnCheckPPT_Click(object sender, RoutedEventArgs e) diff --git a/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs b/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs index 9ecf4d1c..a54ef935 100644 --- a/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs +++ b/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs @@ -842,8 +842,8 @@ namespace Ink_Canvas if (!string.IsNullOrEmpty(savedPptPath) && !string.IsNullOrEmpty(currentPptPath)) { // 使用文件路径哈希值进行比较,避免路径格式差异 - string savedHash = GetFileHash(savedPptPath); - string currentHash = GetFileHash(currentPptPath); + string savedHash = HashHelper.GetFileHash(savedPptPath); + string currentHash = HashHelper.GetFileHash(currentPptPath); if (savedHash != currentHash) {