通过编程方式获取 UAC 用户账户控制通知级别以及 UAC 的介绍

本文最后更新于:9 天前

前言

提到 UAC,想必大家都见到过这种弹窗吧?

下图是 UAC 的设置界面

UAC 设置是我们在程序开发中启动的外部进程能否正常提权 (获取管理员权限) 的关键。因此,我们有必要事先获取当前系统的 UAC 通知级别来决定是否启动外部进程或者是提示用户进行相关操作从而避免因提权失败导致的异常现象。这期就讲讲如何在 C# 中获取系统 UAC 通知级别,可以适用于 Windows 7 ~ 11 (其他系统请自行测试)。

UAC 通知级别简介

首先我们得先清楚 UAC 4个通知级别的含义。UAC 设置可以通过开始菜单搜索 (Win+S) uac 打开。如果需要更改 UAC 设置,请使用以下类型的账户登录 (因为非管理员账户的 UAC 对话框与管理员的 不一样,无法真正意义上的实现更改):

  • 内置的 Administrator
  • 自带管理员权限的其他账户

有关提升账户权限,可以看看我的这篇文章。

安全桌面的意义

在正式介绍各通知级别之前,还有一个概念就是 “降低桌面亮度” (Dimming),这其实就是决定是否在 “安全桌面” (Secure Desktop) 中弹出 UAC 弹窗,也就是决定是否出现那个黑色的背景。它的作用是防止万一有恶意程序锁定 UAC 弹窗并模拟用户点击 是 按钮来达到自我获取权限的目的。故也建议启用安全桌面 (Level 2 即以上)。如图:

降低桌面亮度,有安全桌面:

不降低桌面亮度,没有安全桌面:

Level 1 - 始终通知: 出现以下情况时始终通知我

如图,若设置到这个级别就会:打开个别软件时 (图标右下角有小盾牌的那种) 和 在系统设置里面更改设置时 就会 在安全桌面中 弹出 UAC 弹窗。

Level 2 - 仅当应用尝试更改我的计算机时通知我(默认)

这个是系统默认的,它只会在仅当你 打开个别软件时 (图标右下角有小盾牌的那种) 才会 在安全桌面中 弹出 UAC 弹窗。

Level 3 - 仅当应用尝试更改计算机时通知我(不降低桌面亮度)

这个和 Level 2 相同,唯一区别就是仍会在仅当你 打开个别软件时 (图标右下角有小盾牌的那种) 直接弹出 UAC 弹窗,而 不是在安全桌面中 弹出。

Level 4 - 从不通知: 出现以下情况时始终不要通知我

相当于直接 禁用了 UAC,且 不再弹出 UAC 弹窗

C# 实现

主要就是是去读取注册表 HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System 下的 EnableLUAConsentPromptBehaviorAdminPromptOnSecureDesktop 的值综合判断设置的是哪个级别,如下表:

通知级别 EnableLUA ConsentPromptBehaviorAdmin PromptOnSecureDesktop
1 1 2 1
2 1 5 1
3 1 5 0
4 1 或 0 0 0
已禁用 0 * *
未知 - - -

这样我们就可以用 C# 代码来进行判断了,首先我们创建一个枚举 (依次对应 Level 1~4),方面我们标识 UAC 通知级别:

1
2
3
4
5
6
7
8
9
public enum UACNotifyLevel
{
AllDimming,
AppsOnlyDimming,
AppsOnlyNoDimming,
Never,
Disabled, // 表示已禁用 UAC
Unknown // 作为不属于上述所有情况的缓冲,防止报错
}

然后建立一个辅助类专门用于获取 UAC 通知等级:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static class UACHelper
{
public static bool IsUACDisabled { get; }
public static UACNotifyLevel Level { get; }

static UACHelper()
// 显式使用静态构造函数是为了保证那两个属性具有一先一后的赋值顺序
{
Level = GetUACNotifyLevel();
IsUACDisabled = Level >= UACNotifyLevel.Never;
// 只要 UAC 设置在 Level 3 及以上就可以保证正常提权了,故 UAC 是否禁用就可以这样判断
}

public static UACNotifyLevel GetUACNotifyLevel()
{
using var reg = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System", false); // false 代表以只读方式打开注册表,因为这些键只能在 HKLM 中找到,且在 HKLM 写入需要管理员权限。但我们只需要读取就行,故为只读模式,确保在无管理员权限下也可以正常查询

return ((int)reg.GetValue("EnableLUA", 0), (int)reg.GetValue("ConsentPromptBehaviorAdmin", 0), (int)reg.GetValue("PromptOnSecureDesktop", 0)) switch
{
(1, 2, 1) => UACNotifyLevel.AllDimming,
(1, 5, 1) => UACNotifyLevel.AppsOnlyDimming,
(1, 5, 0) => UACNotifyLevel.AppsOnlyNoDimming,
(1 or 0, 0, 0) => UACNotifyLevel.Never,
(0, _, _) => UACNotifyLevel.Disabled,
_ => UACNotifyLevel.Unknown
};
}
}

接着在合适的位置调用 UACHelper,这里仅供测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
internal static class Program
{
private static void Main()
{
var state = UACHelper.IsUACDisabled;
var level = UACHelper.Level;

MessageBox.Show(
$"""
UAC 通知等级检测结果:

是否禁用 UAC:{(state ? "" : "")}
当前 UAC 通知级别:{level} (Level {(int)(level + 1)})
提权状态:{(state ? "无法提权" : "可以正常提权")}
""");
}
}

最后来看看效果:

好了,到此结束,感谢阅读。

温馨提示

不要轻易禁用 UAC:因为以前我看过有些 Windows 系统优化的教程会教用户禁用 UAC,原因也仅仅只是为了不让那个 “烦人” 的对话框在你每次运行程序的时候就弹出。这样虽然可以阻止对话框弹出,但是同时也阻止了所有用户程序 “以管理员身份运行”,这对必须有管理员权限才能运行程序的场景十分不友好。因为一旦禁用 UAC,无论是用户,还是程序,都不能通过常规方式获取管理员权限。正确的做法应该是将 UAC 设置为 Level 2,即系统默认值,若要关闭 UAC 弹窗,需要当前账户自带管理员权限 (获取方法)。

相关链接


通过编程方式获取 UAC 用户账户控制通知级别以及 UAC 的介绍
https://wanghaonie.github.io/posts/17dc52648ff5/
作者
WangHaonie
发布于
2025-05-06 07:12:33
更新于
2025-05-06 07:30:40
许可协议