首页
/ PowerToys Hosts 文件编辑器:模块架构、Hosts 解析引擎与源码级实现解析

PowerToys Hosts 文件编辑器:模块架构、Hosts 解析引擎与源码级实现解析

2026-09-04 11:46:18作者:邵娇湘

本文基于 PowerToys 仓库中 Hosts File Editor 模块的开发文档与配套源码,完整讲解该模块的三项目结构(入口宿主、WinUI 3 UI 库、Runner 接口)、hosts 文件的解析与校验规则(IPv4/IPv6 正则、单行 9 个主机名上限)、写入与备份机制、settings.json 配置项及其默认值,并给出从 runner 加载到文件保存的完整调用链。读完后可直接定位该模块的每一层实现,理解其读写 C:\Windows\System32\drivers\etc\hosts 的完整技术路径。

PowerToys Hosts File Editor 模块结构图

模块定位:用 GUI 管理系统的 hosts 文件

Hosts File Editor 是 PowerToys 中用于编辑系统 hosts 文件的模块。hosts 文件是操作系统用来把主机名映射到 IP 地址的纯文本文件,用户可以通过它为特定域名覆盖 DNS 解析结果。该模块把原本需要手动编辑文本(且通常需要管理员权限)的操作封装成了带校验、过滤、备份和 Ping 检测的图形界面。

模块代码位于 src/modules/Hosts,文档明确指出其结构设计与 Environment Variables 模块类似,分为三个主要组件:

  1. Hosts —— 模块入口点,通过 helper 工具类管理核心服务与设置;
  2. HostsModuleInterface —— 定义 Hosts 模块与 PowerToys 系统集成(Runner 加载)的接口;
  3. HostsUILib —— 使用 WinUI 3 实现的 UI 层库。

此外仓库中还包含三类测试工程,是理解各组件行为的最佳佐证材料:

Runner 集成:模块如何被加载

文档指明模块由 PowerToys runner 负责加载。在 main.cpp 中可以看到模块注册处加载的是 L"WinUI3Apps/PowerToys.HostsModuleInterface.dll"(当前仓库中该字符串位于 main.cpp 附近)。

从源码结构看,完整的启动链路是:

  1. runner 通过 LoadLibrary 加载 C++ 工程 HostsModuleInterface(入口为 dllmain.cpp);
  2. 接口 DLL 启动独立的 Hosts 应用进程(C# WinUI 3 应用);
  3. 进程入口 Program.cs 依次完成三件事:
    • 初始化日志到 \Hosts\Logs,并调用 WinRT.ComWrappersSupport.InitializeComWrappers()
    • 检查 GPO 策略:若 GPOWrapper.GetConfiguredHostsFileEditorEnabledValue() 返回 Disabled,直接退出并提示联系系统管理员(见 Program.cs);
    • 通过 AppInstance.FindOrRegisterForKey("PowerToys_Hosts_Instance") 保证单实例,非首个实例直接退出;
  4. 首个实例启动 XAML 应用,进入 App.xaml.cs 并加载主窗口 MainWindow.xaml.cs

主工程 Hosts:服务注册与设置监听

Host 服务注册中心

Host.cs 是该工程的服务注册入口,负责把 HostsServiceUserSettings、备份与提权 helper 等实例装配起来,供 UI 页面注入使用。

NativeEventWaiter:跨线程 UI 更新

NativeEventWaiter.cs 的作用是“从后台线程获取 dispatcher 队列并投递 UI 更新”,这是 WinUI 3 应用中典型的后台工作(如文件读取、Ping)通知 UI 线程刷新数据的机制,公共库 NativeEventWaiter.cs 中也有同款实现。

UserSettings:settings.json 的读取、追踪与热更新

UserSettings.cs 实现了 IUserSettings 接口,负责从 settings.json 读取、追踪并更新用户设置,核心机制有四点:

  • 缺失即重建LoadSettingsFromJson() 发现 Hosts 模块的 settings.json 不存在时,会用 new HostsSettings() 以默认值重新创建并保存;
  • 重试保护:读取异常时最多重试 MaxNumberOfRetry = 5 次,每次间隔 500ms,防止 runner 与设置界面并发写入时读到损坏的 JSON;
  • 热更新:构造函数中通过 Helper.GetFileWatcher(HostsModuleName, "settings.json", ...) 注册文件监视,settings.json 被外部(如设置界面)修改后自动重新加载,无需重启应用;
  • 变更事件LoopbackDuplicates 属性的 setter 在值变化时触发 LoopbackDuplicatesChanged 事件,UI 据此重新计算重复标记。

HostsUILib:解析引擎、校验规则与写入机制

Entry 模型:一行 hosts 文本的完整解析

Entry.cs 表示单条 hosts 条目(IP 地址、主机名列表、注释、启用标志)。构造函数 Entry(int id, string line) 调用 Parse(),其解析规则如下:

  1. # 开头的行视为注释行,Active = false
  2. 先按 # 分割,第一部分为“地址 + 主机名”,其余部分拼接为 Comment
  3. 地址部分按空格/Tab 拆分,第一个能被 IPAddress.TryParse 识别且包含 .: 的元素作为 Address,其余全部归入 Hosts(以空格连接);
  4. Address 属性变更时通过 OnAddressChanged 重新判定类型:依次尝试 ValidationHelper.ValidIPv4ValidationHelper.ValidIPv6,都不满足则标记为 AddressType.Invalid(枚举定义在 AddressType.cs)。

Entry 使用 CommunityToolkit.Mvvm[ObservableProperty] 宏生成属性变更通知,供 XAML 双向绑定;Valid 属性聚合了地址校验与主机名校验两个维度。

校验规则:正则与 MaxHostsCount

ValidationHelper.cs 定义了全部校验逻辑:

  • IPv4:逐段匹配 25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?,即每段 0–255 的严格正则;
  • IPv6:覆盖完整 8 组、:: 压缩、IPv4 尾段混合(::ffff:a.b.c.d)及 link-local 带 %zone 的完整正则;
  • 主机名:对空格拆分后的每个 host 调用 Uri.CheckHostName,返回 Unknown 即判非法;同时受单行上限约束——Consts.csMaxHostsCount = 9,注释说明这是“系统在一行中可检测的最大主机数”。

单行超过 9 个主机名时,HostsService.ReadAsync 不会丢弃该行:它先用 entry.Validate(false)(不校验长度)确认其余部分合法,然后按 Chunk(Consts.MaxHostsCount) 把超长的 Hosts 拆成多条克隆条目,并置位 HostsData.SplittedEntries 供 UI 提示用户。测试用例 EntryTest.cs 对解析与拆分行为有对应断言。

HostsData:解析结果的只读封装

HostsData.cs 将解析结果封装为三个部分:

  • EntriesReadOnlyCollection<Entry>):成功解析的条目集合;
  • AdditionalLines(string):无法按 hosts 语法解析的行(空行除外),原样保留以便回写;
  • SplittedEntries(bool):是否有条目因超长被拆分。

