UpdateLock:一个原生 Win32 的 Windows 自动更新管理工具

在 Windows 使用过程中,Windows Update 是一项非常重要的系统基础设施,但在某些场景下,用户可能希望暂时控制自动更新行为。例如测试环境、旧软件兼容性环境、长期运行的专用设备,或者需要避免系统在特定时间自动重启的场景。

UpdateLock 正是针对这一需求设计的一个轻量级 Windows 工具。项目地址:

hhht110/UpdateLock GitHub 仓库

与许多依赖脚本、PowerShell 或第三方运行时的方案不同,UpdateLock 采用 原生 C++/Win32 实现,以单文件 EXE 的形式运行,并针对 Windows 7、Windows 10 和 Windows 11 分别采用不同的系统策略。

本文主要从工程实现角度介绍项目。关闭系统更新可能降低系统安全性,实际使用时应根据设备用途和安全策略谨慎决定。

一、为什么要做 UpdateLock?

Windows Update 并不是简单地“开”或者“关”。

不同 Windows 版本在更新机制、策略项以及相关服务方面存在明显差异。尤其是 Windows 7 与 Windows 10/11,在更新管理模型上并不完全相同。

因此,一个真正面向多个 Windows 版本的更新控制工具,需要解决几个问题:

  1. 如何识别当前 Windows 版本?
  2. 不同系统应该修改哪些策略?
  3. 如何避免粗暴地删除系统文件或者破坏更新组件?
  4. 修改之前如何保存用户原有配置?
  5. 如果修改失败,如何恢复现场?
  6. 恢复之后如何确认配置真的恢复成功?
  7. 如何在没有 .NET、PowerShell 或其他额外组件的情况下运行?

UpdateLock 的设计基本围绕这些问题展开。

项目 README 将它定义为一个“Win7/10/11 自动更新关闭工具”,采用原生 Win32 x86 EXE,并通过 Windows Registry API 和 Service Control Manager API 完成系统配置修改。


二、项目最大的特点:原生 Win32

UpdateLock 并没有选择 PowerShell、批处理或者 reg.exesc.exe 这样的外部命令,而是直接调用 Windows API。

从源码可以看到,它直接包含了:

  • windows.h
  • commctrl.h
  • shellapi.h
  • shlobj.h
  • sddl.h
  • aclapi.h
  • wincrypt.h
  • objbase.h
  • wuapi.h

核心逻辑直接建立在 Win32 API 之上。

这种设计带来的好处非常明显。

1. 不依赖 PowerShell

很多 Windows 自动化脚本都会执行 PowerShell,例如:

Set-Service
Set-ItemProperty
Get-Service

而 UpdateLock 不依赖这些命令。

这意味着它不需要考虑 PowerShell 是否可用、执行策略是否限制脚本,以及外部命令输出解析等问题。

2. 不依赖 .NET Runtime

项目采用静态 CRT 的方式构建,目标是在 Windows 7 SP1、Windows 10 和 Windows 11 上直接运行。

因此最终用户面对的就是一个比较传统的:

UpdateLock.exe

而不是需要先安装运行环境的软件包。

3. 更直接的错误处理

直接调用 Win32 API,也意味着程序可以获得更加明确的系统错误码。

源码中定义了 ThrowWin32(),会把 Win32 错误码转换为用户可以理解的错误信息,例如:

Win32 5:拒绝访问

这种方式比简单地判断一个外部命令返回值更加直接。


三、Win7 与 Win10/11 为什么要区别处理?

这是 UpdateLock 设计中比较值得关注的一点。

项目没有采用“一套策略打天下”的方式,而是明确将 Windows 7 与 Windows 10/11 分开处理。

Windows 7

Windows 7 的处理主要涉及:

  • NoAutoUpdate
  • DisableWindowsUpdateAccess
  • wuauserv

其中 wuauserv 是 Windows Update 服务。

也就是说,Win7 的方案除了修改策略之外,还会通过 Windows 的 Service Control Manager 对 Windows Update 服务进行停止和禁用。

但项目刻意没有进一步处理:

  • BITS
  • CryptSvc
  • TrustedInstaller
  • SoftwareDistribution
  • Catroot2
  • 系统文件

这种克制其实非常重要。

一个系统工具如果为了“彻底关闭更新”而直接删除更新缓存、修改系统目录甚至替换系统文件,很容易从一个配置工具变成系统破坏工具。

UpdateLock 的方案明显更倾向于:

只修改完成目标所必需的配置。


