@@ -49,5 +49,5 @@ using System.Windows;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.7.1.10")]
|
||||
[assembly: AssemblyFileVersion("1.7.1.10")]
|
||||
[assembly: AssemblyVersion("1.7.1.12")]
|
||||
[assembly: AssemblyFileVersion("1.7.1.12")]
|
||||
|
||||
@@ -10,7 +10,9 @@ using System.Linq;
|
||||
using System.Windows.Controls;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
@@ -31,7 +33,7 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
|
||||
// 通道-线路组映射
|
||||
private static readonly Dictionary<UpdateChannel, List<UpdateLineGroup>> ChannelLineGroups = new Dictionary<UpdateChannel, List<UpdateLineGroup>>
|
||||
public static readonly Dictionary<UpdateChannel, List<UpdateLineGroup>> ChannelLineGroups = new Dictionary<UpdateChannel, List<UpdateLineGroup>>
|
||||
{
|
||||
{ UpdateChannel.Release, new List<UpdateLineGroup>
|
||||
{
|
||||
@@ -85,6 +87,15 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
};
|
||||
|
||||
// 区块任务结构体(移到类体内)
|
||||
private class BlockTask
|
||||
{
|
||||
public int Index;
|
||||
public long Start;
|
||||
public long End;
|
||||
public int RetryCount;
|
||||
}
|
||||
|
||||
// 检测URL延迟
|
||||
private static async Task<long> GetUrlDelay(string url)
|
||||
{
|
||||
@@ -342,44 +353,88 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
// 通过GitHub API获取最新Release信息
|
||||
private static async Task<(string version, string downloadUrl, string releaseNotes)> GetLatestGithubRelease(UpdateChannel channel)
|
||||
{
|
||||
try
|
||||
{
|
||||
string apiUrl = channel == UpdateChannel.Beta
|
||||
? "https://api.github.com/repos/InkCanvasForClass/community-beta/releases/latest"
|
||||
: "https://api.github.com/repos/InkCanvasForClass/community/releases/latest";
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
client.DefaultRequestHeaders.Add("User-Agent", "ICC-CE Auto Updater");
|
||||
var response = await client.GetStringAsync(apiUrl);
|
||||
var json = JObject.Parse(response);
|
||||
string version = json["tag_name"]?.ToString();
|
||||
string releaseNotes = json["body"]?.ToString();
|
||||
string downloadUrl = json["assets"]?.First?["browser_download_url"]?.ToString();
|
||||
if (!string.IsNullOrEmpty(version) && !string.IsNullOrEmpty(downloadUrl))
|
||||
return (version, downloadUrl, releaseNotes);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | GitHub Releases API 获取失败: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
return (null, null, null);
|
||||
}
|
||||
|
||||
// 主要的更新检测方法(优先检测延迟,失败时自动切换线路组)
|
||||
public static async Task<(string remoteVersion, UpdateLineGroup lineGroup)> CheckForUpdates(UpdateChannel channel = UpdateChannel.Release, bool alwaysGetRemote = false)
|
||||
// 仅检测新版本时用GitHub API,实际下载时只用线路组
|
||||
public static async Task<(string remoteVersion, UpdateLineGroup lineGroup, string releaseNotes)> CheckForUpdates(UpdateChannel channel = UpdateChannel.Release, bool alwaysGetRemote = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
string localVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 本地版本: {localVersion}");
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 检测通道 {channel} 下最快线路组...");
|
||||
|
||||
// 获取所有可用线路组(按延迟排序)
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 优先通过GitHub Releases API检测...");
|
||||
// 1. 优先通过GitHub Releases API获取
|
||||
var (apiVersion, _, apiReleaseNotes) = await GetLatestGithubRelease(channel);
|
||||
if (!string.IsNullOrEmpty(apiVersion))
|
||||
{
|
||||
Version local = new Version(localVersion);
|
||||
Version remote = new Version(apiVersion.TrimStart('v', 'V'));
|
||||
if (remote > local || alwaysGetRemote)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 通过GitHub Releases API发现新版本: {apiVersion}");
|
||||
// 只返回版本号和日志,不返回直链
|
||||
var group = (await GetAvailableLineGroupsOrdered(channel)).FirstOrDefault();
|
||||
return (apiVersion, group, apiReleaseNotes);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 当前版本已是最新 (GitHub Releases API)");
|
||||
var group = (await GetAvailableLineGroupsOrdered(channel)).FirstOrDefault();
|
||||
return (null, group, apiReleaseNotes);
|
||||
}
|
||||
}
|
||||
// 2. 回退到原有txt方案
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | GitHub Releases API获取失败,回退到txt方案...");
|
||||
var availableGroups = await GetAvailableLineGroupsOrdered(channel);
|
||||
if (availableGroups.Count == 0)
|
||||
{
|
||||
LogHelper.WriteLogToFile("AutoUpdate | 所有线路组均不可用", LogHelper.LogType.Error);
|
||||
return (null, null);
|
||||
return (null, null, null);
|
||||
}
|
||||
|
||||
// 依次尝试每个线路组,直到成功获取版本信息
|
||||
foreach (var group in availableGroups)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 尝试使用线路组获取版本信息: {group.GroupName}");
|
||||
string remoteVersion = await GetRemoteVersion(group.VersionUrl);
|
||||
|
||||
if (remoteVersion != null)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 成功从线路组 {group.GroupName} 获取远程版本: {remoteVersion}");
|
||||
Version local = new Version(localVersion);
|
||||
Version remote = new Version(remoteVersion);
|
||||
|
||||
if (remote > local || alwaysGetRemote)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 发现新版本或强制获取: {remoteVersion}");
|
||||
return (remoteVersion, group);
|
||||
return (remoteVersion, group, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 当前版本已是最新");
|
||||
return (null, group);
|
||||
return (null, group, null);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -387,14 +442,13 @@ namespace Ink_Canvas.Helpers
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 线路组 {group.GroupName} 获取版本失败,尝试下一个线路组", LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
LogHelper.WriteLogToFile("AutoUpdate | 所有线路组均无法获取版本信息", LogHelper.LogType.Error);
|
||||
return (null, null);
|
||||
return (null, null, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | CheckForUpdates错误: {ex.Message}", LogHelper.LogType.Error);
|
||||
return (null, null);
|
||||
return (null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,7 +459,7 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
|
||||
// 使用多线路组下载新版(支持自动切换)
|
||||
public static async Task<bool> DownloadSetupFileWithFallback(string version, List<UpdateLineGroup> groups)
|
||||
public static async Task<bool> DownloadSetupFileWithFallback(string version, List<UpdateLineGroup> groups, Action<double, string> progressCallback = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -414,6 +468,7 @@ namespace Ink_Canvas.Helpers
|
||||
if (File.Exists(statusFilePath) && File.ReadAllText(statusFilePath).Trim().ToLower() == "true")
|
||||
{
|
||||
LogHelper.WriteLogToFile("AutoUpdate | 安装包已下载");
|
||||
progressCallback?.Invoke(100, "已下载完成");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -435,12 +490,13 @@ namespace Ink_Canvas.Helpers
|
||||
string url = string.Format(group.DownloadUrlFormat, version);
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 尝试从线路组 {group.GroupName} 下载: {url}");
|
||||
|
||||
bool downloadSuccess = await DownloadFile(url, zipFilePath);
|
||||
bool downloadSuccess = await DownloadFile(url, zipFilePath, progressCallback);
|
||||
|
||||
if (downloadSuccess)
|
||||
{
|
||||
SaveDownloadStatus(true);
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 从线路组 {group.GroupName} 下载成功");
|
||||
progressCallback?.Invoke(100, "下载完成");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -450,6 +506,7 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
|
||||
LogHelper.WriteLogToFile("AutoUpdate | 所有线路组下载均失败", LogHelper.LogType.Error);
|
||||
progressCallback?.Invoke(0, "所有线路组下载均失败");
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -461,283 +518,185 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
|
||||
SaveDownloadStatus(false);
|
||||
progressCallback?.Invoke(0, $"下载异常: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 下载文件的具体实现
|
||||
private static async Task<bool> DownloadFile(string fileUrl, string destinationPath)
|
||||
public static async Task<bool> DownloadFile(string fileUrl, string destinationPath, Action<double, string> progressCallback = null)
|
||||
{
|
||||
// 检测是否为Windows 7
|
||||
var osVersion = Environment.OSVersion;
|
||||
bool isWindows7 = osVersion.Version.Major == 6 && osVersion.Version.Minor == 1;
|
||||
|
||||
if (isWindows7)
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 正在尝试多线程下载: {fileUrl}");
|
||||
int maxRetry = 3;
|
||||
int[] threadOptions = new int[] { 32, 4 };
|
||||
foreach (int threadCount in threadOptions)
|
||||
{
|
||||
// Windows 7使用特殊配置
|
||||
using (var handler = new HttpClientHandler())
|
||||
long totalSize = await GetContentLength(fileUrl);
|
||||
if (totalSize <= 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 配置HttpClientHandler以支持Windows 7
|
||||
handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true;
|
||||
|
||||
using (HttpClient client = new HttpClient(handler))
|
||||
{
|
||||
client.Timeout = TimeSpan.FromMinutes(5);
|
||||
client.DefaultRequestHeaders.Add("User-Agent", "ICC-CE Auto Updater");
|
||||
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 开始下载: {fileUrl}");
|
||||
|
||||
string tempFilePath = destinationPath + ".tmp";
|
||||
|
||||
string directory = Path.GetDirectoryName(destinationPath);
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
var downloadTask = client.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead);
|
||||
var initialTimeoutTask = Task.Delay(RequestTimeout);
|
||||
|
||||
var completedTask = await Task.WhenAny(downloadTask, initialTimeoutTask);
|
||||
if (completedTask == initialTimeoutTask)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 初始连接超时", LogHelper.LogType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
HttpResponseMessage response = await downloadTask;
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | HTTP响应状态: {response.StatusCode}");
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
long? totalBytes = response.Content.Headers.ContentLength;
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 文件大小: {(totalBytes.HasValue ? (totalBytes.Value / 1024.0 / 1024.0).ToString("F2") + " MB" : "未知")}");
|
||||
|
||||
using (var fileStream = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
using (var downloadStream = await response.Content.ReadAsStreamAsync())
|
||||
{
|
||||
byte[] buffer = new byte[8192];
|
||||
long totalBytesRead = 0;
|
||||
int bytesRead;
|
||||
DateTime lastProgressUpdate = DateTime.Now;
|
||||
|
||||
var downloadTimeoutTask = Task.Delay(TimeSpan.FromSeconds(60));
|
||||
var readTask = Task.Run(async () => {
|
||||
while ((bytesRead = await downloadStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
|
||||
{
|
||||
await fileStream.WriteAsync(buffer, 0, bytesRead);
|
||||
totalBytesRead += bytesRead;
|
||||
|
||||
if ((DateTime.Now - lastProgressUpdate).TotalSeconds >= 5)
|
||||
{
|
||||
if (totalBytes.HasValue)
|
||||
{
|
||||
double percentage = (double)totalBytesRead / totalBytes.Value * 100;
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 下载进度: {percentage:F1}% ({(totalBytesRead / 1024.0 / 1024.0):F2} MB / {(totalBytes.Value / 1024.0 / 1024.0):F2} MB)");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 已下载: {(totalBytesRead / 1024.0 / 1024.0):F2} MB");
|
||||
}
|
||||
lastProgressUpdate = DateTime.Now;
|
||||
downloadTimeoutTask = Task.Delay(TimeSpan.FromSeconds(60));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (await Task.WhenAny(readTask, downloadTimeoutTask) == downloadTimeoutTask)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 下载超时(60秒无数据传输)", LogHelper.LogType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool downloadCompleted = await readTask;
|
||||
|
||||
if (downloadCompleted)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 下载完成: {(totalBytesRead / 1024.0 / 1024.0):F2} MB");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (File.Exists(tempFilePath))
|
||||
{
|
||||
if (File.Exists(destinationPath))
|
||||
{
|
||||
File.Delete(destinationPath);
|
||||
}
|
||||
|
||||
File.Move(tempFilePath, destinationPath);
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 文件保存到: {destinationPath}");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | HTTP请求错误: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 下载超时: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 下载文件时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 内部异常: {ex.InnerException.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string tempFilePath = destinationPath + ".tmp";
|
||||
if (File.Exists(tempFilePath))
|
||||
{
|
||||
File.Delete(tempFilePath);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
progressCallback?.Invoke(0, "无法获取文件大小,取消下载");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 其他Windows版本使用标准配置
|
||||
using (HttpClient client = new HttpClient())
|
||||
int blockSize = (int)Math.Ceiling((double)totalSize / threadCount);
|
||||
int blockCount = (int)Math.Ceiling((double)totalSize / blockSize);
|
||||
var blockQueue = new System.Collections.Concurrent.ConcurrentQueue<BlockTask>();
|
||||
var finishedBlocks = new System.Collections.Concurrent.ConcurrentDictionary<int, bool>();
|
||||
long[] blockDownloaded = new long[blockCount];
|
||||
for (int i = 0; i < blockCount; i++)
|
||||
{
|
||||
try
|
||||
long start = i * blockSize;
|
||||
long end = Math.Min(start + blockSize - 1, totalSize - 1);
|
||||
blockQueue.Enqueue(new BlockTask { Index = i, Start = start, End = end, RetryCount = 0 });
|
||||
}
|
||||
CancellationTokenSource cts = new CancellationTokenSource();
|
||||
var tasks = new List<Task>();
|
||||
for (int t = 0; t < threadCount; t++)
|
||||
{
|
||||
tasks.Add(Task.Run(async () =>
|
||||
{
|
||||
client.Timeout = TimeSpan.FromMinutes(5);
|
||||
client.DefaultRequestHeaders.Add("User-Agent", "ICC-CE Auto Updater");
|
||||
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 开始下载: {fileUrl}");
|
||||
|
||||
string tempFilePath = destinationPath + ".tmp";
|
||||
|
||||
string directory = Path.GetDirectoryName(destinationPath);
|
||||
if (!Directory.Exists(directory))
|
||||
while (blockQueue.TryDequeue(out var block))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
var downloadTask = client.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead);
|
||||
var initialTimeoutTask = Task.Delay(RequestTimeout);
|
||||
|
||||
var completedTask = await Task.WhenAny(downloadTask, initialTimeoutTask);
|
||||
if (completedTask == initialTimeoutTask)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 初始连接超时", LogHelper.LogType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
HttpResponseMessage response = await downloadTask;
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | HTTP响应状态: {response.StatusCode}");
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
long? totalBytes = response.Content.Headers.ContentLength;
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 文件大小: {(totalBytes.HasValue ? (totalBytes.Value / 1024.0 / 1024.0).ToString("F2") + " MB" : "未知")}");
|
||||
|
||||
using (var fileStream = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
using (var downloadStream = await response.Content.ReadAsStreamAsync())
|
||||
bool success = false;
|
||||
for (int retry = block.RetryCount; retry < maxRetry && !success; retry++)
|
||||
{
|
||||
byte[] buffer = new byte[8192];
|
||||
long totalBytesRead = 0;
|
||||
int bytesRead;
|
||||
DateTime lastProgressUpdate = DateTime.Now;
|
||||
|
||||
var downloadTimeoutTask = Task.Delay(TimeSpan.FromSeconds(60));
|
||||
var readTask = Task.Run(async () => {
|
||||
while ((bytesRead = await downloadStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
|
||||
try
|
||||
{
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
await fileStream.WriteAsync(buffer, 0, bytesRead);
|
||||
totalBytesRead += bytesRead;
|
||||
|
||||
if ((DateTime.Now - lastProgressUpdate).TotalSeconds >= 5)
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, fileUrl);
|
||||
req.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(block.Start, block.End);
|
||||
|
||||
// 新增:分块下载超时机制
|
||||
var downloadCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token);
|
||||
var lastReadTime = DateTime.UtcNow;
|
||||
bool dataReceived = false;
|
||||
|
||||
using (var resp = await client.SendAsync(req, HttpCompletionOption.ResponseHeadersRead, downloadCts.Token))
|
||||
{
|
||||
if (totalBytes.HasValue)
|
||||
resp.EnsureSuccessStatusCode();
|
||||
string tempPath = destinationPath + $".part{block.Index}";
|
||||
using (var fs = new FileStream(tempPath, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
double percentage = (double)totalBytesRead / totalBytes.Value * 100;
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 下载进度: {percentage:F1}% ({(totalBytesRead / 1024.0 / 1024.0):F2} MB / {(totalBytes.Value / 1024.0 / 1024.0):F2} MB)");
|
||||
var stream = await resp.Content.ReadAsStreamAsync();
|
||||
byte[] buffer = new byte[8192];
|
||||
int read;
|
||||
while (true)
|
||||
{
|
||||
var readTask = stream.ReadAsync(buffer, 0, buffer.Length, downloadCts.Token);
|
||||
var timeoutTask = Task.Delay(15000, downloadCts.Token); // 15秒超时
|
||||
var completed = await Task.WhenAny(readTask, timeoutTask);
|
||||
if (completed == timeoutTask)
|
||||
{
|
||||
// 超时未收到数据,取消本线程,重新入队
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 分块{block.Index} 15秒无数据,线程超时重试", LogHelper.LogType.Warning);
|
||||
progressCallback?.Invoke(0, $"分块{block.Index} 15秒无数据,线程超时重试");
|
||||
downloadCts.Cancel();
|
||||
break;
|
||||
}
|
||||
read = await readTask;
|
||||
if (read <= 0) break;
|
||||
await fs.WriteAsync(buffer, 0, read, downloadCts.Token);
|
||||
blockDownloaded[block.Index] += read;
|
||||
lastReadTime = DateTime.UtcNow;
|
||||
dataReceived = true;
|
||||
// 合并所有块进度
|
||||
long totalDownloaded = blockDownloaded.Sum();
|
||||
double percent = (double)totalDownloaded / totalSize * 100;
|
||||
progressCallback?.Invoke(percent, $"多线程下载中({threadCount}线程): {percent:F1}%");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 已下载: {(totalBytesRead / 1024.0 / 1024.0):F2} MB");
|
||||
}
|
||||
lastProgressUpdate = DateTime.Now;
|
||||
downloadTimeoutTask = Task.Delay(TimeSpan.FromSeconds(60));
|
||||
}
|
||||
// 如果因超时break且未完成,success为false,重新入队
|
||||
if (!dataReceived)
|
||||
{
|
||||
throw new IOException("分块下载超时无数据");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (await Task.WhenAny(readTask, downloadTimeoutTask) == downloadTimeoutTask)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 下载超时(60秒无数据传输)", LogHelper.LogType.Error);
|
||||
return false;
|
||||
success = true;
|
||||
}
|
||||
|
||||
bool downloadCompleted = await readTask;
|
||||
|
||||
if (downloadCompleted)
|
||||
catch (Exception ex) when (ex is HttpRequestException || ex is IOException || ex is TaskCanceledException)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 下载完成: {(totalBytesRead / 1024.0 / 1024.0):F2} MB");
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 分块{block.Index}下载失败,第{retry + 1}次: {ex.Message}", LogHelper.LogType.Warning);
|
||||
progressCallback?.Invoke(0, $"分块{block.Index}下载失败,第{retry + 1}次: {ex.Message}");
|
||||
await Task.Delay(15000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (File.Exists(tempFilePath))
|
||||
{
|
||||
if (File.Exists(destinationPath))
|
||||
if (success)
|
||||
{
|
||||
File.Delete(destinationPath);
|
||||
finishedBlocks[block.Index] = true;
|
||||
}
|
||||
else if (block.RetryCount + 1 < maxRetry)
|
||||
{
|
||||
// 失败但未超最大重试,重新入队
|
||||
block.RetryCount++;
|
||||
blockQueue.Enqueue(block);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 超过最大重试,取消所有任务
|
||||
cts.Cancel();
|
||||
break;
|
||||
}
|
||||
|
||||
File.Move(tempFilePath, destinationPath);
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 文件保存到: {destinationPath}");
|
||||
return true;
|
||||
}
|
||||
|
||||
}));
|
||||
}
|
||||
await Task.WhenAll(tasks);
|
||||
if (cts.IsCancellationRequested || finishedBlocks.Count != blockCount)
|
||||
{
|
||||
progressCallback?.Invoke(0, $"多线程下载失败({threadCount}线程)");
|
||||
// 清理分块文件
|
||||
for (int i = 0; i < blockCount; i++)
|
||||
{
|
||||
string tempPath = destinationPath + $".part{i}";
|
||||
if (File.Exists(tempPath)) File.Delete(tempPath);
|
||||
}
|
||||
if (threadCount == threadOptions.Last())
|
||||
{
|
||||
// 已经是最后一次尝试
|
||||
return false;
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | HTTP请求错误: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | {threadCount}线程下载失败,尝试降级为{threadOptions.Last()}线程");
|
||||
progressCallback?.Invoke(0, $"{threadCount}线程下载失败,尝试降级为{threadOptions.Last()}线程");
|
||||
continue;
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
}
|
||||
// 合并所有块
|
||||
using (var output = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
for (int i = 0; i < blockCount; i++)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 下载超时: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 下载文件时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
if (ex.InnerException != null)
|
||||
string tempPath = destinationPath + $".part{i}";
|
||||
using (var input = new FileStream(tempPath, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 内部异常: {ex.InnerException.Message}", LogHelper.LogType.Error);
|
||||
await input.CopyToAsync(output);
|
||||
}
|
||||
File.Delete(tempPath);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string tempFilePath = destinationPath + ".tmp";
|
||||
if (File.Exists(tempFilePath))
|
||||
{
|
||||
File.Delete(tempFilePath);
|
||||
}
|
||||
progressCallback?.Invoke(100, $"多线程下载完成({threadCount}线程)");
|
||||
return true;
|
||||
}
|
||||
// 理论上不会到这里
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取文件总大小
|
||||
private static async Task<long> GetContentLength(string fileUrl)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
var req = new HttpRequestMessage(HttpMethod.Head, fileUrl);
|
||||
var resp = await client.SendAsync(req);
|
||||
if (resp.IsSuccessStatusCode && resp.Content.Headers.ContentLength.HasValue)
|
||||
return resp.Content.Headers.ContentLength.Value;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 保存下载状态
|
||||
@@ -1065,7 +1024,7 @@ namespace Ink_Canvas.Helpers
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 开始修复版本,通道: {channel}");
|
||||
|
||||
// 获取远程版本号(自动选择最快线路组,始终下载远程版本)
|
||||
var (remoteVersion, group) = await CheckForUpdates(channel, true);
|
||||
var (remoteVersion, group, _) = await CheckForUpdates(channel, true);
|
||||
if (string.IsNullOrEmpty(remoteVersion) || group == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("AutoUpdate | 修复版本时获取远程版本失败", LogHelper.LogType.Error);
|
||||
@@ -1097,6 +1056,37 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
// 获取所有GitHub历史版本(Release)
|
||||
public static async Task<List<(string version, string downloadUrl, string releaseNotes)>> GetAllGithubReleases(UpdateChannel channel = UpdateChannel.Release)
|
||||
{
|
||||
var result = new List<(string, string, string)>();
|
||||
try
|
||||
{
|
||||
string apiUrl = channel == UpdateChannel.Beta
|
||||
? "https://api.github.com/repos/InkCanvasForClass/community-beta/releases"
|
||||
: "https://api.github.com/repos/InkCanvasForClass/community/releases";
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
client.DefaultRequestHeaders.Add("User-Agent", "ICC-CE Auto Updater");
|
||||
var response = await client.GetStringAsync(apiUrl);
|
||||
var arr = JArray.Parse(response);
|
||||
foreach (var item in arr)
|
||||
{
|
||||
string version = item["tag_name"]?.ToString();
|
||||
string releaseNotes = item["body"]?.ToString();
|
||||
string downloadUrl = item["assets"]?.First?["browser_download_url"]?.ToString();
|
||||
if (!string.IsNullOrEmpty(version) && !string.IsNullOrEmpty(downloadUrl))
|
||||
result.Add((version, downloadUrl, releaseNotes));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 获取历史版本失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 测试Windows 7 TLS连接的方法
|
||||
public static async Task<bool> TestWindows7TlsConnection()
|
||||
{
|
||||
@@ -1145,6 +1135,37 @@ namespace Ink_Canvas.Helpers
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动手动指定版本的多线路多线程下载并自动安装(用于历史版本回滚等场景)
|
||||
/// </summary>
|
||||
public static async Task<bool> StartManualDownloadAndInstall(string version, UpdateChannel channel, Action<double, string> progressCallback = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 先检测并排序所有可用线路组
|
||||
var groups = await GetAvailableLineGroupsOrdered(channel);
|
||||
bool downloadSuccess = await DownloadSetupFileWithFallback(version, groups, progressCallback);
|
||||
if (!downloadSuccess)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 手动下载版本{version}失败");
|
||||
return false;
|
||||
}
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 手动安装版本: {version}");
|
||||
InstallNewVersionApp(version, false);
|
||||
App.IsAppExitByUser = true;
|
||||
Application.Current.Dispatcher.Invoke(() => {
|
||||
Application.Current.Shutdown();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 手动下载或安装异常: {ex.Message}", LogHelper.LogType.Error);
|
||||
progressCallback?.Invoke(0, $"下载异常: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class AutoUpdateWithSilenceTimeComboBox
|
||||
|
||||
@@ -645,8 +645,10 @@
|
||||
Width="120" HorizontalAlignment="Left" Click="FixVersionButton_Click"/>
|
||||
<TextBlock Text="# 版本修复会根据当前选择的通道下载最新版本并执行安装,可用于修复损坏的安装"
|
||||
TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
|
||||
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="5" Padding="12"
|
||||
<Button x:Name="HistoryRollbackButton" Content="历史版本回滚" Width="120" Margin="0,10,0,0" Click="HistoryRollbackButton_Click"/>
|
||||
<TextBlock Text="# 历史版本回滚,点击后会弹出相应页面供用户手动回滚到之前的版本"
|
||||
TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
<Border BorderBrush="White" BorderThickness="1" CornerRadius="5" Padding="12"
|
||||
Visibility="{Binding ElementName=ToggleSwitchIsAutoUpdateWithSilence, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<ui:SimpleStackPanel Spacing="12">
|
||||
<TextBlock
|
||||
@@ -655,12 +657,12 @@
|
||||
<ui:SimpleStackPanel x:Name="AutoUpdateTimePeriodBlock" Spacing="12">
|
||||
<ui:SimpleStackPanel Spacing="12">
|
||||
<TextBlock Text="静默更新时间段" FontSize="15" FontWeight="Bold"
|
||||
TextWrapping="Wrap" Foreground="Black" />
|
||||
TextWrapping="Wrap" Foreground="#fafafa" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" Spacing="12">
|
||||
<ui:SimpleStackPanel Orientation="Horizontal">
|
||||
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center"
|
||||
Text="起始时间" FontSize="14" TextWrapping="Wrap"
|
||||
Foreground="Black" />
|
||||
Foreground="#fafafa" />
|
||||
<ComboBox x:Name="AutoUpdateWithSilenceStartTimeComboBox"
|
||||
Width="90"
|
||||
SelectionChanged="AutoUpdateWithSilenceStartTimeComboBox_SelectionChanged" />
|
||||
@@ -668,7 +670,7 @@
|
||||
<ui:SimpleStackPanel Orientation="Horizontal">
|
||||
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center"
|
||||
Text="终止时间" FontSize="14" TextWrapping="Wrap"
|
||||
Foreground="Black" />
|
||||
Foreground="#fafafa" />
|
||||
<ComboBox x:Name="AutoUpdateWithSilenceEndTimeComboBox"
|
||||
Width="90"
|
||||
SelectionChanged="AutoUpdateWithSilenceEndTimeComboBox_SelectionChanged" />
|
||||
@@ -849,6 +851,15 @@
|
||||
</ui:SimpleStackPanel>
|
||||
<TextBlock Text="# 允许选中墨迹后对墨迹进行双指或多指缩放操作(此设置不受“允许双指旋转”设置的影响)" TextWrapping="Wrap"
|
||||
Foreground="#a1a1aa" />
|
||||
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
|
||||
StrokeThickness="1" Margin="0,4,0,4" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="启用手掌擦" VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchEnablePalmEraser" IsOn="True"
|
||||
FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchEnablePalmEraser_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<TextBlock Text="# 关闭后,手掌将无法触发橡皮擦功能" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
</ui:SimpleStackPanel>
|
||||
</GroupBox>
|
||||
<GroupBox Name="GroupBoxInkRecognition">
|
||||
@@ -1206,6 +1217,7 @@
|
||||
Toggled="ToggleSwitchEnableWppProcessKill_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<TextBlock Text="# 关闭后将不会自动查杀WPP残留进程,可能导致WPP关闭卡顿或无法彻底退出。" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
<TextBlock Text="# 如果您只使用PowerPoint请不要打开WPS联动开关,如果使用WPS建议不要使用PowerPoint!" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
<Border BorderBrush="#ef4444"
|
||||
BorderThickness="2" Padding="8" CornerRadius="6" Background="#991b1b">
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" Spacing="4">
|
||||
@@ -2888,7 +2900,7 @@
|
||||
</ui:ScrollViewerEx>
|
||||
|
||||
<!-- 底部按钮区域 -->
|
||||
<Grid Grid.Row="2" VerticalAlignment="Bottom" Height="50">
|
||||
<Grid Grid.Row="2" VerticalAlignment="Bottom" Height="65">
|
||||
<Button FontFamily="Microsoft YaHei UI"
|
||||
Width="120" Margin="10"
|
||||
HorizontalAlignment="Right"
|
||||
|
||||
@@ -35,6 +35,14 @@ namespace Ink_Canvas {
|
||||
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
|
||||
private const int GWL_EXSTYLE = -20;
|
||||
private const int WS_EX_NOACTIVATE = 0x08000000;
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
|
||||
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
|
||||
private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
|
||||
private const uint SWP_NOSIZE = 0x0001;
|
||||
private const uint SWP_NOMOVE = 0x0002;
|
||||
private const uint SWP_NOACTIVATE = 0x0010;
|
||||
private const uint SWP_SHOWWINDOW = 0x0040;
|
||||
|
||||
// 新增:设置窗口置顶并兼容无焦点
|
||||
private void SetTopmostWithNoActivate(bool topmost)
|
||||
@@ -45,6 +53,15 @@ namespace Ink_Canvas {
|
||||
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_NOACTIVATE);
|
||||
// 设置 Topmost
|
||||
this.Topmost = topmost;
|
||||
// 使用SetWindowPos确保无焦点置顶
|
||||
if (topmost)
|
||||
{
|
||||
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
||||
}
|
||||
// 再加回 WS_EX_NOACTIVATE
|
||||
exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
||||
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_NOACTIVATE);
|
||||
@@ -248,10 +265,25 @@ namespace Ink_Canvas {
|
||||
catch { }
|
||||
}
|
||||
|
||||
// 新增:记录上一个模式
|
||||
private InkCanvasEditingMode lastEditingMode = InkCanvasEditingMode.Ink;
|
||||
|
||||
private void inkCanvas_EditingModeChanged(object sender, RoutedEventArgs e) {
|
||||
var inkCanvas1 = sender as InkCanvas;
|
||||
if (inkCanvas1 == null) return;
|
||||
|
||||
// 只负责显示/隐藏覆盖层,不再强制切换模式
|
||||
var eraserOverlay = this.FindName("AdvancedEraserOverlay") as Border;
|
||||
if (eraserOverlay != null) {
|
||||
if (inkCanvas1.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
eraserOverlay.IsHitTestVisible = true;
|
||||
Trace.WriteLine("Advanced Eraser: Overlay enabled in eraser mode");
|
||||
} else {
|
||||
eraserOverlay.IsHitTestVisible = false;
|
||||
DisableAdvancedEraserSystem();
|
||||
Trace.WriteLine("Advanced Eraser: Overlay disabled in non-eraser mode");
|
||||
}
|
||||
}
|
||||
// 使用辅助方法设置光标
|
||||
SetCursorBasedOnEditingMode(inkCanvas1);
|
||||
if (Settings.Canvas.IsShowCursor) {
|
||||
@@ -262,31 +294,13 @@ namespace Ink_Canvas {
|
||||
else
|
||||
inkCanvas1.ForceCursor = false;
|
||||
} else {
|
||||
// 套索选择模式下始终强制显示光标,即使用户设置不显示光标
|
||||
if (inkCanvas1.EditingMode == InkCanvasEditingMode.Select) {
|
||||
inkCanvas1.ForceCursor = true;
|
||||
} else {
|
||||
inkCanvas1.ForceCursor = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (inkCanvas1.EditingMode == InkCanvasEditingMode.Ink) forcePointEraser = !forcePointEraser;
|
||||
|
||||
// 处理高级橡皮擦覆盖层的启用/禁用
|
||||
var eraserOverlay = this.FindName("AdvancedEraserOverlay") as Border;
|
||||
if (eraserOverlay != null) {
|
||||
if (inkCanvas1.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
// 橡皮擦模式下启用覆盖层
|
||||
eraserOverlay.IsHitTestVisible = true;
|
||||
Trace.WriteLine("Advanced Eraser: Overlay enabled in eraser mode");
|
||||
} else {
|
||||
// 其他模式下禁用覆盖层
|
||||
eraserOverlay.IsHitTestVisible = false;
|
||||
// 同时禁用高级橡皮擦系统
|
||||
DisableAdvancedEraserSystem();
|
||||
Trace.WriteLine("Advanced Eraser: Overlay disabled in non-eraser mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Ink Canvas
|
||||
@@ -297,11 +311,16 @@ namespace Ink_Canvas {
|
||||
public static string settingsFileName = "Settings.json";
|
||||
private bool isLoaded = false;
|
||||
private bool forcePointEraser = false;
|
||||
public static bool EnablePalmEraser = true;
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e) {
|
||||
loadPenCanvas();
|
||||
//加载设置
|
||||
LoadSettings(true);
|
||||
// 同步手掌擦开关
|
||||
EnablePalmEraser = Settings.Canvas.EnablePalmEraser;
|
||||
if (ToggleSwitchEnablePalmEraser != null)
|
||||
ToggleSwitchEnablePalmEraser.IsOn = EnablePalmEraser;
|
||||
|
||||
// 加载自定义背景颜色
|
||||
LoadCustomBackgroundColor();
|
||||
@@ -381,6 +400,14 @@ namespace Ink_Canvas {
|
||||
|
||||
// 初始化插件系统
|
||||
InitializePluginSystem();
|
||||
|
||||
// 新增:确保EditingModeChanged事件已绑定
|
||||
var inkCanvas = this.FindName("inkCanvas") as InkCanvas;
|
||||
if (inkCanvas != null)
|
||||
{
|
||||
inkCanvas.EditingModeChanged -= inkCanvas_EditingModeChanged;
|
||||
inkCanvas.EditingModeChanged += inkCanvas_EditingModeChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void SystemEventsOnDisplaySettingsChanged(object sender, EventArgs e) {
|
||||
@@ -535,7 +562,7 @@ namespace Ink_Canvas {
|
||||
AvailableLatestLineGroup = null;
|
||||
|
||||
// 使用当前选择的更新通道检查更新
|
||||
var (remoteVersion, lineGroup) = await AutoUpdateHelper.CheckForUpdates(Settings.Startup.UpdateChannel);
|
||||
var (remoteVersion, lineGroup, apiReleaseNotes) = await AutoUpdateHelper.CheckForUpdates(Settings.Startup.UpdateChannel);
|
||||
AvailableLatestVersion = remoteVersion;
|
||||
AvailableLatestLineGroup = lineGroup;
|
||||
|
||||
@@ -1312,5 +1339,25 @@ namespace Ink_Canvas {
|
||||
// 直接调用PPT放映结束按钮的逻辑
|
||||
BtnPPTSlideShowEnd_Click(BtnPPTSlideShowEnd, null);
|
||||
}
|
||||
|
||||
private void HistoryRollbackButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// 收起设置面板(与插件面板一致)
|
||||
BorderSettings.Visibility = Visibility.Hidden;
|
||||
BorderSettingsMask.Visibility = Visibility.Hidden;
|
||||
var win = new HistoryRollbackWindow(Settings.Startup.UpdateChannel);
|
||||
win.ShowDialog();
|
||||
// 可选:回滚窗口关闭后恢复设置面板显示
|
||||
BorderSettings.Visibility = Visibility.Visible;
|
||||
BorderSettingsMask.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
private void ToggleSwitchEnablePalmEraser_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
EnablePalmEraser = ToggleSwitchEnablePalmEraser.IsOn;
|
||||
Settings.Canvas.EnablePalmEraser = EnablePalmEraser;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -634,52 +634,68 @@ namespace Ink_Canvas {
|
||||
|
||||
// 绑定事件处理
|
||||
overlay.MouseDown += (sender, e) => {
|
||||
if (!MainWindow.EnablePalmEraser) return;
|
||||
var inkCanvas = this.FindName("inkCanvas") as InkCanvas;
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
lastEditingMode = inkCanvas.EditingMode;
|
||||
overlay.CaptureMouse();
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
StartAdvancedEraserOperation(sender);
|
||||
}
|
||||
};
|
||||
|
||||
overlay.MouseUp += (sender, e) => {
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
if (!MainWindow.EnablePalmEraser) return;
|
||||
var inkCanvas = this.FindName("inkCanvas") as InkCanvas;
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.None) {
|
||||
overlay.ReleaseMouseCapture();
|
||||
EndAdvancedEraserOperation(sender);
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink; // 抬手后自动回到画笔
|
||||
}
|
||||
};
|
||||
|
||||
overlay.MouseMove += (sender, e) => {
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
if (!MainWindow.EnablePalmEraser) return;
|
||||
var inkCanvas = this.FindName("inkCanvas") as InkCanvas;
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.None) {
|
||||
var position = e.GetPosition((UIElement)this.FindName("inkCanvas"));
|
||||
Trace.WriteLine($"Advanced Eraser: Mouse move event triggered at ({position.X:F1}, {position.Y:F1})");
|
||||
UpdateAdvancedEraserPosition(sender, position);
|
||||
} else {
|
||||
Trace.WriteLine($"Advanced Eraser: Mouse move ignored - not in eraser mode, current mode: {inkCanvas.EditingMode}");
|
||||
}
|
||||
};
|
||||
|
||||
// 触控笔事件
|
||||
overlay.StylusDown += (sender, e) => {
|
||||
if (!MainWindow.EnablePalmEraser) return;
|
||||
var inkCanvas = this.FindName("inkCanvas") as InkCanvas;
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
e.Handled = true;
|
||||
if (e.StylusDevice.TabletDevice.Type == TabletDeviceType.Stylus) {
|
||||
overlay.CaptureStylus();
|
||||
}
|
||||
lastEditingMode = inkCanvas.EditingMode;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
StartAdvancedEraserOperation(sender);
|
||||
}
|
||||
};
|
||||
|
||||
overlay.StylusUp += (sender, e) => {
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
if (!MainWindow.EnablePalmEraser) return;
|
||||
var inkCanvas = this.FindName("inkCanvas") as InkCanvas;
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.None) {
|
||||
e.Handled = true;
|
||||
if (e.StylusDevice.TabletDevice.Type == TabletDeviceType.Stylus) {
|
||||
overlay.ReleaseStylusCapture();
|
||||
}
|
||||
EndAdvancedEraserOperation(sender);
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink; // 抬手后自动回到画笔
|
||||
}
|
||||
};
|
||||
|
||||
overlay.StylusMove += (sender, e) => {
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
if (!MainWindow.EnablePalmEraser) return;
|
||||
var inkCanvas = this.FindName("inkCanvas") as InkCanvas;
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.None) {
|
||||
e.Handled = true;
|
||||
var position = e.GetPosition((UIElement)this.FindName("inkCanvas"));
|
||||
UpdateAdvancedEraserPosition(sender, position);
|
||||
|
||||
@@ -9,6 +9,9 @@ using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public class TimeViewModel : INotifyPropertyChanged {
|
||||
@@ -55,6 +58,35 @@ namespace Ink_Canvas {
|
||||
|
||||
private TimeViewModel nowTimeVM = new TimeViewModel();
|
||||
|
||||
private async Task<DateTime> GetNetworkTimeAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
const string ntpServer = "ntp.ntsc.ac.cn";
|
||||
var ntpData = new byte[48];
|
||||
ntpData[0] = 0x1B;
|
||||
var addresses = await Dns.GetHostAddressesAsync(ntpServer);
|
||||
var ipEndPoint = new IPEndPoint(addresses[0], 123);
|
||||
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
|
||||
{
|
||||
socket.ReceiveTimeout = 2000;
|
||||
socket.Connect(ipEndPoint);
|
||||
await Task.Factory.FromAsync(socket.BeginSend(ntpData, 0, ntpData.Length, SocketFlags.None, null, socket), socket.EndSend);
|
||||
await Task.Factory.FromAsync(socket.BeginReceive(ntpData, 0, ntpData.Length, SocketFlags.None, null, socket), socket.EndReceive);
|
||||
}
|
||||
const byte serverReplyTime = 40;
|
||||
ulong intPart = BitConverter.ToUInt32(ntpData.Skip(serverReplyTime).Take(4).Reverse().ToArray(), 0);
|
||||
ulong fractPart = BitConverter.ToUInt32(ntpData.Skip(serverReplyTime + 4).Take(4).Reverse().ToArray(), 0);
|
||||
var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
|
||||
var networkDateTime = (new DateTime(1900, 1, 1)).AddMilliseconds((long)milliseconds);
|
||||
return networkDateTime.ToLocalTime();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitTimers() {
|
||||
timerCheckPPT.Elapsed += TimerCheckPPT_Elapsed;
|
||||
timerCheckPPT.Interval = 500;
|
||||
@@ -66,7 +98,7 @@ namespace Ink_Canvas {
|
||||
timerCheckAutoUpdateWithSilence.Interval = 1000 * 60 * 10;
|
||||
WaterMarkTime.DataContext = nowTimeVM;
|
||||
WaterMarkDate.DataContext = nowTimeVM;
|
||||
timerDisplayTime.Elapsed += TimerDisplayTime_Elapsed;
|
||||
timerDisplayTime.Elapsed += async (s, e) => await TimerDisplayTime_ElapsedAsync();
|
||||
timerDisplayTime.Interval = 1000;
|
||||
timerDisplayTime.Start();
|
||||
timerDisplayDate.Elapsed += TimerDisplayDate_Elapsed;
|
||||
@@ -77,8 +109,14 @@ namespace Ink_Canvas {
|
||||
nowTimeVM.nowTime = DateTime.Now.ToShortTimeString().ToString();
|
||||
}
|
||||
|
||||
private void TimerDisplayTime_Elapsed(object sender, ElapsedEventArgs e) {
|
||||
nowTimeVM.nowTime = DateTime.Now.ToShortTimeString().ToString();
|
||||
private async Task TimerDisplayTime_ElapsedAsync()
|
||||
{
|
||||
DateTime now = await GetNetworkTimeAsync();
|
||||
// 只更新时间,日期由原有逻辑定时更新即可
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
nowTimeVM.nowTime = now.ToShortTimeString();
|
||||
});
|
||||
}
|
||||
|
||||
private void TimerDisplayDate_Elapsed(object sender, ElapsedEventArgs e) {
|
||||
|
||||
@@ -49,5 +49,5 @@ using System.Windows;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.7.1.10")]
|
||||
[assembly: AssemblyFileVersion("1.7.1.10")]
|
||||
[assembly: AssemblyVersion("1.7.1.12")]
|
||||
[assembly: AssemblyFileVersion("1.7.1.12")]
|
||||
|
||||
@@ -77,6 +77,8 @@ namespace Ink_Canvas
|
||||
public OptionalOperation HyperbolaAsymptoteOption { get; set; } = OptionalOperation.Ask;
|
||||
[JsonProperty("isCompressPicturesUploaded")]
|
||||
public bool IsCompressPicturesUploaded { get; set; } = false;
|
||||
[JsonProperty("enablePalmEraser")]
|
||||
public bool EnablePalmEraser { get; set; } = true;
|
||||
}
|
||||
|
||||
public enum OptionalOperation
|
||||
|
||||
@@ -78,6 +78,12 @@
|
||||
Background="#f8fafc" BorderBrush="#cbd5e1" ToolTip="跳过此版本更新" Visibility="Visible" IsEnabled="True"/>
|
||||
</ui:SimpleStackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 下载进度条和状态 -->
|
||||
<StackPanel x:Name="DownloadProgressPanel" Orientation="Vertical" HorizontalAlignment="Center" Margin="0,10,0,0" Visibility="Collapsed">
|
||||
<ProgressBar x:Name="DownloadProgressBar" Width="360" Height="18" Minimum="0" Maximum="100" Value="0"/>
|
||||
<TextBlock x:Name="DownloadProgressText" Text="正在下载..." FontSize="14" Foreground="#2563eb" HorizontalAlignment="Center" Margin="0,6,0,0"/>
|
||||
</StackPanel>
|
||||
</ui:SimpleStackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
|
||||
@@ -7,6 +7,9 @@ using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
@@ -129,16 +132,59 @@ namespace Ink_Canvas
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Update dialog buttons visibility ensured");
|
||||
}
|
||||
|
||||
private void UpdateNowButton_Click(object sender, RoutedEventArgs e)
|
||||
private async void UpdateNowButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Update Now button clicked");
|
||||
|
||||
// 禁用按钮,显示进度条
|
||||
UpdateNowButton.IsEnabled = false;
|
||||
UpdateLaterButton.IsEnabled = false;
|
||||
SkipVersionButton.IsEnabled = false;
|
||||
DownloadProgressPanel.Visibility = Visibility.Visible;
|
||||
DownloadProgressBar.Value = 0;
|
||||
DownloadProgressText.Text = "正在准备下载...";
|
||||
|
||||
// 启动多线路下载
|
||||
bool downloadSuccess = false;
|
||||
try
|
||||
{
|
||||
// 获取当前通道的所有线路组
|
||||
var groups = AutoUpdateHelper.ChannelLineGroups[MainWindow.Settings.Startup.UpdateChannel];
|
||||
downloadSuccess = await AutoUpdateHelper.DownloadSetupFileWithFallback(NewVersion, groups, (percent, text) =>
|
||||
{
|
||||
Dispatcher.Invoke(() => {
|
||||
DownloadProgressBar.Value = percent;
|
||||
DownloadProgressText.Text = text;
|
||||
});
|
||||
});
|
||||
if (downloadSuccess)
|
||||
{
|
||||
// 下载完成后自动安装
|
||||
await DownloadAndInstallVersion(NewVersion, null, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DownloadProgressText.Text = $"下载失败: {ex.Message}";
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 下载异常: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
if (downloadSuccess)
|
||||
{
|
||||
DownloadProgressBar.Value = 100;
|
||||
DownloadProgressText.Text = "下载完成,准备安装...";
|
||||
await Task.Delay(800);
|
||||
// 设置结果为立即更新
|
||||
Result = UpdateResult.UpdateNow;
|
||||
|
||||
// 关闭窗口,返回到MainWindow处理后续下载和安装流程
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
DownloadProgressText.Text = "下载失败,请检查网络后重试。";
|
||||
UpdateNowButton.IsEnabled = true;
|
||||
UpdateLaterButton.IsEnabled = true;
|
||||
SkipVersionButton.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLaterButton_Click(object sender, RoutedEventArgs e)
|
||||
@@ -279,5 +325,23 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 多线程分块下载并自动安装
|
||||
private async Task<bool> DownloadAndInstallVersion(string version, string downloadUrl, CancellationToken token)
|
||||
{
|
||||
if (string.IsNullOrEmpty(downloadUrl))
|
||||
{
|
||||
// 自动更新场景下,downloadUrl为null,直接用主下载目录
|
||||
string updatesFolderPath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "AutoUpdate");
|
||||
downloadUrl = Path.Combine(updatesFolderPath, $"InkCanvasForClass.CE.{version}.zip");
|
||||
}
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 开始安装版本: {version}");
|
||||
AutoUpdateHelper.InstallNewVersionApp(version, false);
|
||||
App.IsAppExitByUser = true;
|
||||
Application.Current.Dispatcher.Invoke(() => {
|
||||
Application.Current.Shutdown();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<Window x:Class="Ink_Canvas.HistoryRollbackWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
|
||||
xmlns:mdxam="clr-namespace:MdXaml;assembly=MdXaml"
|
||||
mc:Ignorable="d"
|
||||
Title="历史版本回滚" Height="600" Width="850" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
|
||||
<Grid Background="#fafafa">
|
||||
<ui:SimpleStackPanel VerticalAlignment="Stretch" Spacing="0">
|
||||
<TextBlock Text="选择历史版本进行回滚" FontSize="24" FontWeight="Bold" Foreground="#2563eb" Margin="24,24,0,12"/>
|
||||
<ComboBox x:Name="VersionComboBox" Width="400" Height="36" Margin="24,0,0,0" DisplayMemberPath="Version" SelectionChanged="VersionComboBox_SelectionChanged"/>
|
||||
<Border BorderBrush="#3f3f46" Background="White" BorderThickness="1" CornerRadius="4" Margin="24,16,24,0" Height="180">
|
||||
<mdxam:MarkdownScrollViewer x:Name="ReleaseNotesViewer" Foreground="Black" MarkdownStyleName="GithubLike"/>
|
||||
</Border>
|
||||
<Button x:Name="RollbackButton" Content="回滚到此版本" Width="360" Height="48" Margin="24,24,0,0" Click="RollbackButton_Click"/>
|
||||
<StackPanel x:Name="DownloadProgressPanel" Orientation="Vertical" HorizontalAlignment="Center" Margin="0,10,0,0" Visibility="Collapsed">
|
||||
<ProgressBar x:Name="DownloadProgressBar" Width="360" Height="18" Minimum="0" Maximum="100" Value="0"/>
|
||||
<TextBlock x:Name="DownloadProgressText" Text="正在下载..." FontSize="14" Foreground="#2563eb" HorizontalAlignment="Center" Margin="0,6,0,0"/>
|
||||
</StackPanel>
|
||||
</ui:SimpleStackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,142 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Linq; // Added for OrderByDescending
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
public partial class HistoryRollbackWindow : Window
|
||||
{
|
||||
private class VersionItem
|
||||
{
|
||||
public string Version { get; set; }
|
||||
public string DownloadUrl { get; set; }
|
||||
public string ReleaseNotes { get; set; }
|
||||
}
|
||||
|
||||
private List<VersionItem> versionList = new List<VersionItem>();
|
||||
private VersionItem selectedItem = null;
|
||||
private UpdateChannel channel = UpdateChannel.Release;
|
||||
private CancellationTokenSource downloadCts = null;
|
||||
|
||||
public HistoryRollbackWindow(UpdateChannel channel = UpdateChannel.Release)
|
||||
{
|
||||
InitializeComponent();
|
||||
this.channel = channel;
|
||||
LoadVersions();
|
||||
}
|
||||
|
||||
private async void LoadVersions()
|
||||
{
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 开始加载历史版本,通道: {channel}");
|
||||
RollbackButton.IsEnabled = false;
|
||||
VersionComboBox.Items.Clear();
|
||||
DownloadProgressPanel.Visibility = Visibility.Collapsed;
|
||||
DownloadProgressBar.Value = 0;
|
||||
DownloadProgressText.Text = "";
|
||||
ReleaseNotesViewer.Markdown = "正在获取历史版本...";
|
||||
var releases = await AutoUpdateHelper.GetAllGithubReleases(channel);
|
||||
versionList.Clear();
|
||||
foreach (var (version, url, notes) in releases)
|
||||
{
|
||||
versionList.Add(new VersionItem { Version = version, DownloadUrl = url, ReleaseNotes = notes });
|
||||
}
|
||||
// 按版本号数字降序排列
|
||||
versionList = versionList.OrderByDescending(v => ParseVersionForSort(v.Version)).ToList();
|
||||
VersionComboBox.ItemsSource = versionList;
|
||||
if (versionList.Count > 0)
|
||||
{
|
||||
VersionComboBox.SelectedIndex = 0;
|
||||
RollbackButton.IsEnabled = true;
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 加载到 {versionList.Count} 个历史版本");
|
||||
}
|
||||
else
|
||||
{
|
||||
ReleaseNotesViewer.Markdown = "未获取到历史版本信息。";
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 未获取到历史版本信息", LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助方法:解析版本号用于排序
|
||||
private Version ParseVersionForSort(string version)
|
||||
{
|
||||
var v = version.TrimStart('v', 'V');
|
||||
Version result;
|
||||
if (Version.TryParse(v, out result))
|
||||
return result;
|
||||
return new Version(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
private void VersionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
selectedItem = VersionComboBox.SelectedItem as VersionItem;
|
||||
if (selectedItem != null)
|
||||
{
|
||||
ReleaseNotesViewer.Markdown = selectedItem.ReleaseNotes ?? "无更新日志";
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 用户选择版本: {selectedItem.Version}");
|
||||
}
|
||||
// 取消聚焦,防止父级自动滚动
|
||||
Keyboard.ClearFocus();
|
||||
}
|
||||
|
||||
private async void RollbackButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (selectedItem == null) return;
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 用户点击回滚,目标版本: {selectedItem.Version}");
|
||||
RollbackButton.IsEnabled = false;
|
||||
VersionComboBox.IsEnabled = false;
|
||||
DownloadProgressPanel.Visibility = Visibility.Visible;
|
||||
DownloadProgressBar.Value = 0;
|
||||
DownloadProgressText.Text = "正在准备下载...";
|
||||
|
||||
bool downloadSuccess = false;
|
||||
try
|
||||
{
|
||||
downloadSuccess = await AutoUpdateHelper.StartManualDownloadAndInstall(
|
||||
selectedItem.Version,
|
||||
channel,
|
||||
(percent, text) =>
|
||||
{
|
||||
Dispatcher.Invoke(() => {
|
||||
DownloadProgressBar.Value = percent;
|
||||
DownloadProgressText.Text = text;
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DownloadProgressText.Text = $"下载失败: {ex.Message}";
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 下载异常: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
if (downloadSuccess)
|
||||
{
|
||||
DownloadProgressBar.Value = 100;
|
||||
DownloadProgressText.Text = "下载完成,准备安装...";
|
||||
await Task.Delay(800);
|
||||
this.DialogResult = true;
|
||||
this.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
DownloadProgressText.Text = "下载失败,请检查网络后重试。";
|
||||
RollbackButton.IsEnabled = true;
|
||||
VersionComboBox.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnClosing(CancelEventArgs e)
|
||||
{
|
||||
downloadCts?.Cancel();
|
||||
base.OnClosing(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,11 @@ none
|
||||
false
|
||||
TRACE;DEBUG;NETFRAMEWORK;NET472;;NET30_OR_GREATER;NET35_OR_GREATER;NET40_OR_GREATER;NET45_OR_GREATER;NET451_OR_GREATER;NET452_OR_GREATER;NET46_OR_GREATER;NET461_OR_GREATER;NET462_OR_GREATER;NET47_OR_GREATER;NET471_OR_GREATER;NET472_OR_GREATER
|
||||
E:\ICC CE\ICC CE main\community\Ink Canvas\App.xaml
|
||||
21348134359
|
||||
22-2143008179
|
||||
|
||||
752071346691
|
||||
76-141727233
|
||||
471037513499
|
||||
Helpers\Plugins\BuiltIn\SuperLauncher\LauncherSettingsControl.xaml;Helpers\Plugins\BuiltIn\SuperLauncher\LauncherWindow.xaml;MainWindow.xaml;MainWindow_cs\MW_Eraser.xaml;Resources\DrawShapeImageDictionary.xaml;Resources\IconImageDictionary.xaml;Resources\SeewoImageDictionary.xaml;Resources\Styles\Dark.xaml;Resources\Styles\Light.xaml;Windows\AddCustomIconWindow.xaml;Windows\AddPickNameBackgroundWindow.xaml;Windows\CountdownTimerWindow.xaml;Windows\CustomIconWindow.xaml;Windows\CycleProcessBar.xaml;Windows\HasNewUpdateWindow.xaml;Windows\ManagePickNameBackgroundsWindow.xaml;Windows\NamesInputWindow.xaml;Windows\OperatingGuideWindow.xaml;Windows\PluginSettingsWindow.xaml;Windows\RandWindow.xaml;Windows\YesOrNoNotificationWindow.xaml;
|
||||
Helpers\Plugins\BuiltIn\SuperLauncher\LauncherSettingsControl.xaml;Helpers\Plugins\BuiltIn\SuperLauncher\LauncherWindow.xaml;MainWindow.xaml;MainWindow_cs\MW_Eraser.xaml;Resources\DrawShapeImageDictionary.xaml;Resources\IconImageDictionary.xaml;Resources\SeewoImageDictionary.xaml;Resources\Styles\Dark.xaml;Resources\Styles\Light.xaml;Windows\AddCustomIconWindow.xaml;Windows\AddPickNameBackgroundWindow.xaml;Windows\CountdownTimerWindow.xaml;Windows\CustomIconWindow.xaml;Windows\CycleProcessBar.xaml;Windows\HasNewUpdateWindow.xaml;Windows\HistoryRollbackWindow.xaml;Windows\ManagePickNameBackgroundsWindow.xaml;Windows\NamesInputWindow.xaml;Windows\OperatingGuideWindow.xaml;Windows\PluginSettingsWindow.xaml;Windows\RandWindow.xaml;Windows\YesOrNoNotificationWindow.xaml;
|
||||
|
||||
False
|
||||
|
||||
|
||||
Reference in New Issue
Block a user