From 5a387eef968c3948b16627afea4abfc9c1940292 Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Fri, 1 May 2026 18:55:03 +0800 Subject: [PATCH] =?UTF-8?q?add:=E6=9B=B4=E6=96=B0=E9=9D=A2=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/Helpers/AutoUpdateHelper.cs | 109 ++- Ink Canvas/MainWindow.xaml | 14 +- Ink Canvas/MainWindow.xaml.cs | 133 +--- Ink Canvas/MainWindow_cs/MW_Timer.cs | 6 +- Ink Canvas/Windows/HasNewUpdateWindow.xaml | 346 -------- Ink Canvas/Windows/HasNewUpdateWindow.xaml.cs | 513 ------------ Ink Canvas/Windows/HistoryRollbackWindow.xaml | 182 ----- .../Windows/HistoryRollbackWindow.xaml.cs | 370 --------- .../SettingsViews/Pages/UpdatePage.xaml | 394 +++++++--- .../SettingsViews/Pages/UpdatePage.xaml.cs | 740 ++++++++++++++---- 10 files changed, 1011 insertions(+), 1796 deletions(-) delete mode 100644 Ink Canvas/Windows/HasNewUpdateWindow.xaml delete mode 100644 Ink Canvas/Windows/HasNewUpdateWindow.xaml.cs delete mode 100644 Ink Canvas/Windows/HistoryRollbackWindow.xaml delete mode 100644 Ink Canvas/Windows/HistoryRollbackWindow.xaml.cs diff --git a/Ink Canvas/Helpers/AutoUpdateHelper.cs b/Ink Canvas/Helpers/AutoUpdateHelper.cs index 9ed0dc1a..72e4c559 100644 --- a/Ink Canvas/Helpers/AutoUpdateHelper.cs +++ b/Ink Canvas/Helpers/AutoUpdateHelper.cs @@ -27,6 +27,37 @@ namespace Ink_Canvas.Helpers private static readonly string updatesFolderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "AutoUpdate"); private static string statusFilePath; + // 全局下载取消令牌;UI 通过 RequestCancelDownload 取消当前下载 + private static CancellationTokenSource _activeDownloadCts; + private static readonly object _activeDownloadLock = new object(); + + public static void RequestCancelDownload() + { + lock (_activeDownloadLock) + { + try { _activeDownloadCts?.Cancel(); } catch { } + } + } + + private static CancellationTokenSource BeginDownloadSession() + { + lock (_activeDownloadLock) + { + try { _activeDownloadCts?.Cancel(); } catch { } + _activeDownloadCts = new CancellationTokenSource(); + return _activeDownloadCts; + } + } + + private static void EndDownloadSession(CancellationTokenSource cts) + { + lock (_activeDownloadLock) + { + if (ReferenceEquals(_activeDownloadCts, cts)) _activeDownloadCts = null; + } + try { cts?.Dispose(); } catch { } + } + public static bool IsX64UpdatePackageSelected() { try @@ -383,6 +414,8 @@ namespace Ink_Canvas.Helpers // 获取所有可用线路组,按延迟排序 public static async Task> GetAvailableLineGroupsOrdered(UpdateChannel channel) { + var cached = TryGetCachedOrderedGroups(channel); + if (cached != null) return cached; var groups = ChannelLineGroups[channel]; var availableGroups = new List<(UpdateLineGroup group, long delay)>(); @@ -468,9 +501,46 @@ namespace Ink_Canvas.Helpers LogHelper.WriteLogToFile("AutoUpdate | 所有线路组均不可用", LogHelper.LogType.Error); } + CacheOrderedGroups(channel, orderedGroups); return orderedGroups; } + // 缓存按延迟排序后的线路组,避免短时间内重复测速 + private static readonly Dictionary groups, DateTime cachedAt)> _orderedGroupsCache + = new Dictionary, DateTime)>(); + private static readonly TimeSpan _orderedGroupsCacheTtl = TimeSpan.FromMinutes(15); + + private static List TryGetCachedOrderedGroups(UpdateChannel channel) + { + lock (_orderedGroupsCache) + { + if (_orderedGroupsCache.TryGetValue(channel, out var entry) && + entry.groups != null && entry.groups.Count > 0 && + DateTime.UtcNow - entry.cachedAt < _orderedGroupsCacheTtl) + { + LogHelper.WriteLogToFile($"AutoUpdate | 复用线路组延迟检测缓存({entry.groups.Count} 个)"); + return new List(entry.groups); + } + return null; + } + } + + private static void CacheOrderedGroups(UpdateChannel channel, List groups) + { + lock (_orderedGroupsCache) + { + _orderedGroupsCache[channel] = (new List(groups), DateTime.UtcNow); + } + } + + public static void InvalidateOrderedGroupsCache() + { + lock (_orderedGroupsCache) + { + _orderedGroupsCache.Clear(); + } + } + private static async Task GetDownloadUrlDelay(string url) { try @@ -945,6 +1015,7 @@ namespace Ink_Canvas.Helpers // 使用多线路组下载新版(支持自动切换) public static async Task DownloadSetupFileWithFallback(string version, List groups, Action progressCallback = null) { + var session = BeginDownloadSession(); try { version = NormalizeVersionForUpdate(version); @@ -1021,6 +1092,13 @@ namespace Ink_Canvas.Helpers progressCallback?.Invoke(0, "所有线路组下载均失败"); return false; } + catch (OperationCanceledException) + { + LogHelper.WriteLogToFile("AutoUpdate | 下载已被用户取消", LogHelper.LogType.Warning); + SaveDownloadStatus(false); + progressCallback?.Invoke(0, "下载已取消"); + return false; + } catch (Exception ex) { LogHelper.WriteLogToFile($"AutoUpdate | 下载更新时出错: {ex.Message}", LogHelper.LogType.Error); @@ -1033,6 +1111,10 @@ namespace Ink_Canvas.Helpers progressCallback?.Invoke(0, $"下载异常: {ex.Message}"); return false; } + finally + { + EndDownloadSession(session); + } } // 下载文件的具体实现 @@ -1043,6 +1125,12 @@ namespace Ink_Canvas.Helpers // 降低并发数,减少网络压力 int[] threadOptions = { 32, 16, 8, 4, 1 }; + CancellationToken externalToken; + lock (_activeDownloadLock) + { + externalToken = _activeDownloadCts?.Token ?? CancellationToken.None; + } + // 检查服务器是否支持Range分块下载 bool supportRange = false; long totalSize = -1; @@ -1146,7 +1234,7 @@ namespace Ink_Canvas.Helpers // 增加连接超时设置 client.Timeout = TimeSpan.FromSeconds(30); - var downloadCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token); + var downloadCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, externalToken); var lastReadTime = DateTime.UtcNow; bool dataReceived = false; @@ -1339,12 +1427,18 @@ namespace Ink_Canvas.Helpers LogHelper.WriteLogToFile($"AutoUpdate | 开始单线程下载: {fileUrl}"); progressCallback?.Invoke(0, "开始单线程下载"); + CancellationToken token; + lock (_activeDownloadLock) + { + token = _activeDownloadCts?.Token ?? CancellationToken.None; + } + using (var client = new HttpClient()) { client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"); client.Timeout = TimeSpan.FromMinutes(10); // 单线程下载设置更长的超时时间 - using (var resp = await client.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead)) + using (var resp = await client.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead, token)) { resp.EnsureSuccessStatusCode(); using (var fs = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None)) @@ -1355,9 +1449,9 @@ namespace Ink_Canvas.Helpers long downloaded = 0; var lastProgressUpdate = DateTime.UtcNow; - while ((read = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0) + while ((read = await stream.ReadAsync(buffer, 0, buffer.Length, token)) > 0) { - await fs.WriteAsync(buffer, 0, read); + await fs.WriteAsync(buffer, 0, read, token); downloaded += read; // 限制进度更新频率,避免UI卡顿 @@ -1379,6 +1473,13 @@ namespace Ink_Canvas.Helpers LogHelper.WriteLogToFile("AutoUpdate | 单线程下载完成"); return true; } + catch (OperationCanceledException) + { + LogHelper.WriteLogToFile("AutoUpdate | 单线程下载已被取消", LogHelper.LogType.Warning); + progressCallback?.Invoke(0, "下载已取消"); + try { if (File.Exists(destinationPath)) File.Delete(destinationPath); } catch { } + return false; + } catch (Exception ex) { LogHelper.WriteLogToFile($"AutoUpdate | 单线程下载失败: {ex.Message}", LogHelper.LogType.Error); diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index 36576c21..fbcf4f01 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -2268,11 +2268,15 @@ - - - + + whiteboardPages = new List(); private int currentPageIndex; private System.Windows.Controls.Canvas currentCanvas; - private AutoUpdateHelper.UpdateLineGroup AvailableLatestLineGroup; + internal AutoUpdateHelper.UpdateLineGroup AvailableLatestLineGroup; // 全局快捷键管理器 private GlobalHotkeyManager _globalHotkeyManager; @@ -1203,7 +1203,6 @@ namespace Ink_Canvas LoadCustomBackgroundColor(); SetWindowMode(); - // HasNewUpdateWindow hasNewUpdateWindow = new HasNewUpdateWindow(); // 根据设置应用主题 switch (Settings.Appearance.Theme) { @@ -1846,6 +1845,7 @@ namespace Ink_Canvas var (remoteVersion, lineGroup, apiReleaseNotes) = await AutoUpdateHelper.CheckForUpdates(Settings.Startup.UpdateChannel); AvailableLatestVersion = remoteVersion; AvailableLatestLineGroup = lineGroup; + AvailableLatestReleaseNotes = apiReleaseNotes; // 声明下载状态变量,用于整个方法 bool isDownloadSuccessful = false; @@ -1882,9 +1882,6 @@ namespace Ink_Canvas SaveSettingsToFile(); } - // 获取当前版本 - string currentVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); - // 如果启用了静默更新,则自动下载更新而不显示提示 if (Settings.Startup.IsAutoUpdateWithSilence) { @@ -1908,121 +1905,10 @@ namespace Ink_Canvas return; } - // 如果没有启用静默更新,则显示常规更新窗口 - string releaseDate = DateTime.Now.ToString("yyyy年MM月dd日"); - - // 从服务器获取更新日志 - string releaseNotes = await AutoUpdateHelper.GetUpdateLog(Settings.Startup.UpdateChannel); - - // 如果获取失败,使用默认文本 - if (string.IsNullOrEmpty(releaseNotes)) - { - releaseNotes = $@"# InkCanvasForClass v{AvailableLatestVersion}更新 - - 无法获取更新日志,但新版本已准备就绪。"; - } - - // 创建并显示更新窗口 - HasNewUpdateWindow updateWindow = new HasNewUpdateWindow(currentVersion, AvailableLatestVersion, releaseDate, releaseNotes); - updateWindow.Owner = this; - bool? dialogResult = updateWindow.ShowDialog(); - - // 如果窗口被关闭但没有点击按钮,则不执行任何操作 - if (dialogResult != true) - { - LogHelper.WriteLogToFile("AutoUpdate | Update dialog closed without selection"); - return; - } - - // 不再从更新窗口获取自动更新设置 - - // 根据用户选择处理更新 - switch (updateWindow.Result) - { - case HasNewUpdateWindow.UpdateResult.UpdateNow: - // 立即更新:显示下载进度,下载完成后立即安装 - LogHelper.WriteLogToFile("AutoUpdate | User chose to update now"); - - // 显示下载进度提示 - MessageBox.Show("开始下载更新,请稍候...", "正在更新", MessageBoxButton.OK, MessageBoxImage.Information); - - // 下载更新文件,使用多线路组下载功能 - isDownloadSuccessful = await DownloadUpdateWithFallback(AvailableLatestVersion, AvailableLatestLineGroup, Settings.Startup.UpdateChannel); - - if (isDownloadSuccessful) - { - // 下载成功,提示用户准备安装 - MessageBoxResult result = MessageBox.Show("更新已下载完成,点击确定后将关闭软件并安装新版本!", "安装更新", MessageBoxButton.OKCancel, MessageBoxImage.Information); - - // 只有当用户点击确定按钮后才关闭软件 - if (result == MessageBoxResult.OK) - { - // 设置为用户主动退出,避免被看门狗判定为崩溃 - App.IsAppExitByUser = true; - - // 准备批处理脚本 - AutoUpdateHelper.InstallNewVersionApp(AvailableLatestVersion, true); // 修改为静默模式,避免重复启动进程 - - // 关闭软件,让安装程序接管 - Application.Current.Shutdown(); - } - else - { - LogHelper.WriteLogToFile("AutoUpdate | User cancelled update installation"); - } - } - else - { - // 下载失败 - MessageBox.Show("更新下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error); - } - break; - - case HasNewUpdateWindow.UpdateResult.UpdateLater: - // 稍后更新:静默下载,在软件关闭时自动安装 - LogHelper.WriteLogToFile("AutoUpdate | User chose to update later"); - - // 不管设置如何,都进行下载,使用多线路组下载功能 - isDownloadSuccessful = await DownloadUpdateWithFallback(AvailableLatestVersion, AvailableLatestLineGroup, Settings.Startup.UpdateChannel); - - if (isDownloadSuccessful) - { - LogHelper.WriteLogToFile("AutoUpdate | Update downloaded successfully, will install when application closes"); - - // 设置标志,在应用程序关闭时安装 - Settings.Startup.IsAutoUpdate = true; - Settings.Startup.IsAutoUpdateWithSilence = true; - - // 启动检查定时器 - timerCheckAutoUpdateWithSilence.Start(); - - // 通知用户 - MessageBox.Show("更新已下载完成,将在软件关闭时自动安装。", "更新已准备就绪", MessageBoxButton.OK, MessageBoxImage.Information); - } - else - { - LogHelper.WriteLogToFile("AutoUpdate | Update download failed", LogHelper.LogType.Error); - MessageBox.Show("更新下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error); - } - break; - - case HasNewUpdateWindow.UpdateResult.SkipVersion: - // 跳过该版本:记录到设置中 - LogHelper.WriteLogToFile($"AutoUpdate | User chose to skip version {AvailableLatestVersion}"); - - // 记录要跳过的版本号 - Settings.Startup.SkippedVersion = AvailableLatestVersion; - - // 保存设置到文件 - SaveSettingsToFile(); - - // 通知用户 - MessageBox.Show($"已设置跳过版本 {AvailableLatestVersion},在下次发布新版本之前不会再提示更新。", - "已跳过此版本", - MessageBoxButton.OK, - MessageBoxImage.Information); - break; - } + // 如果没有启用静默更新,则记录日志并依赖 Toast 通知用户。 + // 用户可在 设置 → 更新 中查看版本说明并选择更新方式。 + LogHelper.WriteLogToFile( + $"AutoUpdate | New version {AvailableLatestVersion} available; user notified via toast, will act from settings page."); } else if (hasValidLineGroup) { @@ -2271,9 +2157,10 @@ namespace Ink_Canvas private void HistoryRollbackButton_Click(object sender, RoutedEventArgs e) { - var win = new HistoryRollbackWindow(Settings.Startup.UpdateChannel); - win.Owner = this; - win.ShowDialog(); + var settingsWindow = new Windows.SettingsViews.SettingsWindow(); + settingsWindow.Owner = this; + settingsWindow.Show(); + settingsWindow.NavigateToPage("UpdatePage"); } [DllImport("user32.dll")] diff --git a/Ink Canvas/MainWindow_cs/MW_Timer.cs b/Ink Canvas/MainWindow_cs/MW_Timer.cs index b2139bb9..fcd2e663 100644 --- a/Ink Canvas/MainWindow_cs/MW_Timer.cs +++ b/Ink Canvas/MainWindow_cs/MW_Timer.cs @@ -95,7 +95,11 @@ namespace Ink_Canvas /// /// 可用的最新版本号 /// - private string AvailableLatestVersion; + internal string AvailableLatestVersion; + /// + /// 最近一次自动检查得到的更新说明(Markdown) + /// + internal string AvailableLatestReleaseNotes; /// /// 静默更新检查定时器 /// diff --git a/Ink Canvas/Windows/HasNewUpdateWindow.xaml b/Ink Canvas/Windows/HasNewUpdateWindow.xaml deleted file mode 100644 index 9387ce79..00000000 --- a/Ink Canvas/Windows/HasNewUpdateWindow.xaml +++ /dev/null @@ -1,346 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # InkCanvasForClass v5.0.2更新 - - 你好,旅行者们,本次InkCanvasForClass Community Edition更新带来了如下新功能供您探索: - - 1. 全新设计的UI界面,包括浮动工具栏和白板页面均经过重新设计,更加现代化的UI让您在使用的过程中更加舒适。 - 2. 带来了实时修改橡皮大小和橡皮形状的菜单。您可以选择使用圆形橡皮,方形橡皮,和类似希沃白板的真实黑板擦(矩形)橡皮。 - 3. 白板页面支持显示当前时间和日期 - 4. 自动收纳新增对希沃轻白板、智绘教、鸿合屏幕书写等软件的支持,自动查杀新增对鸿合屏幕书写、希沃轻白板等软件的支持。 - 5. 为设置界面重写了全新的UI。 - 6. 重写了随机抽选模块,现在支持更丰富的抽选机制和自定义选项。 - 7. 修复了部分小Bug,提升了整体的用户体验。 - 8. 带来了基于FitToCurve的笔迹平滑,基于贝塞尔曲线平滑,让墨迹线条更加优美好看。 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + 16 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + - - - - - - - - - - + - - - - - - - - - + + + + + - - - - -