feat: 添加文件占用处理逻辑以支持文件更新
当文件被占用时,尝试解锁文件并保存。如果无法解锁,则使用备用文件名保存,并创建批处理文件在程序退出后替换文件。此功能提高了文件更新的可靠性。
This commit is contained in:
parent
598ceaf233
commit
88468aa3a5
165
Form1.cs
165
Form1.cs
@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@ -9,6 +10,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
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()
|
private void VerifyAndSaveAllFiles()
|
||||||
{
|
{
|
||||||
UpdateStatus("正在校验文件...");
|
UpdateStatus("正在校验文件...");
|
||||||
@ -304,7 +310,18 @@ namespace CheckDownload
|
|||||||
|
|
||||||
string localPath = Path.Combine(Application.StartupPath, relativePath);
|
string localPath = Path.Combine(Application.StartupPath, relativePath);
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(localPath));
|
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);
|
int percentage = 95 + (int)(processedCount * 5.0 / totalFiles);
|
||||||
UpdateProgressValue(Math.Min(100, percentage));
|
UpdateProgressValue(Math.Min(100, percentage));
|
||||||
@ -332,6 +349,152 @@ namespace CheckDownload
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
private string CalculateMD5(string input)
|
||||||
{
|
{
|
||||||
using (var md5 = MD5.Create())
|
using (var md5 = MD5.Create())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user