102 lines
3.8 KiB
C#
102 lines
3.8 KiB
C#
using System.Diagnostics;
|
||
using Microsoft.Win32;
|
||
|
||
namespace ThingHK;
|
||
|
||
/// <summary>
|
||
/// 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 诊断模式不安装,保持被动。
|
||
/// </summary>
|
||
internal static class PawnIoSupport
|
||
{
|
||
/// <summary>驱动服务注册表键:存在即认为已安装</summary>
|
||
private const string ServiceKeyName = @"SYSTEM\CurrentControlSet\Services\PawnIO";
|
||
|
||
private const string SetupFileName = "PawnIO_setup.exe";
|
||
|
||
/// <summary>3010 = ERROR_SUCCESS_REBOOT_REQUIRED(安装成功但需重启生效)</summary>
|
||
private const int ExitCodeRebootRequired = 3010;
|
||
|
||
/// <summary>驱动安装通常数秒内完成,留足余量防止卡死启动流程</summary>
|
||
private const int InstallTimeoutMs = 90_000;
|
||
|
||
/// <summary>检测 PawnIO 驱动服务是否已注册</summary>
|
||
public static bool IsServiceInstalled()
|
||
{
|
||
try
|
||
{
|
||
using var key = Registry.LocalMachine.OpenSubKey(ServiceKeyName);
|
||
return key != null;
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 确保 PawnIO 就绪:已安装直接返回;未安装且当前已提权时静默安装。
|
||
/// 返回描述性结果(写入 stderr 日志 + /status 诊断)。
|
||
/// </summary>
|
||
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}";
|
||
}
|
||
}
|
||
}
|