fix(windows): 驱动安装假成功——InstallShield 壳提前退出骗过 -Wait,改 pnputil 实时校验

用户实测:一键安装日志显示 12 秒"装完"但打印机仍离线。根因:HiTi 的
Setup64.exe 是自解压壳(InstallShield 类),Start-Process -Wait 等的是壳进程,
内层安装向导未真正完成即返回;且旧的注册表标记机制把这次假安装记成了"已装"。

修复:
- 安装流程尾部增加 pnputil /enum-drivers 校验(匹配 cs2xxhcd/htitahcd/hiti),
  未发现驱动即报"校验未通过"(引导重试或手动运行 Setup64),不再盲写标记
- queryInstalled 改为 pnputil 实时校验驱动商店(唯一真源,废弃注册表标记——
  假安装误写后向导会错误地不再显示安装按钮)
- 成功日志改为提示"拔插 USB 重新枚举设备后再检测"
This commit is contained in:
dzc committed 2026-09-21 16:46:31 +08:00
1 parent 7e65d22f09
commit 9ece6cc294
1 file changed
+25 -18
+25 -18
View File
@@ -6,13 +6,11 @@ import '../utils/command_runner.dart';
///
/// - macOS:CUPS 过滤器 pkg(dmg 内置),已装判断 = /Library/Printers/HiTi 存在,
/// 安装 = 挂载 dmg → osascript 提权 installer → 卸载卷
/// - Windows:HiTi 官方驱动 exe(assets/drivers/*.exe 内置),UAC 提权运行;
/// 已装判断 = 我们自己的注册表标记(HKCU\Software\zax\desktop-plugin,
/// 系统级驱动安装状态无统一可查标志,安装成功后写标记最可靠)
/// - Windows:HiTi 官方驱动整目录 zip 内置,已装判断 = pnputil 枚举驱动商店
/// (实时校验,安装器壳提前退出不会误判),安装 = 解压 → UAC 提权 Setup64 →
/// pnputil 校验通过才算成功
class DriverInstall {
static const installedMarker = '/Library/Printers/HiTi';
static const _winMarkKey = r'HKCU\Software\zax\desktop-plugin';
static const _winMarkValue = 'HiTiDriverInstalled';
static bool get isSupported => Platform.isMacOS || Platform.isWindows;
@@ -22,15 +20,14 @@ class DriverInstall {
return false; // Windows 走异步 queryInstalled(注册表查询需要子进程)
}
/// Windows:异步查询安装标记。
/// Windows:pnputil 实时校验驱动商店(安装器是自解压壳、可能假成功,
/// 注册表标记会被失败的安装误写,故以系统驱动商店为唯一真源)。
static Future<bool> queryInstalled() async {
if (!Platform.isWindows) return isInstalled();
final out = await runCommand(
'reg',
['query', _winMarkKey, '/v', _winMarkValue],
tolerateExit: true,
);
return out.contains('0x1');
final drivers = await runCommand('pnputil', ['/enum-drivers'],
tolerateExit: true, timeoutMs: 60000);
final lower = drivers.toLowerCase();
return lower.contains('cs2xxhcd') || lower.contains('htitahcd') || lower.contains('hiti');
}
/// 内置驱动安装包的文件系统路径;未打包携带返回 null。
@@ -135,10 +132,11 @@ class DriverInstall {
throw StateError('驱动包内未找到安装程序(Setup64.exe)');
}
log('开始安装 HiTi 打印机驱动(请在 UAC 弹窗中确认,安装向导可能需要点几次"下一步")…');
// -Verb RunAs 触发 UAC;-Wait 等安装程序退出(用户取消 UAC 时 PowerShell 报错)
// 注意:HiTi 的 Setup64 是自解压壳(InstallShield 类),-Wait 等的是壳进程,
// 内层安装向导可能仍在运行——安装是否成功必须以后续的驱动校验为准
final script = 'Start-Process -FilePath "$setup" -Verb RunAs -Wait';
await runCommand('powershell', ['-Command', script], timeoutMs: 600000);
log('驱动安装程序已退出');
log('安装程序已退出,正在校验驱动是否装入系统…');
} finally {
// 3. 清理临时目录(失败不影响安装结果)
try {
@@ -146,10 +144,19 @@ class DriverInstall {
} catch (_) {}
}
// 4. 写安装标记(我们自己的注册表键;0x1 = 已装)
await runCommand('reg',
['add', _winMarkKey, '/v', _winMarkValue, '/t', 'REG_DWORD', '/d', '1', '/f']);
log('已记录驱动安装状态');
// 4. 校验:驱动是否真的进了驱动商店(pnputil 枚举,匹配 htitahcd/CS2xx)
await Future<void>.delayed(const Duration(seconds: 3)); // 给安装收尾一点时间
final drivers = await runCommand('pnputil', ['/enum-drivers'],
tolerateExit: true, timeoutMs: 60000);
final lower = drivers.toLowerCase();
final installed = lower.contains('cs2xxhcd') || lower.contains('htitahcd') || lower.contains('hiti');
if (!installed) {
throw StateError(
'驱动校验未通过:系统中未发现 HiTi 驱动(安装器可能未真正完成,请重试,'
'或手动运行驱动包 CS2xx 目录内的 Setup64.exe 完成安装)');
}
log('校验通过:HiTi 驱动已装入系统');
log('驱动安装完成——请拔掉打印机 USB 重新插上(驱动绑定需要设备重新枚举),然后重新检测');
}
static String? _findFileDeep(String root, String lowerName) {