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)
{