diff --git a/Ink Canvas/App.xaml.cs b/Ink Canvas/App.xaml.cs index 33820dd5..fbb53b9c 100644 --- a/Ink Canvas/App.xaml.cs +++ b/Ink Canvas/App.xaml.cs @@ -678,7 +678,7 @@ namespace Ink_Canvas Application.Current.Dispatcher.Invoke(() => { }, DispatcherPriority.Render); } - System.Threading.Thread.Sleep(500); + await Task.Delay(500); RootPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase; LogHelper.NewLog(string.Format("Ink Canvas Starting (Version: {0})", Assembly.GetExecutingAssembly().GetName().Version)); @@ -1011,7 +1011,7 @@ namespace Ink_Canvas mutex = new Mutex(true, mutexName, out bool tempRet); // 额外等待一小段时间确保更新进程完全退出 - Thread.Sleep(1000); + await Task.Delay(1000); LogHelper.WriteLogToFile("App | 特殊模式等待完成,继续启动"); } @@ -1124,7 +1124,7 @@ namespace Ink_Canvas } // 心跳相关 - private static Timer heartbeatTimer; + private static DispatcherTimer heartbeatTimer; private static DateTime lastHeartbeat = DateTime.Now; private static Timer watchdogTimer; private static bool isStartupComplete = false; @@ -1134,7 +1134,13 @@ namespace Ink_Canvas private void StartHeartbeatMonitor() { - heartbeatTimer = new Timer(_ => lastHeartbeat = DateTime.Now, null, 0, 1000); + heartbeatTimer = new DispatcherTimer + { + Interval = TimeSpan.FromSeconds(1) + }; + heartbeatTimer.Tick += (_, __) => lastHeartbeat = DateTime.Now; + heartbeatTimer.Start(); + watchdogTimer = new Timer(_ => { if (!isStartupComplete && appStartupStartTime != DateTime.MinValue) diff --git a/Ink Canvas/Helpers/AutoUpdateHelper.cs b/Ink Canvas/Helpers/AutoUpdateHelper.cs index 788aed3a..ab24bcdb 100644 --- a/Ink Canvas/Helpers/AutoUpdateHelper.cs +++ b/Ink Canvas/Helpers/AutoUpdateHelper.cs @@ -1431,7 +1431,7 @@ namespace Ink_Canvas.Helpers try { LogHelper.WriteLogToFile($"AutoUpdate | 开始解压ZIP文件到: {extractPath}"); - ZipFile.ExtractToDirectory(zipFilePath, extractPath); + SafeZipExtractor.ExtractZipSafely(zipFilePath, extractPath, overwrite: true); LogHelper.WriteLogToFile("AutoUpdate | ZIP文件解压完成"); } catch (Exception ex) diff --git a/Ink Canvas/Helpers/HardwareAcceleratedInkProcessor.cs b/Ink Canvas/Helpers/HardwareAcceleratedInkProcessor.cs index 5342b8f6..aaf41cf3 100644 --- a/Ink Canvas/Helpers/HardwareAcceleratedInkProcessor.cs +++ b/Ink Canvas/Helpers/HardwareAcceleratedInkProcessor.cs @@ -186,11 +186,11 @@ namespace Ink_Canvas.Helpers } /// - /// 释放GPU资源 + /// 释放GPU相关资源标记 /// public void Dispose() { - _renderTarget?.Clear(); + _isInitialized = false; } } diff --git a/Ink Canvas/Helpers/MultiTouchInput.cs b/Ink Canvas/Helpers/MultiTouchInput.cs index 7b89b243..1fea525e 100644 --- a/Ink Canvas/Helpers/MultiTouchInput.cs +++ b/Ink Canvas/Helpers/MultiTouchInput.cs @@ -209,9 +209,5 @@ namespace Ink_Canvas.Helpers private readonly DrawingAttributes _drawingAttributes; - public static implicit operator Stroke(StrokeVisual v) - { - throw new NotImplementedException(); - } } } diff --git a/Ink Canvas/Helpers/SafeZipExtractor.cs b/Ink Canvas/Helpers/SafeZipExtractor.cs new file mode 100644 index 00000000..706bdff4 --- /dev/null +++ b/Ink Canvas/Helpers/SafeZipExtractor.cs @@ -0,0 +1,75 @@ +using System; +using System.IO; +using System.IO.Compression; + +namespace Ink_Canvas.Helpers +{ + public static class SafeZipExtractor + { + /// ZIP 文件路径 + /// 解压目标目录 + /// 是否覆盖已存在文件 + public static void ExtractZipSafely(string zipFilePath, string extractPath, bool overwrite = true) + { + if (string.IsNullOrWhiteSpace(zipFilePath)) + throw new ArgumentNullException(nameof(zipFilePath)); + if (string.IsNullOrWhiteSpace(extractPath)) + throw new ArgumentNullException(nameof(extractPath)); + + var fullExtractPath = Path.GetFullPath(extractPath); + Directory.CreateDirectory(fullExtractPath); + + using (var zip = ZipFile.OpenRead(zipFilePath)) + { + foreach (var entry in zip.Entries) + { + // 跳过空条目 + if (string.IsNullOrEmpty(entry.FullName)) + continue; + + // 防止绝对路径和盘符前缀 + if (Path.IsPathRooted(entry.FullName)) + continue; + + // 统一路径分隔符 + var normalized = entry.FullName.Replace('/', Path.DirectorySeparatorChar); + + // 拒绝包含 .. 的路径,防止目录穿越 + if (normalized.Contains(".." + Path.DirectorySeparatorChar) || + normalized.StartsWith(".." + Path.DirectorySeparatorChar, StringComparison.Ordinal)) + { + continue; + } + + var destinationPath = Path.GetFullPath( + Path.Combine(fullExtractPath, normalized)); + + // 再次确认仍然在目标目录下 + if (!destinationPath.StartsWith(fullExtractPath, StringComparison.OrdinalIgnoreCase)) + continue; + + // 目录条目 + if (entry.FullName.EndsWith("/", StringComparison.Ordinal) || + entry.FullName.EndsWith("\\", StringComparison.Ordinal)) + { + Directory.CreateDirectory(destinationPath); + continue; + } + + Directory.CreateDirectory(Path.GetDirectoryName(destinationPath) ?? fullExtractPath); + + if (!overwrite && File.Exists(destinationPath)) + continue; + + using (var input = entry.Open()) + using (var output = File.Create(destinationPath)) + { + input.CopyTo(output); + } + } + } + } + } +} + + diff --git a/Ink Canvas/Helpers/TimeMachine.cs b/Ink Canvas/Helpers/TimeMachine.cs index fae069be..58b9c546 100644 --- a/Ink Canvas/Helpers/TimeMachine.cs +++ b/Ink Canvas/Helpers/TimeMachine.cs @@ -92,6 +92,11 @@ namespace Ink_Canvas.Helpers public TimeMachineHistory Undo() { + if (_currentIndex < 0 || _currentIndex >= _currentStrokeHistory.Count) + { + return null; + } + var item = _currentStrokeHistory[_currentIndex]; item.StrokeHasBeenCleared = !item.StrokeHasBeenCleared; _currentIndex--; @@ -102,6 +107,11 @@ namespace Ink_Canvas.Helpers public TimeMachineHistory Redo() { + if (_currentStrokeHistory.Count == 0 || _currentIndex >= _currentStrokeHistory.Count - 1) + { + return null; + } + var item = _currentStrokeHistory[++_currentIndex]; item.StrokeHasBeenCleared = !item.StrokeHasBeenCleared; NotifyUndoRedoState(); diff --git a/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs b/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs index 2ef7f379..9ecf4d1c 100644 --- a/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs +++ b/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs @@ -730,7 +730,7 @@ namespace Ink_Canvas try { // 解压ZIP文件 - ZipFile.ExtractToDirectory(zipFilePath, tempDir); + SafeZipExtractor.ExtractZipSafely(zipFilePath, tempDir, overwrite: true); // 读取元数据文件 string metadataFile = Path.Combine(tempDir, "metadata.txt");