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");