这正是文档 Key Features 表中 ReadHosts() 背后“load and parse file”的具体形态。

HostsService:读取、写入、备份与 Ping

HostsService.cs 是与磁盘交互的核心服务,要点如下:

文件路径与外部变更监听

  • 路径固定为 %Windows%\System32\drivers\etc\hosts(构造函数中 Environment.SpecialFolder.Windows 拼接);
  • 通过 IFileSystemWatcher 监视该目录的 hosts 文件 LastWrite 变化,触发 FileChanged 事件,UI 据此提示文件已被外部修改;
  • 编码由用户设置决定:HostsEncoding.Utf8 对应无 BOM 的 UTF8Encoding(false),否则为带 BOM 的 UTF8Encoding(true)

写入前置检查(两个异常类)

WriteAsync 写入前做两道检查,对应 Exceptions 下的两个异常:

  1. 未提权 → 抛出 NotRunningElevatedException
  2. 文件带只读属性 → 抛出 ReadOnlyHostsException(UI 可引导用户调用 RemoveReadOnlyAttribute() 解除)。

格式化与 additional lines 定位

写入时对所有条目做对齐:取最长地址长度与最长主机名长度分别 PadRight,注释以 # 追加在行尾;被禁用的条目前缀 # (若存在任何禁用条目且未开启 NoLeadingSpaces,启用条目前会补两个空格以对齐列)。additionalLinesAdditionalLinesPosition 设置插入文件顶部(Top)或底部(Bottom)。

备份与并发安全

写文件前会先关闭 watcher(避免把自身写入识别为外部变更)、调用 _backupManager.Create(HostsFilePath) 生成备份、以 FileMode.OpenOrCreate 打开流(注释说明这是为了防止 hosts 文件为隐藏属性时抛出 UnauthorizedAccessException),最后 stream.SetLength(stream.Position) 截断超长旧内容。整个写入过程由 SemaphoreSlim 串行化。

Ping 检测

PingAsync 使用 .NET 的 Ping.SendPingAsync(address, 4000),4000ms 与 ping.exe 的默认超时一致,用于 UI 中逐行 Ping 验证目标可达性。

OpenHostsFile

OpenHostsFile() 以管理员权限调用 System32\notepad.exe 直接打开 hosts 文件,对应文档 Key Features 中的“Open Hosts File”功能。

调用流程:从启用模块到保存文件

综合文档的 Call Flow 与各源码文件,完整调用链为:

  1. Enable app:在设置界面(src/settings-ui)启用 Hosts 模块,runner 加载 HostsModuleInterface DLL 并拉起独立进程;
  2. Start appProgram.csHostsXAMLApp 初始化服务容器、加载主窗口)→ HostsMainPage
  3. Load hosts dataMainViewModel 调用 HostsService.ReadAsync() 得到 HostsData(解析、拆分超长行、收集 additional lines);
  4. User edits:XAML 控件双向绑定到 MainViewModelEntry 的 Observable 属性,编辑即时反映到条目状态;
  5. Save changes:ViewModel 触发 HostsService.WriteAsync(),按前述“提权检查 → 只读检查 → 备份 → 对齐格式化 → 写文件”完成落盘;
  6. Settings managementUserSettings.cs 持久化用户偏好并监视 settings.json 变化。

