Compare commits

...

No commits in common. "CheckDownload" and "CreateMd5Json" have entirely different histories.

13 changed files with 361 additions and 584 deletions

197
Form1.Designer.cs generated
View File

@ -1,6 +1,6 @@
namespace CheckDownload
namespace MD5Create
{
partial class Update
partial class MD5Create
{
/// <summary>
/// 必需的设计器变量。
@ -28,53 +28,158 @@
/// </summary>
private void InitializeComponent()
{
this.Update_Text = new System.Windows.Forms.Label();
this.Update_Pro = new System.Windows.Forms.ProgressBar();
this.Status_Box = new System.Windows.Forms.Label();
this.Path_Text = new System.Windows.Forms.Label();
this.Path1_Box = new System.Windows.Forms.TextBox();
this.Choose1_Button = new System.Windows.Forms.Button();
this.folderBrowserDialog1 = new System.Windows.Forms.FolderBrowserDialog();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.progressBar1 = new System.Windows.Forms.ProgressBar();
this.Start_Button = new System.Windows.Forms.Button();
this.Process_Text = new System.Windows.Forms.Label();
this.Process_Box = new System.Windows.Forms.RichTextBox();
this.Exit_Button = new System.Windows.Forms.Button();
this.label3 = new System.Windows.Forms.Label();
this.textBox1 = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// Update_Text
// Path_Text
//
this.Update_Text.AutoSize = true;
this.Update_Text.Location = new System.Drawing.Point(12, 12);
this.Update_Text.Name = "Update_Text";
this.Update_Text.Size = new System.Drawing.Size(59, 12);
this.Update_Text.TabIndex = 0;
this.Update_Text.Text = "更新状态:";
this.Path_Text.AutoSize = true;
this.Path_Text.Location = new System.Drawing.Point(12, 15);
this.Path_Text.Name = "Path_Text";
this.Path_Text.Size = new System.Drawing.Size(35, 12);
this.Path_Text.TabIndex = 0;
this.Path_Text.Text = "路径:";
//
// Update_Pro
// Path1_Box
//
this.Update_Pro.Location = new System.Drawing.Point(12, 36);
this.Update_Pro.Name = "Update_Pro";
this.Update_Pro.Size = new System.Drawing.Size(339, 23);
this.Update_Pro.TabIndex = 1;
this.Path1_Box.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.Path1_Box.Location = new System.Drawing.Point(49, 12);
this.Path1_Box.Name = "Path1_Box";
this.Path1_Box.ReadOnly = true;
this.Path1_Box.Size = new System.Drawing.Size(319, 21);
this.Path1_Box.TabIndex = 1;
//
// Status_Box
// Choose1_Button
//
this.Status_Box.AutoSize = true;
this.Status_Box.Location = new System.Drawing.Point(72, 12);
this.Status_Box.Name = "Status_Box";
this.Status_Box.Size = new System.Drawing.Size(23, 12);
this.Status_Box.TabIndex = 2;
this.Status_Box.Text = "...";
this.Choose1_Button.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.Choose1_Button.Location = new System.Drawing.Point(374, 12);
this.Choose1_Button.Name = "Choose1_Button";
this.Choose1_Button.Size = new System.Drawing.Size(32, 21);
this.Choose1_Button.TabIndex = 2;
this.Choose1_Button.Text = "...";
this.Choose1_Button.UseVisualStyleBackColor = true;
this.Choose1_Button.Click += new System.EventHandler(this.Choose1_Button_Click);
//
// Update
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 53);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(0, 12);
this.label1.TabIndex = 3;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(12, 79);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(35, 12);
this.label2.TabIndex = 4;
this.label2.Text = "进度:";
//
// progressBar1
//
this.progressBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.progressBar1.Location = new System.Drawing.Point(49, 76);
this.progressBar1.Name = "progressBar1";
this.progressBar1.Size = new System.Drawing.Size(357, 23);
this.progressBar1.TabIndex = 5;
//
// Start_Button
//
this.Start_Button.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.Start_Button.Location = new System.Drawing.Point(12, 322);
this.Start_Button.Name = "Start_Button";
this.Start_Button.Size = new System.Drawing.Size(75, 23);
this.Start_Button.TabIndex = 6;
this.Start_Button.Text = "开始";
this.Start_Button.UseVisualStyleBackColor = true;
this.Start_Button.Click += new System.EventHandler(this.Start_Button_Click);
//
// Process_Text
//
this.Process_Text.AutoSize = true;
this.Process_Text.Location = new System.Drawing.Point(12, 47);
this.Process_Text.Name = "Process_Text";
this.Process_Text.Size = new System.Drawing.Size(35, 12);
this.Process_Text.TabIndex = 7;
this.Process_Text.Text = "版本:";
//
// Process_Box
//
this.Process_Box.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.Process_Box.Location = new System.Drawing.Point(49, 111);
this.Process_Box.Name = "Process_Box";
this.Process_Box.Size = new System.Drawing.Size(357, 203);
this.Process_Box.TabIndex = 8;
this.Process_Box.Text = "";
//
// Exit_Button
//
this.Exit_Button.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.Exit_Button.Location = new System.Drawing.Point(331, 322);
this.Exit_Button.Name = "Exit_Button";
this.Exit_Button.Size = new System.Drawing.Size(75, 23);
this.Exit_Button.TabIndex = 9;
this.Exit_Button.Text = "退出";
this.Exit_Button.UseVisualStyleBackColor = true;
this.Exit_Button.Click += new System.EventHandler(this.Exit_Button_Click);
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(12, 111);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(35, 12);
this.label3.TabIndex = 10;
this.label3.Text = "执行:";
//
// textBox1
//
this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBox1.ForeColor = System.Drawing.SystemColors.WindowText;
this.textBox1.Location = new System.Drawing.Point(49, 42);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(357, 21);
this.textBox1.TabIndex = 11;
this.textBox1.Text = "1.0.0";
//
// MD5Create
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(363, 73);
this.ControlBox = false;
this.Controls.Add(this.Status_Box);
this.Controls.Add(this.Update_Pro);
this.Controls.Add(this.Update_Text);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Update";
this.ShowInTaskbar = false;
this.Text = "自动更新";
this.TopMost = true;
this.Load += new System.EventHandler(this.Update_Load);
this.ClientSize = new System.Drawing.Size(418, 357);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.label3);
this.Controls.Add(this.Exit_Button);
this.Controls.Add(this.Process_Box);
this.Controls.Add(this.Process_Text);
this.Controls.Add(this.Start_Button);
this.Controls.Add(this.progressBar1);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.Choose1_Button);
this.Controls.Add(this.Path1_Box);
this.Controls.Add(this.Path_Text);
this.Name = "MD5Create";
this.Text = "MD5Create";
this.ResumeLayout(false);
this.PerformLayout();
@ -82,9 +187,19 @@
#endregion
private System.Windows.Forms.Label Update_Text;
private System.Windows.Forms.ProgressBar Update_Pro;
private System.Windows.Forms.Label Status_Box;
private System.Windows.Forms.Label Path_Text;
private System.Windows.Forms.TextBox Path1_Box;
private System.Windows.Forms.Button Choose1_Button;
private System.Windows.Forms.FolderBrowserDialog folderBrowserDialog1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Button Start_Button;
private System.Windows.Forms.Label Process_Text;
private System.Windows.Forms.RichTextBox Process_Box;
private System.Windows.Forms.Button Exit_Button;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.TextBox textBox1;
}
}

