From 88468aa3a5e703dccfa880bb199f2517b8f7931c Mon Sep 17 00:00:00 2001 From: Dong <1278815766@qq.com> Date: Tue, 13 May 2025 19:15:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=8D=A0=E7=94=A8=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=E4=BB=A5?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=96=87=E4=BB=B6=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当文件被占用时,尝试解锁文件并保存。如果无法解锁,则使用备用文件名保存,并创建批处理文件在程序退出后替换文件。此功能提高了文件更新的可靠性。 --- Form1.cs | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 164 insertions(+), 1 deletion(-) diff --git a/Form1.cs b/Form1.cs index b23e652..92700d0 100644 --- a/Form1.cs +++ b/Form1.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Data; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Globalization; @@ -9,6 +10,7 @@ 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; @@ -278,6 +280,10 @@ namespace CheckDownload } } + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool GetProcessHandleCount(IntPtr hProcess, out uint pdwHandleCount); + private void VerifyAndSaveAllFiles() { UpdateStatus("正在校验文件..."); @@ -304,7 +310,18 @@ namespace CheckDownload string localPath = Path.Combine(Application.StartupPath, relativePath); Directory.CreateDirectory(Path.GetDirectoryName(localPath)); - File.WriteAllBytes(localPath, data); + + // 尝试保存文件,如果文件被占用则尝试解锁 + 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)); @@ -331,6 +348,152 @@ namespace CheckDownload 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) + 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; + } + + // 尝试获取进程句柄数量,如果句柄数量较多,可能正在使用文件 + uint handleCount = 0; + if (GetProcessHandleCount(process.Handle, out handleCount) && handleCount > 0) + { + // 尝试关闭进程 + UpdateStatus($"尝试关闭可能占用文件的进程: {process.ProcessName}"); + process.CloseMainWindow(); + + // 等待进程关闭 + if (!process.WaitForExit(1000)) + { + // 如果进程没有响应,尝试强制结束 + process.Kill(); + } + } + } + catch + { + // 忽略处理单个进程时的错误 + } + finally + { + process.Dispose(); + } + } + + // 再次尝试写入文件 + Thread.Sleep(500); // 等待一段时间确保文件已释放 + File.WriteAllBytes(filePath, data); + UpdateStatus($"文件 {Path.GetFileName(filePath)} 解锁成功并已更新"); + return true; + } + catch + { + // 如果仍然无法写入,返回失败 + return false; + } + } + catch (Exception ex) + { + UpdateStatus($"保存文件时发生错误: {ex.Message}"); + return false; + } + } + + private void CreateReplaceScript(string originalFile, string newFile) + { + try + { + string batchFilePath = Path.Combine(Application.StartupPath, "update_files.bat"); + string processId = Process.GetCurrentProcess().Id.ToString(); + string escapedOriginalFile = originalFile.Replace("\\", "\\\\"); + string escapedNewFile = newFile.Replace("\\", "\\\\"); + + // 创建批处理文件内容 + 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"); + + // 写入批处理文件 + bool fileExists = File.Exists(batchFilePath); + using (StreamWriter writer = new StreamWriter(batchFilePath, fileExists)) + { + if (!fileExists) + { + writer.Write(batchContent.ToString()); + } + else + { + // 如果文件已存在,只添加移动文件的命令 + writer.WriteLine($"del \"{escapedOriginalFile}\" /f /q"); + writer.WriteLine($"move \"{escapedNewFile}\" \"{escapedOriginalFile}\""); + } + } + + // 如果是第一次创建批处理文件,启动它 + if (!fileExists) + { + ProcessStartInfo startInfo = new ProcessStartInfo + { + FileName = "cmd.exe", + Arguments = $"/c start \"\" /min \"{batchFilePath}\"", + CreateNoWindow = true, + UseShellExecute = true, + WindowStyle = ProcessWindowStyle.Hidden + }; + Process.Start(startInfo); + } + } + catch (Exception ex) + { + UpdateStatus($"创建替换脚本时出错: {ex.Message}"); + } + } private string CalculateMD5(string input) {