Key Features 与源码对应

功能 文档所列方法 源码位置
添加新条目 Add(Entry entry) MainViewModel.cs
过滤 hosts 条目 ApplyFilters() MainViewModel.cs
打开 Hosts 文件 ReadHosts() MainViewModel.cs,底层为 HostsService.OpenHostsFile
Additional Lines UpdateAdditionalLines(string lines) MainViewModel.cs

设置项:settings.json 中的 Hosts 配置与默认值

设置管理三件套

文档 Settings Management 一节列出了设置侧的三个文件,均已在仓库中确认存在:

  • HostsViewModel.cs:PowerToys 设置界面中 Hosts 页面的 ViewModel;
  • HostsProperties.cs:settings.json 中 Hosts 节点对应的属性类;
  • HostsSettings.cs:包裹 HostsProperties 的设置对象,提供 Save(SettingsUtils) 序列化能力。

完整设置项与默认值

HostsProperties.cs 的构造函数给出了全部默认值,结合 UserSettings.cs 的读取逻辑,可整理出完整的设置清单:

设置 实现属性 类型 默认值
启动时显示警告(ShowStartupWarning) UserSettings()->ShowStartupWarning bool true
以管理员身份打开(Open as administrator) HostsProperties.LaunchAdministrator bool true
将环回地址视为重复(Consider loopback addresses as duplicates) UserSettings()->LoopbackDuplicates bool false
Additional lines 位置 UserSettings()->AdditionalLinesPosition HostsAdditionalLinesPosition Top
编码 UserSettings()->Encoding HostsEncoding Utf8
禁用行不对齐前导空格 UserSettings()->NoLeadingSpaces bool false
保存前备份 UserSettings()->BackupHosts bool true
备份路径 UserSettings()->BackupPath string %Windows%\System32\drivers\etc
删除备份模式 UserSettings()->DeleteBackupsMode HostsDeleteBackupMode Age
按天数删除备份 UserSettings()->DeleteBackupsDays int 15
按数量删除备份 UserSettings()->DeleteBackupsCount int 5

其中枚举定义在 HostsUILib/SettingsHostsAdditionalLinesPosition.csTop/Bottom)、HostsEncoding.csHostsDeleteBackupMode.csLaunchAdministratortrue 时,Hosts 编辑器以管理员身份启动,从而满足 WriteAsync 的提权前置检查;这也是“Open as administrator”设置能够保证保存成功的关键。

测试体系:单元测试、模糊测试与 UI 自动化迁移

该模块的可测性建设在 PowerToys 各模块中较为完整:

  • 单元测试 Hosts.TestsEntryTest.cs 验证行解析与条目拆分,ValidationHelperTest.cs 覆盖 IPv4/IPv6/主机名校验边界,HostsServiceTest.cs 借助 CustomMockFileSystem 验证读写行为,BackupManagerTest.cs 验证备份策略;
  • 模糊测试 Hosts.FuzzTestsFuzzTests.cs 对 hosts 文本解析做模糊输入测试,并配置了 OneFuzzConfig.json 接入微软的 OneFuzz 平台;
  • UI 测试 Hosts.UITests:包含 HostModuleTests.csHostsSettingTests.cs 与按 x64Win10/x64Win11/arm64 分平台的 UI 基线截图(如添加条目、空视图等场景)。

文档同时说明 Hosts File Editor 正在进行 UI 测试迁移以提升自动化覆盖率,迁移进度清单位于仓库内的 Release-Test-Checklist-Migration-Progress.md

构建与调试

文档给出的调试步骤,结合仓库工程结构可以落地为:

  1. 以 Debug 模式构建 PowerToys 解决方案(解决方案文件为 PowerToys.slnx);
  2. 将 Hosts 项目(Hosts.csproj)设为启动项目;
  3. 以调试模式启动 Hosts File Editor;
  4. 将调试器附加到 PowerToys.Hosts.dll 进程;
  5. 在 Hosts 代码中(例如 MainViewModelHostsService)添加断点。

独立调试 UI 层时直接启动 Hosts 宿主工程即可;若要验证与 runner 的集成(模块加载、设置联动、GPO 拦截),则需整体构建并运行 PowerToys runner,观察 main.cppPowerToys.HostsModuleInterface.dll 的加载与进程拉起行为。

小结

Hosts File Editor 是一个结构清晰、边界明确的 WinUI 3 模块:Hosts 宿主负责启动、GPO 检查与设置监听,HostsUILib 承载全部业务逻辑(解析、校验、备份、写入、Ping),HostsModuleInterface 仅作为 runner 的加载入口。源码中几个值得借鉴的工程实践包括:单行 9 个主机名上限的自动拆分而非报错、写入前的双重前置异常(未提权/只读)、写入时自动备份并临时挂起文件监视、以及 settings.json 缺失时以默认值自愈重建。这些细节配合单元测试与模糊测试,构成了该模块在读写系统关键文件时的高可靠性基础。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
504
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384