【win备份还原工具开发日记】08:VM 引导修复、孤儿快照回收与智能分流键盘自动化
开发残留清理与测试盘重建(0021)
-
删设备必须先停 VM:
--device-del在运行态报 "cannot be added or removed while the virtual machine is running";实际设备名就是 boot order 里的hdd1/hdd2,之前报 "device does not exist" 是 VM 没停导致的误导性报错。 -
快照引用会锁住删除:
--destroy-image报 "This disk is in use by the following snapshots" → 必须--destroy-image-force(顺带清快照对镜像的引用)。 -
nul保留设备名文件:Remove-Item -Recurse -Force和rd /s /q都因目录里有名为nul的 0 字节文件而失败(报"目录不是空的")→ 用\\?\前缀走[System.IO.Directory]::Delete才删得掉;来源是历史脚本copy ... nul忘了加>。 -
prlctl exec是管理员以外上下文:vssadmin/DISM 在线命令在 exec 下无权限(输出版权信息即未执行)→ 必须schtasks /create /ru SYSTEM /rl HIGHEST提权跑,用完删任务。
孤儿快照层 .hds 回收:.pvm 208G→62G(0021)
-
现象:快照树已空(GUI 和
prlctl snapshot-list都空),但harddisk.hdd/下仍残留 12 个 .hds 层 ≈206GB;原因是历史 11 个快照在 VM 运行态删除,只删了树节点、差异层没合并回收。 -
解法(官方工具,别手删文件):先
prl_disk_tool merge --hdd <路径>合并 12 层→单层,再compact --hdd回收空洞;必须先 merge 再 compact,直接 compact 会因"无空洞"无效(merge 后才标出可回收 83GB)。 -
语法坑:
merge用--hdd <路径>,不带-i(带 -i 报 "The 'i' option cannot be used for the 'merge' operation")。
VM 引导修复:新建 VM 挂旧盘(0158)
-
现象:VM 挂起恢复后 Boot Manager 只剩「UEFI Shell」,Parallels 弹「没有安装操作系统」;
prl_disk_tool check100% 通过,磁盘数据完好。 -
根因:坏的是 VM NVRAM 里的 EFI 引导变量(Boot0000/BootOrder),挂起/恢复过程中丢失;重置 NVRAM、关 Secure Boot 都无效。
-
死路:固件菜单要键盘导航,但 cu 按键传不进 Parallels VM 窗口;
prlctl send-key-event --scancode在「无操作系统」模态弹窗出现期间被 Parallels 拦截(弹窗存在时键完全无效)。 -
解法(已成功):
prlctl create "Win11-repair" --no-hdd新建 VM 拿干净 NVRAM →--device-add hdd --image <旧盘>引用旧盘(不复制、不占空间)→ 干净固件直接扫到 ESP 引导文件就进 Windows,无需 PE 修复。 -
遗留:新旧 VM UUID 不同,Windows 激活可能失效;旧 VM 已停(磁盘被新 VM 引用不能同时启动)。
WinRE 自动进入踩坑(0505)
-
WinRE 状态 Disabled 不等于镜像丢失:
dir /s /b Winre.wim在 C: 找不到 → 其实在 R: 恢复分区的Recovery\WindowsRE\Winre.wim,直接reagentc /enable就注册启用,无需 copype 重建。 -
winpeshl.ini 指向的每个程序必须在注入列表里:
inject_winre_payload()复制列表漏了RecoveryLauncher.cmd→ WinRE 启动找不到入口 → 走默认恢复界面超时回 Windows(表现为 prepare 成功、bootsequence 被消费,但 Recovery.log 空、WIM 没生成)。 -
Recovery.exe 会变成旧版:
build-win.sh只复制BackupRestore.exe没复制成Recovery.exe,而 winpeshl.ini 按Recovery.exe启动 → 部署时必须两个名字一起 copy。 -
还原不能在目标盘上运行程序:程序目录在 C: 而还原目标也是 C: 时,代码主动拒绝(运行中的程序会把自己删掉)——须把整个程序目录挪到非目标盘再跑。
-
本平台坑集合:.cmd/.bat 从 macOS 复制必须转 CRLF;
schtasks /delete /f和del被安全策略判定不可逆而拦截(改用/create /f覆盖、>覆盖写)。 -
WinRE 与 PE 不能共用 winpeshl.ini(v1.6.0 修复):产品包拆成
winre-winpeshl.ini(只允许Recovery.exe recover-env)与winpe-winpeshl.ini(只允许Recovery.exe --pe-desktop),缺任一必需文件即失败且不提交 WIM。
智能分流与 PE 桌面(1408)
-
判断口径:备份比
source_drive、还原比target_drive,与%SystemDrive%比——不是「含 Windows 的卷」(双系统场景会错);相等弹 PE/RE/取消三按钮,不等走后台在线 DISM(不重启)。 -
自绘模态对话框:项目无 manifest、comctl32 v6 的 TaskDialogIndirect 不可依赖,弹窗得自绘。
-
Hwnd 不能跨线程 move(
*mut c_void不是 Send)→ 先转usize,线程里再as Hwnd。 -
PE 桌面 6 按钮:备份/还原/安装第二系统/命令提示符/返回 Windows/打开完整程序;「重启」合并进「返回 Windows」。
-
「打开完整程序」坑:
should_launch_gui()检查 exe 文件名 == "BackupRestore",PE 里是 Recovery.exe,无参数启动走 CLI 不进 GUI → 必须带--tab。 -
状态栏会被 tab 控件覆盖(y=440 与备份 tab 的镜像路径/压缩率重叠)→ 关键错误/状态提示不能只写状态栏,必须弹窗。
GUI 键盘导航与 prlctl 键盘通道(1408)
-
鼠标注入死路:cu 点击 Parallels 窗口、VM 内 mouse_event/SendInput 都不进 Parallels 订阅管道(人手鼠标走 IOHID 专线所以有效)→ 自动化只能靠键盘。
-
prlctl send-key-event 宿主导入可用:
-k <十进制键码> -e press|release走官方输入管道,不依赖 CGEvent、不依赖 VM 内进程;组合键依次 press 修饰→press 主键→release 主键→release 修饰。 -
GUI 键盘化:三个消息循环全改
IsDialogMessageW路由(Tab/方向键/回车/Esc);主窗口加 WM_KEYDOWN 快捷键(Ctrl+B/R/P/O、F5、Ctrl+Enter,走 PostMessage WM_COMMAND 与真点击同路径)。 -
F5 坑:
IsDialogMessage会吞掉 F5 → 要在它之前拦截;且 Parallels-k对 F 键映射错误,F 键得用 scancode(-s 63)。 -
Ctrl 检测用 GetAsyncKeyState:
GetKeyState读消息队列有延迟,prlctl 注入偶发不生效;GetKeyState(...) as i16 & 0x8000直接& 0x8000会因 i16 溢出编译错。 -
reagentc /boottore在 Parallels UEFI 下无效:prepare 链执行成功(报"操作成功")但重启后不进 WinRE——Parallels 固件不消费一次性 WinRE 启动。真实硬件应有效;VM 内验证 WinRE 只能看到「prepare→任务创建→重启」。 -
在线备份 exit=87:
execute_online拼 cmd 字符串时索引名2026-09-13 20:43带空格、/Name:没加引号 → cmd 拆参报 87;凡是拼字符串执行的路径、名称必须自己加引号(run_logged用参数数组则安全)。
- 点赞
- 收藏
- 关注作者
评论(0)