添加HVM项目配置文件,更新文件清理逻辑,增强文件强制替换功能
This commit is contained in:
81
CheckDown.hvmprj
Normal file
81
CheckDown.hvmprj
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<HVMProject version ="2">
|
||||||
|
<Assemblies>
|
||||||
|
<assembly>
|
||||||
|
<path>.\bin\Debug\CheckDownload.exe</path>
|
||||||
|
</assembly>
|
||||||
|
</Assemblies>
|
||||||
|
<Output>
|
||||||
|
<path>.\bin\Debug_Protected</path>
|
||||||
|
<runtime>HVMRuntm.dll</runtime>
|
||||||
|
<runtime64>HVMRun64.dll</runtime64>
|
||||||
|
<X64Optimize>true</X64Optimize>
|
||||||
|
<LibMode>1</LibMode>
|
||||||
|
<EmbedRuntime>0</EmbedRuntime>
|
||||||
|
<shrink>true</shrink>
|
||||||
|
<SingleEXE>true</SingleEXE>
|
||||||
|
<EXE>
|
||||||
|
<Main>CheckDownload.exe</Main>
|
||||||
|
<PackType>1</PackType>
|
||||||
|
<EntryType>0</EntryType>
|
||||||
|
<LoadType>0</LoadType>
|
||||||
|
<Assemblies>
|
||||||
|
<assembly>
|
||||||
|
<path>.\bin\Debug\7z.dll</path>
|
||||||
|
<datafile>true</datafile>
|
||||||
|
</assembly>
|
||||||
|
<assembly>
|
||||||
|
<path>.\bin\Debug\CheckDownload.exe.config</path>
|
||||||
|
<datafile>true</datafile>
|
||||||
|
</assembly>
|
||||||
|
<assembly>
|
||||||
|
<path>.\bin\Debug\CheckDownload.pdb</path>
|
||||||
|
<datafile>true</datafile>
|
||||||
|
</assembly>
|
||||||
|
<SplashConfig>
|
||||||
|
<Width>600</Width>
|
||||||
|
<Height>400</Height>
|
||||||
|
<iAutoClose>0</iAutoClose>
|
||||||
|
<Image></Image>
|
||||||
|
<msg1>Loading...</msg1>
|
||||||
|
<msg2>Starting...</msg2>
|
||||||
|
<title></title>
|
||||||
|
</SplashConfig>
|
||||||
|
</Assemblies>
|
||||||
|
</EXE>
|
||||||
|
</Output>
|
||||||
|
<Settings>
|
||||||
|
<EncryptComplierGen>true</EncryptComplierGen>
|
||||||
|
<AntiDump>true</AntiDump>
|
||||||
|
<EncryptNew>true</EncryptNew>
|
||||||
|
<EncryptRes>true</EncryptRes>
|
||||||
|
<EncryptBlob>true</EncryptBlob>
|
||||||
|
<EncryptUS>true</EncryptUS>
|
||||||
|
<HVM>true</HVM>
|
||||||
|
<HVM2>true</HVM2>
|
||||||
|
<HVMSig>true</HVMSig>
|
||||||
|
<HVMEH>true</HVMEH>
|
||||||
|
<HVMStr>true</HVMStr>
|
||||||
|
<Level>4</Level>
|
||||||
|
<Level2>3</Level2>
|
||||||
|
<CompatLevel>15</CompatLevel>
|
||||||
|
<AntiTrace>true</AntiTrace>
|
||||||
|
<HVMAntiTrace>true</HVMAntiTrace>
|
||||||
|
<HVMProxyMethod>true</HVMProxyMethod>
|
||||||
|
<AnonymousProtection>2</AnonymousProtection>
|
||||||
|
<StripConstantsString>true</StripConstantsString>
|
||||||
|
</Settings>
|
||||||
|
<Obfuscation>
|
||||||
|
<AutoRename>true</AutoRename>
|
||||||
|
<ObILCode>true</ObILCode>
|
||||||
|
<ObMeta>true</ObMeta>
|
||||||
|
<RenameMode>2</RenameMode>
|
||||||
|
</Obfuscation>
|
||||||
|
<TrialSettings>
|
||||||
|
</TrialSettings>
|
||||||
|
<LicenseSettings>
|
||||||
|
<AddBiosID>true</AddBiosID>
|
||||||
|
<AddMacID>true</AddMacID>
|
||||||
|
<MasterKey></MasterKey>
|
||||||
|
</LicenseSettings>
|
||||||
|
</HVMProject>
|
495
Update.cs
495
Update.cs
@@ -252,7 +252,7 @@ namespace CheckDownload
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
InitializeTempDirectory();
|
InitializeTempDirectory();
|
||||||
CleanupNewFiles();
|
CleanupOldFiles();
|
||||||
UpdateStatus("下载在线MD5文件并读取...");
|
UpdateStatus("下载在线MD5文件并读取...");
|
||||||
UpdateCount("");
|
UpdateCount("");
|
||||||
UpdateSize("");
|
UpdateSize("");
|
||||||
@@ -366,16 +366,35 @@ namespace CheckDownload
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 清理旧的更新文件,删除所有.new后缀的临时文件
|
/// 清理旧的更新文件,删除备份文件和临时文件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void CleanupNewFiles()
|
private void CleanupOldFiles()
|
||||||
{
|
{
|
||||||
try
|
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("正在清理旧的更新文件...");
|
UpdateStatus("正在清理备份文件...");
|
||||||
|
foreach (var file in backupFiles)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 只清理超过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)
|
foreach (var file in newFiles)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -387,7 +406,6 @@ namespace CheckDownload
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -914,7 +932,7 @@ namespace CheckDownload
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 验证所有下载文件的MD5完整性并保存到目标位置,处理文件占用情况
|
/// 验证所有下载文件的MD5完整性并保存到目标位置,强制替换占用的文件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task VerifyAndSaveAllFiles()
|
private async Task VerifyAndSaveAllFiles()
|
||||||
{
|
{
|
||||||
@@ -922,11 +940,7 @@ namespace CheckDownload
|
|||||||
UpdateCount("");
|
UpdateCount("");
|
||||||
UpdateSize("");
|
UpdateSize("");
|
||||||
|
|
||||||
// 新增:在开始校验前创建临时文件备份
|
|
||||||
//await CreateTempFileBackup();
|
|
||||||
|
|
||||||
var failedFiles = new ConcurrentBag<string>();
|
var failedFiles = new ConcurrentBag<string>();
|
||||||
var filesForScripting = new ConcurrentBag<(string original, string newFile)>();
|
|
||||||
|
|
||||||
int totalFiles = _downloadedFiles.Count;
|
int totalFiles = _downloadedFiles.Count;
|
||||||
int completed = 0;
|
int completed = 0;
|
||||||
@@ -941,18 +955,17 @@ namespace CheckDownload
|
|||||||
string expectedMd5 = item.Value;
|
string expectedMd5 = item.Value;
|
||||||
string tempFilePath = Path.Combine(_tempDirectory, relativePath);
|
string tempFilePath = Path.Combine(_tempDirectory, relativePath);
|
||||||
|
|
||||||
// 添加调试信息:检查临时文件是否存在
|
// 检查临时文件是否存在
|
||||||
if (!File.Exists(tempFilePath))
|
if (!File.Exists(tempFilePath))
|
||||||
{
|
{
|
||||||
//UpdateStatus($"[调试] 临时文件在校验前消失: {relativePath}");
|
|
||||||
failedFiles.Add(relativePath);
|
failedFiles.Add(relativePath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证MD5
|
||||||
string actualMd5 = CalculateMD5FromFile(tempFilePath);
|
string actualMd5 = CalculateMD5FromFile(tempFilePath);
|
||||||
if (actualMd5 != expectedMd5.ToLower())
|
if (actualMd5 != expectedMd5.ToLower())
|
||||||
{
|
{
|
||||||
//UpdateStatus($"[调试] 文件MD5校验失败: {relativePath}");
|
|
||||||
failedFiles.Add(relativePath);
|
failedFiles.Add(relativePath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -964,23 +977,11 @@ namespace CheckDownload
|
|||||||
Directory.CreateDirectory(localDir);
|
Directory.CreateDirectory(localDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
//UpdateStatus($"[调试] 开始复制文件: {relativePath}");
|
// 使用强制替换方法
|
||||||
if (!await TryCopyFileAsync(tempFilePath, localPath)) // 改为使用复制方法
|
if (!await ForceReplaceFileAsync(tempFilePath, localPath))
|
||||||
{
|
{
|
||||||
//UpdateStatus($"[调试] 直接复制失败,创建.new文件: {relativePath}");
|
failedFiles.Add(relativePath);
|
||||||
string backupPath = localPath + ".new";
|
return;
|
||||||
File.Copy(tempFilePath, backupPath, true); // 复制而非移动
|
|
||||||
filesForScripting.Add((localPath, backupPath));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//UpdateStatus($"[调试] 文件复制成功: {relativePath}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 复制后再次检查临时文件是否还存在
|
|
||||||
if (!File.Exists(tempFilePath))
|
|
||||||
{
|
|
||||||
//UpdateStatus($"[调试] 警告:文件复制后临时文件消失: {relativePath}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -994,11 +995,6 @@ namespace CheckDownload
|
|||||||
|
|
||||||
await Task.WhenAll(tasks);
|
await Task.WhenAll(tasks);
|
||||||
|
|
||||||
if (filesForScripting.Any())
|
|
||||||
{
|
|
||||||
CreateReplaceScriptForAll(filesForScripting.ToList());
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var failed in failedFiles)
|
foreach (var failed in failedFiles)
|
||||||
{
|
{
|
||||||
_downloadedFiles.Remove(failed);
|
_downloadedFiles.Remove(failed);
|
||||||
@@ -1019,178 +1015,9 @@ namespace CheckDownload
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 尝试将文件从临时位置移动到目标位置(异步),若文件被占用则等待一秒后重试一次
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sourcePath">源文件的完整路径</param>
|
|
||||||
/// <param name="targetPath">目标文件的完整路径</param>
|
|
||||||
/// <returns>移动成功返回true,失败返回false</returns>
|
|
||||||
private async Task<bool> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 为被占用文件创建批处理脚本,在程序退出后自动完成文件替换
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="files">包含原始文件路径和新文件路径的元组列表</param>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化程序使用的临时目录,用于存储下载过程中的临时文件
|
/// 初始化程序使用的临时目录,用于存储下载过程中的临时文件
|
||||||
@@ -1864,18 +1691,64 @@ namespace CheckDownload
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 尝试将文件从源位置复制到目标位置
|
/// 强制替换文件,检查占用进程并强制杀掉
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task<bool> TryCopyFileAsync(string sourcePath, string targetPath)
|
/// <param name="sourcePath">源文件路径</param>
|
||||||
|
/// <param name="targetPath">目标文件路径</param>
|
||||||
|
/// <returns>成功返回true,失败返回false</returns>
|
||||||
|
private async Task<bool> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 尝试直接替换文件
|
||||||
|
/// </summary>
|
||||||
|
private async Task<bool> TryDirectReplaceAsync(string sourcePath, string targetPath)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 如果目标文件是.exe文件,先检查并kill掉可能正在运行的进程
|
|
||||||
if (Path.GetExtension(targetPath).Equals(".exe", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
KillProcessIfRunning(targetPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确保目标目录存在
|
// 确保目标目录存在
|
||||||
string targetDir = Path.GetDirectoryName(targetPath);
|
string targetDir = Path.GetDirectoryName(targetPath);
|
||||||
if (!Directory.Exists(targetDir))
|
if (!Directory.Exists(targetDir))
|
||||||
@@ -1883,25 +1756,199 @@ namespace CheckDownload
|
|||||||
Directory.CreateDirectory(targetDir);
|
Directory.CreateDirectory(targetDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行复制操作
|
// 直接复制覆盖
|
||||||
File.Copy(sourcePath, targetPath, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// 文件可能被占用,稍后重试
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await Task.Delay(1000);
|
|
||||||
File.Copy(sourcePath, targetPath, true);
|
File.Copy(sourcePath, targetPath, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
UpdateStatus($"文件复制失败: {Path.GetFileName(targetPath)}");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 强制杀掉占用指定文件的进程
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
{
|
||||||
|
// 忽略杀进程过程中的错误
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 强制杀掉可能使用指定DLL的进程
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
{
|
||||||
|
// 忽略错误
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 使用系统工具强制解锁并替换文件
|
||||||
|
/// </summary>
|
||||||
|
private async Task<bool> 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
|
||||||
|
{
|
||||||
|
if (File.Exists(backupPath))
|
||||||
|
{
|
||||||
|
File.Delete(backupPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 忽略备份文件删除失败
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 尝试将文件从源位置复制到目标位置(保留原方法用于兼容性)
|
||||||
|
/// </summary>
|
||||||
|
private async Task<bool> TryCopyFileAsync(string sourcePath, string targetPath)
|
||||||
|
{
|
||||||
|
return await ForceReplaceFileAsync(sourcePath, targetPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
Reference in New Issue
Block a user