refactor(VHDManager): 改进VHD挂载流程为两步操作并增加容错机制
将VHD挂载流程拆分为两步:先挂载VHD再分配盘符 增加对M盘已存在的检测和自动跳过功能 添加备用盘符分配方法以增强可靠性 删除过时的Windows 11故障排除文档
This commit is contained in:
+227
-24
@@ -516,13 +516,14 @@ namespace VHDMounter
|
||||
await UnmountDrive();
|
||||
await UnmountVHD();
|
||||
|
||||
var diskpartScript = $@"select vdisk file=""{vhdPath}""
|
||||
// 第一步:仅挂载VHD,不分配盘符
|
||||
StatusChanged?.Invoke("步骤1: 挂载VHD文件...");
|
||||
var attachScript = $@"select vdisk file=""{vhdPath}""
|
||||
attach vdisk
|
||||
assign letter=M
|
||||
exit";
|
||||
|
||||
var tempScript = Path.GetTempFileName();
|
||||
await File.WriteAllTextAsync(tempScript, diskpartScript);
|
||||
await File.WriteAllTextAsync(tempScript, attachScript);
|
||||
|
||||
var process = new Process
|
||||
{
|
||||
@@ -547,39 +548,64 @@ exit";
|
||||
if (completed == timeoutTask)
|
||||
{
|
||||
try { process.Kill(); } catch { }
|
||||
StatusChanged?.Invoke("挂载超时(超过60秒),已中止diskpart进程");
|
||||
File.Delete(tempScript);
|
||||
StatusChanged?.Invoke("VHD挂载超时(超过60秒),已中止diskpart进程");
|
||||
if (File.Exists(tempScript))
|
||||
File.Delete(tempScript);
|
||||
return false;
|
||||
}
|
||||
|
||||
string output = await process.StandardOutput.ReadToEndAsync();
|
||||
string error = await process.StandardError.ReadToEndAsync();
|
||||
string attachOutput = await process.StandardOutput.ReadToEndAsync();
|
||||
string attachError = await process.StandardError.ReadToEndAsync();
|
||||
|
||||
File.Delete(tempScript);
|
||||
if (File.Exists(tempScript))
|
||||
File.Delete(tempScript);
|
||||
|
||||
// 等待系统完成卷挂载
|
||||
await Task.Delay(2000);
|
||||
|
||||
bool mounted = Directory.Exists(TARGET_DRIVE);
|
||||
if (!mounted)
|
||||
if (process.ExitCode != 0)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(error))
|
||||
{
|
||||
StatusChanged?.Invoke($"挂载失败:{error.Trim()}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 提供有限的DiskPart输出以辅助定位问题
|
||||
var trimmedOutput = output?.Length > 400 ? output.Substring(output.Length - 400) : output;
|
||||
StatusChanged?.Invoke($"挂载失败:未检测到M盘。DiskPart输出片段:{trimmedOutput}");
|
||||
}
|
||||
StatusChanged?.Invoke($"VHD挂载失败: {attachError.Trim()}");
|
||||
Debug.WriteLine($"VHD挂载输出: {attachOutput}");
|
||||
Debug.WriteLine($"VHD挂载错误: {attachError}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return mounted;
|
||||
StatusChanged?.Invoke("VHD挂载成功,等待系统识别分区...");
|
||||
await Task.Delay(3000); // 等待系统识别分区
|
||||
|
||||
// 第二步:为第一个分区分配盘符
|
||||
StatusChanged?.Invoke("步骤2: 为第一个分区分配盘符M...");
|
||||
|
||||
// 容错检查:如果M盘已经存在,可能是系统自动分配的
|
||||
if (Directory.Exists(TARGET_DRIVE))
|
||||
{
|
||||
StatusChanged?.Invoke("检测到M盘已自动分配,跳过手动分配步骤");
|
||||
StatusChanged?.Invoke("VHD挂载和盘符分配完成");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool driveAssigned = await AssignDriveLetterToFirstPartition(vhdPath);
|
||||
|
||||
if (!driveAssigned)
|
||||
{
|
||||
StatusChanged?.Invoke("分配盘符失败,尝试备用方法...");
|
||||
// 备用方法:直接尝试分配盘符
|
||||
driveAssigned = await AssignDriveLetterDirect();
|
||||
}
|
||||
|
||||
if (driveAssigned)
|
||||
{
|
||||
StatusChanged?.Invoke("VHD挂载和盘符分配完成");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
StatusChanged?.Invoke("盘符分配失败,但VHD已挂载");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusChanged?.Invoke($"挂载失败: {ex.Message}");
|
||||
Debug.WriteLine($"MountVHD异常: {ex}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -780,6 +806,183 @@ exit";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为VHD的第一个分区分配盘符M
|
||||
/// </summary>
|
||||
private async Task<bool> AssignDriveLetterToFirstPartition(string vhdPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 容错机制:检查M盘是否已经存在
|
||||
if (Directory.Exists(TARGET_DRIVE))
|
||||
{
|
||||
StatusChanged?.Invoke("检测到M盘已存在,跳过盘符分配");
|
||||
Debug.WriteLine("M盘已存在,无需重复分配");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 使用diskpart列出VHD的分区并为第一个分区分配盘符
|
||||
var listScript = $@"select vdisk file=""{vhdPath}""
|
||||
list partition
|
||||
select partition 1
|
||||
assign letter=M
|
||||
exit";
|
||||
|
||||
var tempScript = Path.GetTempFileName();
|
||||
await File.WriteAllTextAsync(tempScript, listScript);
|
||||
|
||||
var process = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "diskpart",
|
||||
Arguments = $"/s \"{tempScript}\"",
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true
|
||||
}
|
||||
};
|
||||
|
||||
process.Start();
|
||||
|
||||
var waitTask = process.WaitForExitAsync();
|
||||
var timeoutTask = Task.Delay(30000);
|
||||
var completed = await Task.WhenAny(waitTask, timeoutTask);
|
||||
|
||||
if (completed == timeoutTask)
|
||||
{
|
||||
try { process.Kill(); } catch { }
|
||||
StatusChanged?.Invoke("分配盘符超时");
|
||||
if (File.Exists(tempScript))
|
||||
File.Delete(tempScript);
|
||||
return false;
|
||||
}
|
||||
|
||||
string output = await process.StandardOutput.ReadToEndAsync();
|
||||
string error = await process.StandardError.ReadToEndAsync();
|
||||
|
||||
if (File.Exists(tempScript))
|
||||
File.Delete(tempScript);
|
||||
|
||||
Debug.WriteLine($"分区盘符分配输出: {output}");
|
||||
if (!string.IsNullOrWhiteSpace(error))
|
||||
{
|
||||
Debug.WriteLine($"分区盘符分配错误: {error}");
|
||||
}
|
||||
|
||||
// 等待盘符分配完成
|
||||
await Task.Delay(2000);
|
||||
|
||||
bool driveExists = Directory.Exists(TARGET_DRIVE);
|
||||
if (driveExists)
|
||||
{
|
||||
StatusChanged?.Invoke("盘符M分配成功");
|
||||
}
|
||||
else
|
||||
{
|
||||
StatusChanged?.Invoke("盘符M分配失败,M盘不存在");
|
||||
}
|
||||
|
||||
return driveExists;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusChanged?.Invoke($"分配盘符异常: {ex.Message}");
|
||||
Debug.WriteLine($"AssignDriveLetterToFirstPartition异常: {ex}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 备用方法:直接尝试分配盘符(适用于单分区VHD)
|
||||
/// </summary>
|
||||
private async Task<bool> AssignDriveLetterDirect()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 容错机制:检查M盘是否已经存在
|
||||
if (Directory.Exists(TARGET_DRIVE))
|
||||
{
|
||||
StatusChanged?.Invoke("备用方法检测到M盘已存在,跳过盘符分配");
|
||||
Debug.WriteLine("备用方法:M盘已存在,无需重复分配");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 尝试为最后挂载的磁盘分配盘符
|
||||
var assignScript = @"list disk
|
||||
select disk 1
|
||||
list partition
|
||||
select partition 1
|
||||
assign letter=M
|
||||
exit";
|
||||
|
||||
var tempScript = Path.GetTempFileName();
|
||||
await File.WriteAllTextAsync(tempScript, assignScript);
|
||||
|
||||
var process = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "diskpart",
|
||||
Arguments = $"/s \"{tempScript}\"",
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true
|
||||
}
|
||||
};
|
||||
|
||||
process.Start();
|
||||
|
||||
var waitTask = process.WaitForExitAsync();
|
||||
var timeoutTask = Task.Delay(30000);
|
||||
var completed = await Task.WhenAny(waitTask, timeoutTask);
|
||||
|
||||
if (completed == timeoutTask)
|
||||
{
|
||||
try { process.Kill(); } catch { }
|
||||
StatusChanged?.Invoke("备用盘符分配超时");
|
||||
if (File.Exists(tempScript))
|
||||
File.Delete(tempScript);
|
||||
return false;
|
||||
}
|
||||
|
||||
string output = await process.StandardOutput.ReadToEndAsync();
|
||||
string error = await process.StandardError.ReadToEndAsync();
|
||||
|
||||
if (File.Exists(tempScript))
|
||||
File.Delete(tempScript);
|
||||
|
||||
Debug.WriteLine($"备用盘符分配输出: {output}");
|
||||
if (!string.IsNullOrWhiteSpace(error))
|
||||
{
|
||||
Debug.WriteLine($"备用盘符分配错误: {error}");
|
||||
}
|
||||
|
||||
// 等待盘符分配完成
|
||||
await Task.Delay(2000);
|
||||
|
||||
bool driveExists = Directory.Exists(TARGET_DRIVE);
|
||||
if (driveExists)
|
||||
{
|
||||
StatusChanged?.Invoke("备用方法盘符M分配成功");
|
||||
}
|
||||
else
|
||||
{
|
||||
StatusChanged?.Invoke("备用方法盘符M分配也失败");
|
||||
}
|
||||
|
||||
return driveExists;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusChanged?.Invoke($"备用盘符分配异常: {ex.Message}");
|
||||
Debug.WriteLine($"AssignDriveLetterDirect异常: {ex}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> UnmountVHD()
|
||||
{
|
||||
StatusChanged?.Invoke("正在解除VHD挂载...");
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
# Windows 11 VHD替换故障排除指南
|
||||
|
||||
## 问题描述
|
||||
在Windows 11系统上,VHD文件替换可能会失败,这通常是由于Win11增强的安全策略和文件保护机制导致的。
|
||||
|
||||
## 常见错误原因
|
||||
|
||||
### 1. 权限问题
|
||||
- **症状**: 提示"权限不足,无法替换文件"
|
||||
- **原因**: Win11的UAC(用户账户控制)更加严格
|
||||
- **解决方案**:
|
||||
- 右键点击程序,选择"以管理员身份运行"
|
||||
- 或者在程序属性中设置"始终以管理员身份运行"
|
||||
|
||||
### 2. 文件属性问题
|
||||
- **症状**: 文件无法删除或替换
|
||||
- **原因**: Win11可能自动设置VHD文件为只读、隐藏或系统文件
|
||||
- **解决方案**: 程序现在会自动检测并移除这些属性
|
||||
|
||||
### 3. 文件被占用
|
||||
- **症状**: 提示"文件被占用,无法删除"
|
||||
- **原因**: VHD文件可能被Windows资源管理器或其他程序占用
|
||||
- **解决方案**:
|
||||
- 关闭所有文件资源管理器窗口
|
||||
- 确保没有其他程序正在访问VHD文件
|
||||
- 重启计算机后再尝试
|
||||
|
||||
### 4. Windows Defender实时保护
|
||||
- **症状**: 文件操作被阻止或延迟
|
||||
- **原因**: Windows Defender可能将VHD文件操作视为可疑行为
|
||||
- **解决方案**:
|
||||
- 临时禁用实时保护
|
||||
- 或将VHD文件夹添加到排除列表
|
||||
|
||||
## 程序改进功能
|
||||
|
||||
### 新增的Win11兼容性检查
|
||||
程序现在包含以下改进:
|
||||
|
||||
1. **自动检测Win11**: 程序会自动识别是否运行在Windows 11上
|
||||
2. **权限检查**: 检查程序是否以管理员身份运行
|
||||
3. **文件属性修复**: 自动移除只读、隐藏、系统等属性
|
||||
4. **文件占用检测**: 检查文件是否被其他程序占用
|
||||
5. **详细错误报告**: 提供更详细的错误信息和诊断数据
|
||||
|
||||
### 诊断信息
|
||||
程序会在状态栏显示以下信息:
|
||||
- 操作系统版本
|
||||
- 管理员权限状态
|
||||
- 文件属性详情
|
||||
- 具体的错误原因
|
||||
|
||||
## 手动排除步骤
|
||||
|
||||
### 步骤1: 检查权限
|
||||
1. 右键点击VHDMounter.exe
|
||||
2. 选择"属性" → "兼容性"
|
||||
3. 勾选"以管理员身份运行此程序"
|
||||
4. 点击"确定"
|
||||
|
||||
### 步骤2: 检查文件属性
|
||||
1. 右键点击VHD文件
|
||||
2. 选择"属性"
|
||||
3. 取消勾选"只读"
|
||||
4. 在"高级"中取消勾选"隐藏"和"系统"
|
||||
|
||||
### 步骤3: 检查Windows Defender
|
||||
1. 打开Windows安全中心
|
||||
2. 转到"病毒和威胁防护"
|
||||
3. 在"病毒和威胁防护设置"中点击"管理设置"
|
||||
4. 临时关闭"实时保护"或添加排除项
|
||||
|
||||
### 步骤4: 检查磁盘空间
|
||||
确保目标磁盘有足够的空间存储VHD文件
|
||||
|
||||
### 步骤5: 重启服务
|
||||
如果问题持续,尝试重启以下服务:
|
||||
- Windows资源管理器 (explorer.exe)
|
||||
- 虚拟磁盘服务 (vds)
|
||||
|
||||
## 联系支持
|
||||
如果以上步骤都无法解决问题,请提供以下信息:
|
||||
1. 程序显示的具体错误消息
|
||||
2. Windows 11版本号
|
||||
3. VHD文件大小和位置
|
||||
4. 是否以管理员身份运行程序
|
||||
|
||||
## 预防措施
|
||||
1. 始终以管理员身份运行程序
|
||||
2. 定期检查VHD文件的属性设置
|
||||
3. 保持Windows 11系统更新
|
||||
4. 避免在系统保护的文件夹中存储VHD文件
|
||||
Reference in New Issue
Block a user