diff --git a/CheckDown.hvmprj b/CheckDown.hvmprj
new file mode 100644
index 0000000..a7f9602
--- /dev/null
+++ b/CheckDown.hvmprj
@@ -0,0 +1,81 @@
+
+
+
+
+ .\bin\Debug\CheckDownload.exe
+
+
+
+
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ 4
+ 3
+ 15
+ true
+ true
+ true
+ 2
+ true
+
+
+ true
+ true
+ true
+ 2
+
+
+
+
+ true
+ true
+
+
+
diff --git a/Update.cs b/Update.cs
index bd92ebe..a2fcc83 100644
--- a/Update.cs
+++ b/Update.cs
@@ -252,7 +252,7 @@ namespace CheckDownload
try
{
InitializeTempDirectory();
- CleanupNewFiles();
+ CleanupOldFiles();
UpdateStatus("下载在线MD5文件并读取...");
UpdateCount("");
UpdateSize("");
@@ -366,27 +366,45 @@ namespace CheckDownload
}
///
- /// 清理旧的更新文件,删除所有.new后缀的临时文件
+ /// 清理旧的更新文件,删除备份文件和临时文件
///
- private void CleanupNewFiles()
+ private void CleanupOldFiles()
{
try
{
- var newFiles = Directory.GetFiles(_baseDirectory, "*.new", SearchOption.AllDirectories);
- if (newFiles.Length > 0)
+ // 清理备份文件
+ var backupFiles = Directory.GetFiles(_baseDirectory, "*.bak_*", SearchOption.AllDirectories);
+ if (backupFiles.Length > 0)
{
- UpdateStatus("正在清理旧的更新文件...");
- foreach (var file in newFiles)
+ UpdateStatus("正在清理备份文件...");
+ foreach (var file in backupFiles)
{
try
{
- File.Delete(file);
+ // 只清理超过1小时的备份文件
+ if (File.GetCreationTime(file) < DateTime.Now.AddHours(-1))
+ {
+ File.Delete(file);
+ }
}
catch
{
}
}
}
+
+ // 清理旧的 .new 文件(如果还有遗留的)
+ var newFiles = Directory.GetFiles(_baseDirectory, "*.new", SearchOption.AllDirectories);
+ foreach (var file in newFiles)
+ {
+ try
+ {
+ File.Delete(file);
+ }
+ catch
+ {
+ }
+ }
}
catch
{
@@ -914,7 +932,7 @@ namespace CheckDownload
}
///
- /// 验证所有下载文件的MD5完整性并保存到目标位置,处理文件占用情况
+ /// 验证所有下载文件的MD5完整性并保存到目标位置,强制替换占用的文件
///
private async Task VerifyAndSaveAllFiles()
{
@@ -922,11 +940,7 @@ namespace CheckDownload
UpdateCount("");
UpdateSize("");
- // 新增:在开始校验前创建临时文件备份
- //await CreateTempFileBackup();
-
var failedFiles = new ConcurrentBag();
- var filesForScripting = new ConcurrentBag<(string original, string newFile)>();
int totalFiles = _downloadedFiles.Count;
int completed = 0;
@@ -941,18 +955,17 @@ namespace CheckDownload
string expectedMd5 = item.Value;
string tempFilePath = Path.Combine(_tempDirectory, relativePath);
- // 添加调试信息:检查临时文件是否存在
+ // 检查临时文件是否存在
if (!File.Exists(tempFilePath))
{
- //UpdateStatus($"[调试] 临时文件在校验前消失: {relativePath}");
failedFiles.Add(relativePath);
return;
}
+ // 验证MD5
string actualMd5 = CalculateMD5FromFile(tempFilePath);
if (actualMd5 != expectedMd5.ToLower())
{
- //UpdateStatus($"[调试] 文件MD5校验失败: {relativePath}");
failedFiles.Add(relativePath);
return;
}
@@ -964,23 +977,11 @@ namespace CheckDownload
Directory.CreateDirectory(localDir);
}
- //UpdateStatus($"[调试] 开始复制文件: {relativePath}");
- if (!await TryCopyFileAsync(tempFilePath, localPath)) // 改为使用复制方法
+ // 使用强制替换方法
+ if (!await ForceReplaceFileAsync(tempFilePath, localPath))
{
- //UpdateStatus($"[调试] 直接复制失败,创建.new文件: {relativePath}");
- string backupPath = localPath + ".new";
- File.Copy(tempFilePath, backupPath, true); // 复制而非移动
- filesForScripting.Add((localPath, backupPath));
- }
- else
- {
- //UpdateStatus($"[调试] 文件复制成功: {relativePath}");
- }
-
- // 复制后再次检查临时文件是否还存在
- if (!File.Exists(tempFilePath))
- {
- //UpdateStatus($"[调试] 警告:文件复制后临时文件消失: {relativePath}");
+ failedFiles.Add(relativePath);
+ return;
}
}
finally
@@ -994,11 +995,6 @@ namespace CheckDownload
await Task.WhenAll(tasks);
- if (filesForScripting.Any())
- {
- CreateReplaceScriptForAll(filesForScripting.ToList());
- }
-
foreach (var failed in failedFiles)
{
_downloadedFiles.Remove(failed);
@@ -1019,178 +1015,9 @@ namespace CheckDownload
}
}
- ///
- /// 尝试将文件从临时位置移动到目标位置(异步),若文件被占用则等待一秒后重试一次
- ///
- /// 源文件的完整路径
- /// 目标文件的完整路径
- /// 移动成功返回true,失败返回false
- private async Task TryMoveFileAsync(string sourcePath, string targetPath)
- {
- try
- {
- File.Move(sourcePath, targetPath);
- return true;
- }
- catch (IOException)
- {
- UpdateStatus($"文件被占用,尝试解锁...");
- try
- {
- await Task.Delay(1000);
- File.Move(sourcePath, targetPath);
- UpdateStatus($"文件解锁成功并已更新");
- return true;
- }
- catch
- {
- UpdateStatus($"文件仍被占用,无法移动");
- return false;
- }
- }
- catch (Exception ex)
- {
- UpdateStatus($"移动文件时发生错误: {ex.Message}");
- return false;
- }
- }
- ///
- /// 为被占用文件创建批处理脚本,在程序退出后自动完成文件替换
- ///
- /// 包含原始文件路径和新文件路径的元组列表
- private void CreateReplaceScriptForAll(List<(string original, string newFile)> files)
- {
- if (!files.Any()) return;
-
- try
- {
- string batchFilePath = Path.Combine(_baseDirectory, "update_files.bat");
- string processId = Process.GetCurrentProcess().Id.ToString();
- string processName = Process.GetCurrentProcess().ProcessName;
-
- // 创建锁文件,程序退出前会删除它
- File.WriteAllText(_updateLockFilePath, processId);
-
- var batchContent = new StringBuilder();
- batchContent.AppendLine("@echo off");
- batchContent.AppendLine("title Update Process");
-
- // 简化的等待逻辑,避免复杂的计数器
- batchContent.AppendLine("echo 等待程序退出...");
- batchContent.AppendLine(":wait_loop");
-
- // 优先检查锁文件
- batchContent.AppendLine($"if not exist \"{_updateLockFilePath}\" goto process_check");
-
- // 等待0.5秒
- batchContent.AppendLine("ping 127.0.0.1 -n 1 -w 500 >nul");
- batchContent.AppendLine("goto wait_loop");
-
- batchContent.AppendLine(":process_check");
- // 再次确认进程已退出
- batchContent.AppendLine($"tasklist /FI \"PID eq {processId}\" 2>nul | find /I \"{processId}\" >nul");
- batchContent.AppendLine("if %ERRORLEVEL% equ 0 (");
- batchContent.AppendLine(" ping 127.0.0.1 -n 1 -w 500 >nul");
- batchContent.AppendLine(" goto process_check");
- batchContent.AppendLine(")");
-
- // 额外等待确保完全退出
- batchContent.AppendLine("ping 127.0.0.1 -n 1 -w 1000 >nul");
- batchContent.AppendLine("echo 开始更新文件...");
-
- // 简化的文件处理逻辑,避免复杂的标签
- int fileIndex = 0;
- foreach (var file in files)
- {
- string fileName = Path.GetFileName(file.original);
- batchContent.AppendLine($"echo 处理文件: {fileName}");
-
- // 检查新文件是否存在
- batchContent.AppendLine($"if not exist \"{file.newFile}\" (");
- batchContent.AppendLine($" echo 错误: 新文件不存在 - {fileName}");
- batchContent.AppendLine($" goto file_{fileIndex}_end");
- batchContent.AppendLine($")");
-
- // 删除原文件(如果存在)
- batchContent.AppendLine($"if exist \"{file.original}\" (");
- batchContent.AppendLine($" echo 删除原文件: {fileName}");
- batchContent.AppendLine($" del \"{file.original}\" /f /q");
- batchContent.AppendLine($")");
-
- // 复制新文件
- batchContent.AppendLine($"echo 复制新文件: {fileName}");
- batchContent.AppendLine($"copy /Y \"{file.newFile}\" \"{file.original}\"");
-
- // 验证复制是否成功
- batchContent.AppendLine($"if exist \"{file.original}\" (");
- batchContent.AppendLine($" echo 成功更新: {fileName}");
- batchContent.AppendLine($" del \"{file.newFile}\" /f /q");
- batchContent.AppendLine($") else (");
- batchContent.AppendLine($" echo 失败: 无法创建 {fileName}");
- batchContent.AppendLine($")");
-
- batchContent.AppendLine($":file_{fileIndex}_end");
- fileIndex++;
- }
-
- // 清理工作
- batchContent.AppendLine("echo 清理临时文件...");
-
- // 删除锁文件
- batchContent.AppendLine($"if exist \"{_updateLockFilePath}\" del \"{_updateLockFilePath}\" /f /q");
-
- // 简化的清理逻辑
- batchContent.AppendLine($"cd /d \"{_baseDirectory}\"");
- batchContent.AppendLine("for %%f in (*.new) do del \"%%f\" /f /q");
- batchContent.AppendLine("for %%f in (*.bak) do del \"%%f\" /f /q");
-
- batchContent.AppendLine("echo 更新完成");
- batchContent.AppendLine("ping 127.0.0.1 -n 1 -w 2000 >nul");
-
- // 删除批处理脚本自身
- batchContent.AppendLine("del \"%~f0\" /f /q");
-
- File.WriteAllText(batchFilePath, batchContent.ToString(), new UTF8Encoding(false));
-
- // 使用Win7兼容的启动方式
- var startInfo = new ProcessStartInfo
- {
- FileName = batchFilePath,
- CreateNoWindow = false, // Win7下设为false更稳定
- UseShellExecute = true, // Win7下必须使用Shell执行
- WindowStyle = ProcessWindowStyle.Minimized,
- WorkingDirectory = _baseDirectory
- };
-
- try
- {
- Process.Start(startInfo);
- }
- catch (Exception)
- {
- // 如果上面的方式失败,尝试直接用cmd执行
- var fallbackStartInfo = new ProcessStartInfo
- {
- FileName = "cmd.exe",
- Arguments = $"/c \"{batchFilePath}\"",
- CreateNoWindow = true,
- UseShellExecute = true,
- WindowStyle = ProcessWindowStyle.Hidden,
- WorkingDirectory = _baseDirectory
- };
- Process.Start(fallbackStartInfo);
- }
-
- UpdateStatus("已创建更新脚本,程序退出后将自动完成更新");
- }
- catch (Exception ex)
- {
- HandleError("创建文件替换脚本 (CreateReplaceScriptForAll)", ex);
- }
- }
///
/// 初始化程序使用的临时目录,用于存储下载过程中的临时文件
@@ -1864,18 +1691,64 @@ namespace CheckDownload
}
///
- /// 尝试将文件从源位置复制到目标位置
+ /// 强制替换文件,检查占用进程并强制杀掉
///
- private async Task TryCopyFileAsync(string sourcePath, string targetPath)
+ /// 源文件路径
+ /// 目标文件路径
+ /// 成功返回true,失败返回false
+ private async Task ForceReplaceFileAsync(string sourcePath, string targetPath)
+ {
+ string fileName = Path.GetFileName(targetPath);
+
+ try
+ {
+ // 第一次尝试直接替换
+ if (await TryDirectReplaceAsync(sourcePath, targetPath))
+ {
+ return true;
+ }
+
+ // 如果失败,检查并强制杀掉占用的进程
+ UpdateStatus($"文件被占用,正在强制终止相关进程: {fileName}");
+
+ // 强制杀掉可能占用文件的进程
+ await ForceKillProcessesUsingFileAsync(targetPath);
+
+ // 等待进程完全退出
+ await Task.Delay(2000);
+
+ // 再次尝试替换
+ if (await TryDirectReplaceAsync(sourcePath, targetPath))
+ {
+ UpdateStatus($"强制替换成功: {fileName}");
+ return true;
+ }
+
+ // 如果还是失败,尝试使用系统工具强制解锁
+ UpdateStatus($"尝试解锁文件: {fileName}");
+ if (await ForceUnlockAndReplaceAsync(sourcePath, targetPath))
+ {
+ UpdateStatus($"解锁并替换成功: {fileName}");
+ return true;
+ }
+
+ UpdateStatus($"强制替换失败: {fileName}");
+ return false;
+ }
+ catch (Exception ex)
+ {
+ UpdateStatus($"强制替换异常: {fileName} - {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 尝试直接替换文件
+ ///
+ private async Task TryDirectReplaceAsync(string sourcePath, string targetPath)
{
try
{
- // 如果目标文件是.exe文件,先检查并kill掉可能正在运行的进程
- if (Path.GetExtension(targetPath).Equals(".exe", StringComparison.OrdinalIgnoreCase))
- {
- KillProcessIfRunning(targetPath);
- }
-
// 确保目标目录存在
string targetDir = Path.GetDirectoryName(targetPath);
if (!Directory.Exists(targetDir))
@@ -1883,25 +1756,199 @@ namespace CheckDownload
Directory.CreateDirectory(targetDir);
}
- // 执行复制操作
+ // 直接复制覆盖
File.Copy(sourcePath, targetPath, true);
return true;
}
- catch (Exception ex)
+ catch
{
- // 文件可能被占用,稍后重试
+ return false;
+ }
+ }
+
+ ///
+ /// 强制杀掉占用指定文件的进程
+ ///
+ private async Task ForceKillProcessesUsingFileAsync(string filePath)
+ {
+ try
+ {
+ string fileName = Path.GetFileNameWithoutExtension(filePath);
+ string fileDirectory = Path.GetDirectoryName(filePath);
+
+ // 1. 杀掉同名的进程
+ await Task.Run(() =>
+ {
+ var processes = Process.GetProcessesByName(fileName);
+ foreach (var proc in processes)
+ {
+ if (proc.Id == _currentProcessId) continue;
+
+ try
+ {
+ // 检查进程是否在同一目录下
+ string procPath = proc.MainModule?.FileName;
+ if (!string.IsNullOrEmpty(procPath))
+ {
+ string procDir = Path.GetDirectoryName(procPath);
+ if (string.Equals(procDir, fileDirectory, StringComparison.OrdinalIgnoreCase))
+ {
+ UpdateStatus($"强制终止进程: {proc.ProcessName} (PID: {proc.Id})");
+ proc.Kill();
+ proc.WaitForExit(3000);
+ }
+ }
+ }
+ catch
+ {
+ // 忽略无法访问的进程
+ }
+ }
+ });
+
+ // 2. 使用 taskkill 强制终止
+ await Task.Run(() =>
+ {
+ try
+ {
+ var startInfo = new ProcessStartInfo
+ {
+ FileName = "taskkill",
+ Arguments = $"/F /IM {fileName}.exe",
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true
+ };
+
+ using (var process = Process.Start(startInfo))
+ {
+ process?.WaitForExit(5000);
+ }
+ }
+ catch
+ {
+ // 忽略 taskkill 失败
+ }
+ });
+
+ // 3. 如果是 DLL 文件,尝试卸载相关进程
+ if (Path.GetExtension(filePath).Equals(".dll", StringComparison.OrdinalIgnoreCase))
+ {
+ await ForceKillProcessesUsingDllAsync(filePath);
+ }
+ }
+ catch
+ {
+ // 忽略杀进程过程中的错误
+ }
+ }
+
+ ///
+ /// 强制杀掉可能使用指定DLL的进程
+ ///
+ private async Task ForceKillProcessesUsingDllAsync(string dllPath)
+ {
+ try
+ {
+ string dllName = Path.GetFileName(dllPath);
+ string baseDir = _baseDirectory.ToLowerInvariant();
+
+ await Task.Run(() =>
+ {
+ var allProcesses = Process.GetProcesses();
+ foreach (var proc in allProcesses)
+ {
+ if (proc.Id == _currentProcessId) continue;
+
+ try
+ {
+ string procPath = proc.MainModule?.FileName;
+ if (!string.IsNullOrEmpty(procPath) &&
+ procPath.ToLowerInvariant().StartsWith(baseDir))
+ {
+ UpdateStatus($"终止可能使用DLL的进程: {proc.ProcessName} (PID: {proc.Id})");
+ proc.Kill();
+ proc.WaitForExit(3000);
+ }
+ }
+ catch
+ {
+ // 忽略无法访问的进程
+ }
+ }
+ });
+ }
+ catch
+ {
+ // 忽略错误
+ }
+ }
+
+ ///
+ /// 使用系统工具强制解锁并替换文件
+ ///
+ private async Task ForceUnlockAndReplaceAsync(string sourcePath, string targetPath)
+ {
+ try
+ {
+ // 尝试使用 handle.exe 或其他系统工具来解锁文件
+ // 这里先尝试简单的重命名删除方法
+ string backupPath = targetPath + ".bak_" + DateTime.Now.Ticks;
+
+ // 尝试重命名原文件
+ if (File.Exists(targetPath))
+ {
+ try
+ {
+ File.Move(targetPath, backupPath);
+ }
+ catch
+ {
+ // 如果重命名失败,尝试设置文件属性
+ try
+ {
+ File.SetAttributes(targetPath, FileAttributes.Normal);
+ await Task.Delay(500);
+ File.Move(targetPath, backupPath);
+ }
+ catch
+ {
+ return false;
+ }
+ }
+ }
+
+ // 复制新文件
+ File.Copy(sourcePath, targetPath, true);
+
+ // 删除备份文件
try
{
- await Task.Delay(1000);
- File.Copy(sourcePath, targetPath, true);
- return true;
+ if (File.Exists(backupPath))
+ {
+ File.Delete(backupPath);
+ }
}
catch
{
- UpdateStatus($"文件复制失败: {Path.GetFileName(targetPath)}");
- return false;
+ // 忽略备份文件删除失败
}
+
+ return true;
}
+ catch
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// 尝试将文件从源位置复制到目标位置(保留原方法用于兼容性)
+ ///
+ private async Task TryCopyFileAsync(string sourcePath, string targetPath)
+ {
+ return await ForceReplaceFileAsync(sourcePath, targetPath);
}
///