Files
Thing/ThingHK/PawnIoSupport.cs
T
2026-08-31 18:05:27 +08:00

102 lines
3.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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}";
}
}
}