using System.Diagnostics; using Microsoft.Win32; namespace ThingHK; /// /// PawnIO 驱动支持:检测 + 静默安装。 /// /// 背景:LHM 读取 CPU 温度/频率等 ring0 数据依赖内核驱动,回退用的 WinRing0 被 /// 微软"易受攻击的驱动程序阻止列表"和部分杀软(如火绒)拦截,导致传感器缺失。 /// PawnIO 是正规签名的替代驱动(不在阻止列表、兼容 HVCI/安全启动), /// LHM 0.9.5+ 检测到已安装时优先使用,无需任何代码开关。 /// /// 安装器约定:PawnIO_setup.exe 与 ThingHK.exe 同目录 /// (由 Tauri 侧 prepare_kernel 从资源目录随内核一起复制到 {app_data}/monitor/cores/)。 /// /// 静默参数:-install -silent(官方 CLI 参数,见 namazso/PawnIO.Setup)。 /// 退出码:0=成功;3010=成功但需重启(ERROR_SUCCESS_REBOOT_REQUIRED)。 /// /// 策略:仅在内核已提权时安装。两种提权模式(Thing 提权继承 / 仅提权 ThingHK) /// 都只有一次 UAC,内核拿到权限后自行静默安装,避免二次弹窗。 /// serve 模式调用;scan 诊断模式不安装,保持被动。 /// internal static class PawnIoSupport { /// 驱动服务注册表键:存在即认为已安装 private const string ServiceKeyName = @"SYSTEM\CurrentControlSet\Services\PawnIO"; private const string SetupFileName = "PawnIO_setup.exe"; /// 3010 = ERROR_SUCCESS_REBOOT_REQUIRED(安装成功但需重启生效) private const int ExitCodeRebootRequired = 3010; /// 驱动安装通常数秒内完成,留足余量防止卡死启动流程 private const int InstallTimeoutMs = 90_000; /// 检测 PawnIO 驱动服务是否已注册 public static bool IsServiceInstalled() { try { using var key = Registry.LocalMachine.OpenSubKey(ServiceKeyName); return key != null; } catch { return false; } } /// /// 确保 PawnIO 就绪:已安装直接返回;未安装且当前已提权时静默安装。 /// 返回描述性结果(写入 stderr 日志 + /status 诊断)。 /// public static string EnsureInstalled() { if (IsServiceInstalled()) return "already-installed"; if (!HardwareManager.IsRunningAsAdmin()) return "skipped: not elevated (温度/频率等传感器需要提权运行)"; string setupPath = Path.Combine(AppContext.BaseDirectory, SetupFileName); if (!File.Exists(setupPath)) return $"skipped: {SetupFileName} 未找到(应随内核一起部署,见 prepare_kernel)"; try { using var process = Process.Start(new ProcessStartInfo { FileName = setupPath, Arguments = "-install -silent", UseShellExecute = false, CreateNoWindow = true, }); if (process == null) return "failed: Process.Start 返回 null"; if (!process.WaitForExit(InstallTimeoutMs)) { try { process.Kill(); } catch { /* 超时后进程可能已自行退出 */ } return "failed: 安装超时"; } int code = process.ExitCode; if (code == ExitCodeRebootRequired) return "installed: 需重启后生效"; if (code != 0) return $"failed: 安装器退出码 {code}"; return IsServiceInstalled() ? "installed" : "failed: 安装器返回 0 但服务未注册"; } catch (Exception ex) { return $"failed: {ex.Message}"; } } }