608
Form1.cs
View File

@ -2,521 +2,241 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Aliyun.OSS;
using Aliyun.OSS.Common;
using System.Xml;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace CheckDownload
namespace MD5Create
{
public partial class Update : Form
public partial class MD5Create : Form
{
// MD5文件名称
private const string Md5File = "md5.json";
// 阿里云OSS访问地址
private const string OssEndpoint = "oss-cn-hongkong.aliyuncs.com";
// 阿里云OSS存储空间名称
private const string OssBucketName = "sxho-hk";
// 阿里云OSS访问密钥ID
private const string OssAccessKeyId = "LTAI5tBc8BgooVrHrzfEeg1q";
// 阿里云OSS访问密钥Secret
private const string OssAccessKeySecret = "nMvSp0UqzodTzKImuAMK1a1bkSks5O";
// 用于存储下载的文件数据
private Dictionary<string, (byte[] Data, string ExpectedMd5)> _downloadedFiles = new Dictionary<string, (byte[], string)>();
// 已完成的下载数量
private int _completedCount = 0;
// 总下载数量
private int _totalCount = 0;
// 初始化窗体
public Update()
public MD5Create()
{
InitializeComponent();
ConfigureProgressBar();
// Initialize progress bar
progressBar1.Minimum = 0;
progressBar1.Maximum = 100;
progressBar1.Value = 0;
progressBar1.Step = 1;
}
// 配置进度条
private void ConfigureProgressBar()
// 程序启动前检查目录和版本号
private void Start_Button_Click(object sender, EventArgs e)
{
Update_Pro.Minimum = 0;
Update_Pro.Maximum = 100;
Update_Pro.Value = 0;
Update_Pro.Step = 1;
}
private void UpdateProgressValue(int percentage)
if (string.IsNullOrWhiteSpace(Path1_Box.Text))
{
if (percentage < 0) percentage = 0;
if (percentage > 100) percentage = 100;
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
Update_Pro.Value = Math.Min(Math.Max(percentage, Update_Pro.Minimum), Update_Pro.Maximum);
Application.DoEvents();
});
}
else
{
Update_Pro.Value = Math.Min(Math.Max(percentage, Update_Pro.Minimum), Update_Pro.Maximum);
Application.DoEvents();
}
}
private void UpdateStatus(string message)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate { Status_Box.Text = message; });
}
else
{
Status_Box.Text = message;
}
}
public async void Update_Load(object sender, EventArgs e)
{
PositionFormToBottomRight();
await UpdateFile();
}
private void PositionFormToBottomRight()
{
Rectangle workingArea = Screen.GetWorkingArea(this);
this.Location = new Point(workingArea.Right - this.Width, workingArea.Bottom - this.Height);
}
private async Task UpdateFile()
{
try
{
const int totalSteps = 4;
int currentStep = 0;
UpdateStatus("下载在线MD5文件并读取...");
OssClient client = new OssClient(OssEndpoint, OssAccessKeyId, OssAccessKeySecret);
var onlineData = ReadOnlineMd5File(client, OssBucketName, Md5File);
if (!ValidateOnlineData(onlineData.Version, onlineData.Md5, onlineData.Data))
{
UpdateStatus("在线MD5文件无效");
await Task.Delay(3000);
this.Close();
MessageBox.Show("请先选择目录!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
UpdateStatus("比较本地和在线MD5文件...");
var compareResult = CompareMd5Data(onlineData.Data);
if (compareResult.Count == 0)
if (string.IsNullOrWhiteSpace(textBox1.Text))
{
UpdateStatus("所有文件都是最新的,无需更新");
UpdateProgressValue(100);
await Task.Delay(2000);
this.Close();
MessageBox.Show("请输入版本号!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
UpdateStatus("下载并验证文件...");
_totalCount = compareResult.Count;
await DownloadAndVerifyFiles(compareResult);
UpdateProgressValue(100);
// Reset progress bar
progressBar1.Value = 0;
UpdateStatus("更新完成");
await Task.Delay(1000);
GenerateMD5Json(Path1_Box.Text);
}
private void Choose1_Button_Click(object sender, EventArgs e)
{
folderBrowserDialog1.Description = "请选择文件夹";
folderBrowserDialog1.RootFolder = Environment.SpecialFolder.MyComputer;
folderBrowserDialog1.ShowNewFolderButton = true;
if (Path1_Box.Text.Length > 0) folderBrowserDialog1.SelectedPath = Path1_Box.Text;
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
{
Path1_Box.Text = folderBrowserDialog1.SelectedPath;
}
}
private void Exit_Button_Click(object sender, EventArgs e)
{
this.Close();
}
catch (Exception ex)
// 计算 MD5 的值
private string MD5Generation(string filePath)
{
UpdateStatus($"更新失败: {ex.Message}");
await Task.Delay(3000);
this.Close();
using (var md5 = MD5.Create())
using (var stream = File.OpenRead(filePath))
{
byte[] hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLower();
}
}
private (string Version, string Md5, JObject Data) ReadOnlineMd5File(OssClient client, string bucket, string key)
// 获取目录中文件总数(用于进度计算)
private int GetTotalFileCount(string directoryPath, string excludeFolder)
{
try
{
var obj = client.GetObject(bucket, key);
using (var reader = new StreamReader(obj.Content))
{
string json = reader.ReadToEnd();
var parsed = JObject.Parse(json);
string version = parsed["version"]?.ToString();
var data = (JObject)parsed["data"];
string jsonMd5 = CalculateMD5(json);
return (version, jsonMd5, data);
}
}
catch (Exception ex) when (ex is OssException || ex is JsonException)
{
UpdateStatus($"读取在线MD5文件失败: {ex.Message}");
return (null, null, null);
}
}
int count = Directory.GetFiles(directoryPath).Length;
private bool ValidateOnlineData(string version, string md5, JObject data)
foreach (var dir in Directory.GetDirectories(directoryPath))
{
if (string.IsNullOrEmpty(version) || string.IsNullOrEmpty(md5) || data == null)
{
UpdateStatus("在线MD5文件无效");
return false;
}
return true;
}
private Dictionary<string, string> CompareMd5Data(JObject onlineData, string currentPath = "")
{
var differences = new Dictionary<string, string>();
foreach (var onlineProperty in onlineData.Properties())
{
string key = onlineProperty.Name;
JToken onlineValue = onlineProperty.Value;
string fullPath = string.IsNullOrEmpty(currentPath) ? key : $"{currentPath}/{key}";
if (onlineValue.Type == JTokenType.String)
{
string expectedMd5 = onlineValue.ToString();
if (!File.Exists(fullPath))
{
if (!differences.ContainsKey(fullPath))
{
differences[fullPath] = expectedMd5;
}
}
else
{
string localMd5 = CalculateMD5(File.ReadAllBytes(fullPath));
if (localMd5 != expectedMd5)
{
if (!differences.ContainsKey(fullPath))
{
differences[fullPath] = expectedMd5;
}
}
}
}
else if (onlineValue.Type == JTokenType.Object)
{
var subDifferences = CompareMd5Data((JObject)onlineValue, fullPath);
foreach (var diff in subDifferences)
{
if (!differences.ContainsKey(diff.Key))
{
differences[diff.Key] = diff.Value;
}
}
}
}
return differences;
}
private async Task DownloadAndVerifyFiles(Dictionary<string, string> fileList)
{
try
{
OssClient client = new OssClient(OssEndpoint, OssAccessKeyId, OssAccessKeySecret);
_totalCount = fileList.Count;
_completedCount = 0;
_downloadedFiles.Clear();
var options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
await Task.Run(() =>
{
Parallel.ForEach(fileList, options, (file, state) =>
{
string filePath = file.Key;
string expectedMd5 = file.Value;
try
{
string ossKey = $"File/{expectedMd5}";
var obj = client.GetObject(OssBucketName, ossKey);
using (var memoryStream = new MemoryStream())
{
obj.Content.CopyTo(memoryStream);
byte[] fileData = memoryStream.ToArray();
lock (_downloadedFiles)
{
_downloadedFiles[filePath] = (fileData, expectedMd5);
}
Interlocked.Increment(ref _completedCount);
int progress = (int)((double)_completedCount / _totalCount * 95);
UpdateProgressValue(progress);
UpdateStatus($"已下载 {_completedCount}/{_totalCount}");
}
}
catch (Exception ex)
{
UpdateStatus($"下载文件 {filePath} 失败: {ex.Message}");
}
});
});
if (_downloadedFiles.Count != fileList.Count)
{
throw new Exception($"部分文件下载失败,成功 {_downloadedFiles.Count}/{fileList.Count}");
}
VerifyAndSaveAllFiles();
}
catch (Exception ex)
{
UpdateStatus($"下载过程出错: {ex.Message}");
throw;
}
}
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetProcessHandleCount(IntPtr hProcess, out uint pdwHandleCount);
private void VerifyAndSaveAllFiles()
{
UpdateStatus("正在校验文件...");
var failedFiles = new List<string>();
int processedCount = 0;
int totalFiles = _downloadedFiles.Count;
foreach (var item in _downloadedFiles)
{
string relativePath = item.Key;
byte[] data = item.Value.Data;
string expectedMd5 = item.Value.ExpectedMd5;
try
{
processedCount++;
UpdateStatus($"正在校验和保存文件 ({processedCount}/{totalFiles}): {relativePath}");
string actualMd5 = CalculateMD5(data);
if (actualMd5 != expectedMd5.ToLower())
{
throw new Exception($"MD5校验失败 (期望: {expectedMd5}, 实际: {actualMd5})");
}
string localPath = Path.Combine(Application.StartupPath, relativePath);
Directory.CreateDirectory(Path.GetDirectoryName(localPath));
// 尝试保存文件,如果文件被占用则尝试解锁
if (!TrySaveFile(localPath, data))
{
// 如果无法解锁文件,则使用备用文件名保存
string backupPath = localPath + ".new";
File.WriteAllBytes(backupPath, data);
// 创建一个批处理文件,在应用程序退出后替换文件
CreateReplaceScript(localPath, backupPath);
UpdateStatus($"文件 {relativePath} 正在被占用,将在程序重启后更新");
}
int percentage = 95 + (int)(processedCount * 5.0 / totalFiles);
UpdateProgressValue(Math.Min(100, percentage));
}
catch (Exception ex)
{
UpdateStatus($"文件校验失败: {relativePath} - {ex.Message}");
failedFiles.Add(relativePath);
}
}
foreach (var failedFile in failedFiles)
{
_downloadedFiles.Remove(failedFile);
}
if (failedFiles.Count > 0)
{
throw new Exception($"{failedFiles.Count}个文件校验失败");
}
else
{
UpdateStatus("所有文件校验和保存成功");
UpdateProgressValue(100);
}
}
private bool TrySaveFile(string filePath, byte[] data)
{
try
{
// 直接尝试写入文件
File.WriteAllBytes(filePath, data);
return true;
}
catch (IOException)
{
// 文件被占用,尝试解锁
UpdateStatus($"文件 {Path.GetFileName(filePath)} 被占用,尝试解锁...");
try
{
// 尝试找出占用文件的进程
var processes = Process.GetProcesses();
foreach (var process in processes)
{
try
{
// 跳过当前进程
if (process.Id == Process.GetCurrentProcess().Id)
if (dir.Equals(excludeFolder, StringComparison.OrdinalIgnoreCase))
continue;
// 检查进程是否可能占用了文件
string processName = process.ProcessName.ToLower();
if (processName.Contains("explorer") ||
processName.Contains("chrome") ||
processName.Contains("edge") ||
processName.Contains("firefox") ||
processName.Contains("iexplore") ||
processName.Contains("winword") ||
processName.Contains("excel") ||
processName.Contains("powerpnt") ||
processName.Contains("acrobat") ||
processName.Contains("reader"))
{
// 跳过常见的系统进程
continue;
count += GetTotalFileCount(dir, excludeFolder);
}
return count;
}
// 尝试获取进程句柄数量,如果句柄数量较多,可能正在使用文件
uint handleCount = 0;
if (GetProcessHandleCount(process.Handle, out handleCount) && handleCount > 0)
// 生成目录结构的 json 表示
private Dictionary<string, object> GenerateDirectoryJson(string directoryPath, string rootPath, int totalFiles, ref int processedFiles, string excludeFolder, string fileFolder)
{
// 尝试关闭进程
UpdateStatus($"尝试关闭可能占用文件的进程: {process.ProcessName}");
process.CloseMainWindow();
var result = new Dictionary<string, object>();
// 等待进程关闭
if (!process.WaitForExit(1000))
// 处理当前目录文件
foreach (var file in Directory.GetFiles(directoryPath))
{
// 如果进程没有响应,尝试强制结束
process.Kill();
}
}
}
catch
{
// 忽略处理单个进程时的错误
}
finally
{
process.Dispose();
}
}
// 计算文件MD5
string fileMD5 = MD5Generation(file);
string fileName = Path.GetFileName(file);
result[fileName] = fileMD5;
// 再次尝试写入文件
Thread.Sleep(500); // 等待一段时间确保文件已释放
File.WriteAllBytes(filePath, data);
UpdateStatus($"文件 {Path.GetFileName(filePath)} 解锁成功并已更新");
return true;
}
catch
try
{
// 如果仍然无法写入,返回失败
return false;
}
// 复制文件到fileFolder目录以MD5值命名且不保留后缀
string destPath = Path.Combine(fileFolder, fileMD5);
File.Copy(file, destPath, overwrite: true);
Process_Box.Invoke((MethodInvoker)(() =>
{
Process_Box.AppendText($"已复制: {file} -> {destPath}{Environment.NewLine}");
}));
}
catch (Exception ex)
{
UpdateStatus($"保存文件时发生错误: {ex.Message}");
return false;
}
Process_Box.Invoke((MethodInvoker)(() =>
{
Process_Box.AppendText($"复制文件失败 {file}: {ex.Message}{Environment.NewLine}");
}));
}
private void CreateReplaceScript(string originalFile, string newFile)
// 更新进度条
processedFiles++;
int progress = (int)((double)processedFiles / totalFiles * 100);
progressBar1.Invoke((MethodInvoker)(() =>
{
progressBar1.Value = Math.Min(progress, progressBar1.Maximum);
}));
// 显示处理路径
string relativePath = GetRelativePath(file, rootPath);
Process_Box.Invoke((MethodInvoker)(() =>
{
Process_Box.AppendText($".\\{relativePath}{Environment.NewLine}");
}));
}
// 递归处理子目录(自动跳过排除文件夹)
foreach (var dir in Directory.GetDirectories(directoryPath))
{
if (Path.GetFullPath(dir).Equals(Path.GetFullPath(excludeFolder), StringComparison.OrdinalIgnoreCase))
continue;
string dirName = Path.GetFileName(dir);
result[dirName] = GenerateDirectoryJson(dir, rootPath, totalFiles, ref processedFiles, excludeFolder, fileFolder);
}
return result;
}
// 获取相对路径
private string GetRelativePath(string fullPath, string basePath)
{
if (!basePath.EndsWith(Path.DirectorySeparatorChar.ToString()))
basePath += Path.DirectorySeparatorChar;
Uri baseUri = new Uri(basePath);
Uri fullUri = new Uri(fullPath);
return Uri.UnescapeDataString(baseUri.MakeRelativeUri(fullUri).ToString()
.Replace('/', Path.DirectorySeparatorChar));
}
// 生成 json 文件
private void GenerateMD5Json(string rootDirectory)
{
if (!Directory.Exists(rootDirectory))
{
MessageBox.Show("指定的目录不存在!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
try
{
string batchFilePath = Path.Combine(Application.StartupPath, "update_files.bat");
string processId = Process.GetCurrentProcess().Id.ToString();
string escapedOriginalFile = originalFile.Replace("\\", "\\\\");
string escapedNewFile = newFile.Replace("\\", "\\\\");
Process_Box.Clear();
progressBar1.Value = 0;
Application.DoEvents();
// 创建批处理文件内容
StringBuilder batchContent = new StringBuilder();
batchContent.AppendLine("@echo off");
batchContent.AppendLine(":check_process");
batchContent.AppendLine($"tasklist /FI \"PID eq {processId}\" 2>NUL | find /I \"{ processId }\" >NUL");
batchContent.AppendLine("if %ERRORLEVEL% == 0 (");
batchContent.AppendLine(" timeout /t 1 /nobreak > NUL");
batchContent.AppendLine(" goto check_process");
batchContent.AppendLine(")");
batchContent.AppendLine("timeout /t 2 /nobreak > NUL");
batchContent.AppendLine($"del \"{escapedOriginalFile}\" /f /q");
batchContent.AppendLine($"move \"{escapedNewFile}\" \"{escapedOriginalFile}\"");
batchContent.AppendLine("del \"%~f0\" /f /q");
// 存储MD5值
string md5FolderPath = Path.Combine(rootDirectory, "MD5");
Directory.CreateDirectory(md5FolderPath);
// 存储MD5值对应的文件
string fileFolderPath = Path.Combine(md5FolderPath, "File");
Directory.CreateDirectory(fileFolderPath);
// 写入批处理文件
bool fileExists = File.Exists(batchFilePath);
using (StreamWriter writer = new StreamWriter(batchFilePath, fileExists))
int totalFiles = GetTotalFileCount(rootDirectory, md5FolderPath);
int processedFiles = 0;
// 如果文件数量为0直接设置进度为100%
if (totalFiles == 0)
{
if (!fileExists)
{
writer.Write(batchContent.ToString());
}
else
{
// 如果文件已存在,只添加移动文件的命令
writer.WriteLine($"del \"{escapedOriginalFile}\" /f /q");
writer.WriteLine($"move \"{escapedNewFile}\" \"{escapedOriginalFile}\"");
}
progressBar1.Value = 100;
Process_Box.AppendText("没有找到任何文件\n");
return;
}
// 如果是第一次创建批处理文件,启动它
if (!fileExists)
var directoryStructure = GenerateDirectoryJson(rootDirectory, rootDirectory, totalFiles, ref processedFiles, md5FolderPath, fileFolderPath);
// 创建包含版本号的新结构
var versionedStructure = new Dictionary<string, object>
{
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $"/c start \"\" /min \"{batchFilePath}\"",
CreateNoWindow = true,
UseShellExecute = true,
WindowStyle = ProcessWindowStyle.Hidden
{ "version", textBox1.Text.Trim() },
{ "data", directoryStructure }
};
Process.Start(startInfo);
}
// 序列化和保存JSON
string json = JsonConvert.SerializeObject(versionedStructure, Newtonsoft.Json.Formatting.Indented);
string jsonMD5 = CalculateMD5FromString(json);
string outputPath = Path.Combine(md5FolderPath, "md5.json");
File.WriteAllText(outputPath, json);
// 最终更新
progressBar1.Value = 100;
Process_Box.AppendText($"\nmd5.json文件已生成在{outputPath}\n");
Process_Box.AppendText($"JSON内容MD5校验值{jsonMD5}");
Process_Box.ScrollToCaret();
}
catch (Exception ex)
{
UpdateStatus($"创建替换脚本时出错: {ex.Message}");
MessageBox.Show($"生成JSON文件时出错{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private string CalculateMD5(string input)
// 计算字符串的MD5值
private string CalculateMD5FromString(string input)
{
using (var md5 = MD5.Create())
{
byte[] inputBytes = Encoding.UTF8.GetBytes(input);
byte[] hashBytes = md5.ComputeHash(inputBytes);
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
}
}
private string CalculateMD5(byte[] data)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hashBytes.Length; i++)
{
using (var md5 = MD5.Create())
{
byte[] hashBytes = md5.ComputeHash(data);
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
sb.Append(hashBytes[i].ToString("x2")); // 使用小写十六进制格式
}
return sb.ToString();
}
}
}

View File

@ -117,4 +117,7 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="folderBrowserDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View File

@ -1,14 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="packages\Costura.Fody.6.0.0\build\Costura.Fody.props" Condition="Exists('packages\Costura.Fody.6.0.0\build\Costura.Fody.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{F4681804-7E5A-4A02-87EF-B28E34AA443B}</ProjectGuid>
<ProjectGuid>{353C07CE-BA22-44F3-8816-5F97E2B9DDE0}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>CheckDownload</RootNamespace>
<AssemblyName>CheckDownload</AssemblyName>
<RootNamespace>MD5Create</RootNamespace>
<AssemblyName>MD5Create</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
@ -24,12 +23,11 @@
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationRevision>2</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
<BootstrapperEnabled>true</BootstrapperEnabled>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@ -50,23 +48,23 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ManifestCertificateThumbprint>5F728211560A8301D63BE7DD69560329B0CAB9FC</ManifestCertificateThumbprint>
</PropertyGroup>
<PropertyGroup>
<ManifestKeyFile>MD5Create_TemporaryKey.pfx</ManifestKeyFile>
</PropertyGroup>
<PropertyGroup>
<GenerateManifests>true</GenerateManifests>
</PropertyGroup>
<PropertyGroup>
<SignManifests>true</SignManifests>
</PropertyGroup>
<ItemGroup>
<Reference Include="Aliyun.OSS, Version=2.14.1.0, Culture=neutral, PublicKeyToken=0ad4175f0dac0b9b, processorArchitecture=MSIL">
<HintPath>packages\Aliyun.OSS.SDK.2.14.1\lib\net461\Aliyun.OSS.dll</HintPath>
</Reference>
<Reference Include="Costura, Version=6.0.0.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
<HintPath>packages\Costura.Fody.6.0.0\lib\netstandard2.0\Costura.dll</HintPath>
</Reference>
<Reference Include="DnsClient, Version=1.8.0.0, Culture=neutral, PublicKeyToken=4574bb5573c51424, processorArchitecture=MSIL">
<HintPath>packages\DnsClient.1.8.0\lib\net472\DnsClient.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
@ -99,6 +97,7 @@
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<None Include="MD5Create_TemporaryKey.pfx" />
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
@ -126,14 +125,4 @@
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="packages\Fody.6.8.2\build\Fody.targets" Condition="Exists('packages\Fody.6.8.2\build\Fody.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('packages\Fody.6.8.2\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Fody.6.8.2\build\Fody.targets'))" />
<Error Condition="!Exists('packages\Costura.Fody.6.0.0\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Costura.Fody.6.0.0\build\Costura.Fody.props'))" />
<Error Condition="!Exists('packages\Costura.Fody.6.0.0\build\Costura.Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Costura.Fody.6.0.0\build\Costura.Fody.targets'))" />
</Target>
<Import Project="packages\Costura.Fody.6.0.0\build\Costura.Fody.targets" Condition="Exists('packages\Costura.Fody.6.0.0\build\Costura.Fody.targets')" />
</Project>

View File

@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35825.156 d17.13
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CheckDownload", "CheckDownload.csproj", "{F4681804-7E5A-4A02-87EF-B28E34AA443B}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MD5Create", "MD5Create.csproj", "{353C07CE-BA22-44F3-8816-5F97E2B9DDE0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -11,15 +11,15 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F4681804-7E5A-4A02-87EF-B28E34AA443B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F4681804-7E5A-4A02-87EF-B28E34AA443B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F4681804-7E5A-4A02-87EF-B28E34AA443B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F4681804-7E5A-4A02-87EF-B28E34AA443B}.Release|Any CPU.Build.0 = Release|Any CPU
{353C07CE-BA22-44F3-8816-5F97E2B9DDE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{353C07CE-BA22-44F3-8816-5F97E2B9DDE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{353C07CE-BA22-44F3-8816-5F97E2B9DDE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{353C07CE-BA22-44F3-8816-5F97E2B9DDE0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {EB680316-B04E-4697-BD44-CF7E4AE2D8BD}
SolutionGuid = {25B0F992-2814-48A9-9F14-6F5D10FF4855}
EndGlobalSection
EndGlobal

BIN
MD5Create_TemporaryKey.pfx Normal file

Binary file not shown.

View File

@ -4,7 +4,7 @@ using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CheckDownload
namespace MD5Create
{
static class Program
{
@ -16,7 +16,7 @@ namespace CheckDownload
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Update());
Application.Run(new MD5Create());
}
}
}

View File

@ -5,11 +5,11 @@ using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("CheckDownload")]
[assembly: AssemblyTitle("MD5Create")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CheckDownload")]
[assembly: AssemblyProduct("MD5Create")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@ -20,7 +20,7 @@ using System.Runtime.InteropServices;
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("f4681804-7e5a-4a02-87ef-b28e34aa443b")]
[assembly: Guid("353c07ce-ba22-44f3-8816-5f97e2b9dde0")]
// 程序集的版本信息由下列四个值组成:
//

View File

@ -8,7 +8,7 @@
// </auto-generated>
//------------------------------------------------------------------------------
namespace CheckDownload.Properties
namespace MD5Create.Properties
{
@ -44,7 +44,7 @@ namespace CheckDownload.Properties
{
if ((resourceMan == null))
{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CheckDownload.Properties.Resources", typeof(Resources).Assembly);
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MD5Create.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;

View File

@ -8,7 +8,7 @@
// </auto-generated>
//------------------------------------------------------------------------------
namespace CheckDownload.Properties
namespace MD5Create.Properties
{

View File

@ -1 +1 @@
Check files & Download files
Create md5.json

View File

@ -1,45 +0,0 @@
{
"version": "1.0.1",
"date": {
"Admin-Keyauth-test.exe": "90e4541dd0ce570d726ef5ec7e955f1e",
"CodeDependencies.iss": "3f20c3b14eefa82dd73c4ba8c8cd16a8",
"f1c7016af6c8ca5eaad5c1ad0b8ad28b.json": "f1c7016af6c8ca5eaad5c1ad0b8ad28b",
"GKeyManager_1.4.8_test_auto.exe": "7d1eb2a411ea6e7e14bd4de40274a52f",
"main.go": "b1c53d8b985e6680cbe6b579c25a827e",
"output_test.iss": "fb17a52e4b6dd12c643cd37f61497ab6",
"new": {
"md5-a.json": "1d0cd2f12943c23201610035ca2fc53f"
},
"test": {
"aspnetcore-runtime-6.0.36-win-x86.exe": "638cd17cc32c6882fe01c56eaf003a2c",
"dockerfile": "3ac7da6188e62fd43f8b8558486fe702",
"go.mod": "4a3891b4e68630f56d57e0eca125a185",
"go.sum": "37aff3bcc4eeea1c52aa867c83670d9c",
"Login_update.exe": "d4fbcab2a61960de56c7a01c5ecace85",
"Main.exe": "af5b5d9f44f74e121b0d78e21dfe8328",
"main.go": "a725751ba669944ac36ee6a3cdcffca1",
"md5.json": "58c4bf8e5a9c77ac06eea0bbd6cf25ab",
"netcorecheck.exe": "298fa2564c44e3c1fdbc84cc76d97bd9",
"ssh.tar": "5f87ff72678afc5e15cd92d4344ff901",
"vc_redist.x86.exe": "3ca2b599c42442b57aeb07229d731d71",
"Windows6.1-KB2533623-x64.msu": "0a894c59c245dad32e6e48ecbdbc8921",
"Windows6.1-KB2533623-x86.msu": "edf1d538c85f24ec0ef0991e6b27f0d7",
"windowsdesktop-runtime-6.0.36-win-x86.exe": "9c62d5c212725b00563ca6b201326b8f",
"FIle": {
"aspnetcorev2_inprocess.dll": "a9ec468ef61b60f43cf24d4b62bef9de",
"D3DCompiler_47_cor3.dll": "5ce90e0d97f339e0cdf9873d3cb7c222",
"KeyManages.pdb": "7c1cbed9f87e556630d35f3ec9ea3092",
"Login_update.exe": "dad6b14e7a0ee41514bcbee826084b87",
"Main.exe": "1566dc18caa5ff94e84b7a0b097a1256",
"PenImc_cor3.dll": "931cf859776dbac94c5ab2f69055d9c6",
"PresentationNative_cor3.dll": "690cd2dd64ceaecc84a037835682a1ab",
"vcruntime140_cor3.dll": "9248c36666a2fec5e2a8913d6edabf80",
"wpfgfx_cor3.dll": "5fbd0fb4e2a2cc6f7ed3c6369b369ef6"
},
"utils": {
"encryptData.go": "509d003b33b915372639919f2ad6de6f"
}
},
"新建文件夹": {}
}
}

View File

@ -1,9 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Aliyun.OSS.SDK" version="2.14.1" targetFramework="net472" />
<package id="Costura.Fody" version="6.0.0" targetFramework="net472" developmentDependency="true" />
<package id="DnsClient" version="1.8.0" targetFramework="net472" />
<package id="Fody" version="6.8.2" targetFramework="net472" developmentDependency="true" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net472" />
<package id="System.Buffers" version="4.5.1" targetFramework="net472" />
</packages>