四、Windows 10/11:主要通过策略控制

Windows 10 和 Windows 11 的处理方式有所不同。

项目主要涉及以下策略:

ExcludeWUDriversInQualityUpdate
SetUpdateNotificationLevel
UpdateNotificationLevel
NoAutoUpdate
NoAutoRebootWithLoggedOnUsers
AUOptions

其中比较值得注意的是 AUOptions

关闭时,项目不是简单地把它固定成某个值,而是采用:

关闭时删除,恢复时重新写回原来的状态。

这体现了一个很重要的配置管理思想:

“恢复”不应该意味着恢复成程序认为的默认值,而应该恢复成用户运行程序之前的状态。

例如用户原来的配置可能是:

AUOptions = 4

程序执行关闭操作后,这个值可能被删除。

当用户选择恢复时,程序重新写入:

AUOptions = 4

而不是自作主张地写入另一个默认值。


五、2.1 版本进一步限制 Windows Update 入口

UpdateLock 2.1 又增加了一项:

SetDisableUXWUAccess=1

这个策略的目标并不是停止 Windows Update 服务,而是进一步限制 Windows Update 的用户界面入口。

简单来说,就是减少用户通过 Windows Update 页面进行:

  • 手动检查更新
  • 下载更新
  • 安装更新

等操作的机会。

这里有一个很重要的区别:

Windows 10/11 不会因此直接停止 wuauserv、BITS、UsoSvc、WaaSMedicSvc 或 Update Orchestrator。

这说明项目作者并没有采用“看到更新服务就全部禁用”的思路,而是尽量使用 Windows 本身提供的策略机制。

从系统稳定性的角度看,这是一种相对保守的方案。


六、备份与恢复是这个项目的核心设计之一

如果只是修改注册表,其实并不难。

真正困难的是:

修改之后如何可靠地恢复?

UpdateLock 在这一点上做得比较完整。

Windows 10/11 的策略备份位于:

%ProgramData%\UpdateLock\policy-backup-v1.txt

新增的 Windows Update 用户界面访问限制,则单独保存:

%ProgramData%\UpdateLock\windows-update-access-backup-v1.txt

Windows 7 使用:

%ProgramData%\UpdateLock\windows7-original-state-v1.txt

这种设计意味着程序在修改系统之前,会先记录原始状态。

而不是简单地假设:

恢复 = 删除我刚刚写入的值

两者有本质区别。

例如:

用户原配置
    ↓
读取并保存
    ↓
UpdateLock 修改
    ↓
Windows Update 被限制
    ↓
用户选择恢复
    ↓
重新写入原配置

这样才能真正做到“恢复”。


七、源码中的 RAII:避免资源泄漏

作为一个原生 C++ Windows 程序,UpdateLock 还使用了 RAII 来管理系统资源。

源码中可以看到几个典型的封装:

ScopedHandle
ScopedRegKey
ScopedServiceHandle

例如:

struct ScopedRegKey {
    HKEY value = nullptr;

    ~ScopedRegKey() {
        if (value) RegCloseKey(value);
    }
};

这类设计解决的是一个非常典型的 C++/Win32 问题。

Win32 API 大量使用类似:

HANDLE
HKEY
SC_HANDLE

的资源句柄。

如果程序中途发生异常,很容易出现:

打开资源
↓
执行操作
↓
中途失败
↓
没有释放资源

而 RAII 可以让资源生命周期与 C++ 对象绑定。

因此:

{
    ScopedRegKey key;
    // 操作注册表
}

离开作用域后,注册表句柄自动释放。

这对于一个需要频繁处理注册表、服务和文件的系统工具来说尤其重要。


八、注册表操作并不是简单的“写值”

源码中的注册表逻辑也比较值得分析。

程序定义了类似这样的策略描述:

struct PolicyValue {
    const wchar_t* path;
    const wchar_t* name;
};

然后使用:

ReadDwordState()
SetDword()
DeleteDword()
RestorePolicy()
VerifyPolicy()

形成了一套比较清晰的操作链。

可以抽象为:

Read
 ↓
Backup
 ↓
Modify
 ↓
Read Back
 ↓
Verify

而不是:

Modify
 ↓
Done

这就是系统工具和简单脚本之间一个很重要的区别。


九、修改之后还要“回读验证”

UpdateLock 有一个很实用的机制:

写入之后重新读取注册表进行验证。

源码中可以看到:

void VerifyPolicy(const PolicyState& expected)

它会重新调用读取函数,然后比较:

exists
value

是否与预期一致。

如果不一致,就抛出错误。

这意味着程序不是看到:

RegSetValueExW()

返回成功,就认为整个操作已经完成。

而是:

写入成功
    ↓
重新读取
    ↓
比较预期状态
    ↓
确认一致

对于涉及系统策略的工具来说,这种设计可以显著提高操作的可靠性。


十、失败回滚

进一步看,这套设计还可以形成一个完整的事务式流程:

保存原始状态
       ↓
修改策略 A
       ↓
修改策略 B
       ↓
修改策略 C
       ↓
某一步失败
       ↓
恢复 A
       ↓
恢复 B
       ↓
恢复 C

也就是说,它并不是简单执行一连串互不相关的注册表写操作。

如果中途失败,程序可以尝试将之前已经修改的内容恢复到原状态。

对于 Windows 系统配置工具来说,这一点非常关键。

因为系统配置修改最怕的并不是“完全失败”,而是:

成功了一半。

例如:

策略 1 修改成功
策略 2 修改成功
策略 3 修改失败

如果没有回滚机制,用户最终得到的就是一个半修改状态。


十一、备份文件本身也考虑了安全性

源码中还有一个容易被忽略的细节:

程序会创建:

%ProgramData%\UpdateLock

并设置目录权限。

源码使用安全描述符:

D:P(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)

也就是说,备份目录的访问权限被限制在系统账户和管理员范围。

这个设计很有意义。

因为备份文件中记录的是系统策略状态,如果允许普通用户随意修改这些文件,就可能出现:

程序保存状态
        ↓
文件被篡改
        ↓
用户点击恢复
        ↓
程序恢复恶意/错误配置

所以对于系统配置备份而言:

备份文件不是普通的文本文件。

它同样需要考虑完整性和权限。


十二、备份格式为什么使用 Base64?

源码还提供了:

Base64Encode()
Base64Decode()

并结合 UTF-8 与 UTF-16 之间的转换。

Windows API 大量使用 Unicode 字符串,而备份文件本身则采用 UTF-8 文本格式。

因此项目需要处理:

Windows UTF-16
        ↓
UTF-8
        ↓
Base64
        ↓
备份文件

恢复时再反向转换。

这种设计的好处是让备份文件格式更加明确,同时避免某些特殊字符、换行符或者本地编码环境造成解析问题。


十三、管理员权限与单实例保护

因为修改的是:

HKEY_LOCAL_MACHINE

以及 Windows 服务等系统级资源,所以程序需要管理员权限。

除此之外,项目还加入了单实例保护。

源码中定义了:

const wchar_t* const kMutexName =
    L"Global\\UpdateLock.SingleInstance.7A64EA9A";

通过 Windows Mutex 可以避免用户同时启动多个 UpdateLock 实例。

为什么需要单实例?

因为系统配置修改通常不是适合并发执行的操作。

如果两个实例同时执行:

实例 A:读取旧配置
实例 B:读取旧配置

实例 A:写入新配置
实例 B:写入新配置

实例 A:备份
实例 B:备份

最终的备份状态就可能出现问题。

因此:

单实例保护实际上也是配置事务安全的一部分。


十四、项目没有“暴力破坏”Windows Update

从工程角度来看,UpdateLock 一个值得肯定的地方,是它对修改边界控制得比较明确。

它没有把目标扩展成:

删除 SoftwareDistribution
删除 Catroot2
禁用所有相关服务
删除更新文件
修改系统文件
阻止 Windows Update 进程启动

而是主要集中在:

Windows Update Policy
+
必要的服务控制
+
备份与恢复

这是一种更符合系统工具设计原则的方式:

尽量通过操作系统提供的配置接口实现需求,而不是绕过系统机制。


十五、对普通用户来说,它解决了什么问题?

如果从用户角度来看,UpdateLock 的价值可以概括成一句话:

用一个原生、轻量的 Windows 程序,把复杂的更新策略修改、备份、恢复和验证封装起来。

用户不需要手动打开:

注册表编辑器
本地组策略
服务管理器
命令提示符
PowerShell

然后逐项修改。

程序负责完成:

识别系统
   ↓
读取原配置
   ↓
备份
   ↓
修改策略
   ↓
验证
   ↓
必要时回滚

恢复时则反过来:

读取备份
   ↓
恢复原配置
   ↓
回读
   ↓
验证

这比让普通用户手动操作注册表要友好得多。


十六、它也存在明确的使用边界

需要特别强调的是:

关闭或限制 Windows Update 并不是没有代价的。

首先,Windows Update 不只是负责系统补丁,也涉及驱动程序等内容。

项目 README 特别指出,关闭 Windows Update 的驱动更新后,新接入的 USB 或其他硬件可能无法自动在线获取驱动。

因此如果设备需要安装新硬件,可以考虑:

恢复 Windows Update
       ↓
安装/获取驱动
       ↓
重新关闭更新策略

此外,不建议在 Windows 正在安装或卸载更新时执行关闭或恢复操作。

对于生产环境设备,也应该提前确认:

  • 是否存在企业更新策略;
  • 是否有安全补丁要求;
  • 是否依赖 Windows Update 获取驱动;
  • 是否有统一终端管理系统;
  • 是否有恢复系统配置的要求。

换句话说:

UpdateLock 是一个系统配置工具,而不是 Windows 更新机制的替代品。


十七、构建方式

项目使用 Visual Studio Build Tools、MSVC v141 工具链以及 Windows 10 SDK 19041。

native 目录执行:

build-native.cmd

即可进行原生构建。

项目 README 表明,构建输出位于:

native\build

并且构建脚本同时生成:

UpdateLock.exe

以及原生测试程序。

这种项目结构比较适合希望研究 Win32 系统编程的开发者。

你可以直接从源码看到:

Win32 API
   ↓
Registry API
   ↓
SCM API
   ↓
文件 API
   ↓
安全描述符 API
   ↓
GUI

而不是隐藏在大量第三方框架之后。


十八、如何评价这个项目?

如果从“功能复杂度”来看,UpdateLock 并不是一个特别庞大的项目。

但如果从“系统工具的工程质量”来看,它里面包含了不少值得学习的设计:

1. 原生 API

避免依赖 PowerShell、CMD、reg.exe 和 sc.exe。

2. 状态备份

修改前记录原始配置。

3. 可恢复

恢复的是用户原来的状态,而不是简单恢复默认值。

4. 回读验证

写入之后再次读取确认。

5. 失败回滚

尽可能避免系统处于半修改状态。

6. RAII

自动管理 Win32 资源。

7. 权限控制

限制备份目录访问权限。

8. 单实例

避免多个配置操作同时进行。

这些设计其实已经超出了一个简单“关闭 Windows Update 脚本”的范畴。


十九、从这个项目可以学到什么?

如果你正在学习 Windows C++ 开发,UpdateLock 值得关注的并不仅仅是“如何关闭 Windows Update”。

更值得学习的是它背后的工程思路:

不要直接修改
      ↓
先读取状态

不要假设修改成功
      ↓
写入后验证

不要覆盖用户配置
      ↓
先备份

不要只考虑成功路径
      ↓
设计失败回滚

不要依赖外部命令
      ↓
直接调用系统 API

不要无限扩大修改范围
      ↓
只操作必要的系统接口

这些原则同样适用于很多其他系统工具,例如:

  • Windows 服务配置器;
  • 注册表策略管理工具;
  • 系统初始化工具;
  • 企业终端配置工具;
  • 本地安全策略管理器;
  • 系统维护程序。

二十、结语

UpdateLock 是一个体量不大的 Windows 原生工具,但它体现了一种比较典型的系统工具开发思路:少依赖、直接调用系统 API、明确修改边界,并把备份、验证和恢复放在核心位置。

它并没有试图通过删除系统文件或者粗暴禁用所有相关服务来解决 Windows Update 问题,而是针对 Windows 7 与 Windows 10/11 的差异分别设计策略。

尤其是:

原始状态备份
      +
策略修改
      +
回读验证
      +
失败回滚
      +
完整恢复

构成了整个项目比较重要的工程闭环。

对于普通用户来说,它提供的是一个简单的 Windows Update 控制工具;而对于开发者来说,它更值得研究的部分,则是 Win32 注册表操作、服务控制、权限管理、RAII、原子文件写入以及系统配置事务化处理

如果你的目标是学习 Windows 原生 C++,这个项目是一个不错的小型案例:代码规模并不庞大,却覆盖了不少真实 Windows 系统工具会遇到的问题。

项目地址:
https://github.com/hhht110/UpdateLock

注:本文依据 UpdateLock 仓库当前 README 与源码进行整理。Windows 更新策略会随着系统版本、补丁和企业管理策略变化,具体行为应以目标系统实际测试结果为准。

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
评论 抢沙发

    暂无评论内容