UAC 权限机制

何为 UAC

用户帐户控制(User Account Control,简写作 UAC) 是微软公司在其 Windows Vista 及更高版本操作系统中采用的一种控制机制。其原理是通知用户是否对应用程序使用硬盘驱动器和系统文件授权,以达到帮助阻止恶意程序(有时也称为 “恶意软件”)损坏系统的效果。

UAC 的表现

UAC的表现
相信大多数人都见过这个窗口,也就是当一个新的Win10系统在一开始安装软件时总能见到的一个窗口。

它表示即将运行的程序在向当前 Windows 登录账户申请管理员权限,一旦拥有管理员权限,程序就可以做一些更深入的操作,往往涉及到磁盘的读写、注册表的访问、剪贴板的使用等等


WPF 下对 UAC 不兼容的地方

Drop 事件

不兼容表现

当 A 程序和 B 程序的权限层级不在同一层时,是无法直接从其中一个程序拖动项目到另一个程序的
因为 Drop 事件是通过处理 Window 系统的消息流抓取拖动消息来处理拖动事件的,而(打个比方)不同权限层级的消息流不是同一个流,自然 A 程序就捕获不到 B 程序中的拖动消息

临床表现 : 无法从桌面拖动文件到以管理员权限运行的 WPF 程序中(桌面是由资源管理器(explorer.exe)提供的),因为资源管理器始终以用户权限(User Access)运行

传统解决办法

让用户界面和后台脚本权限分离,以用户权限运行界面,后台脚本则以管理员权限运行
如何实现?百度上有很多教程,可以自行搜索

系统访问

不兼容表现

非管理员权限运行的程序不能:

  1. 访问注册表 (这意味着你不能使用自定义文件格式、不能开机自启动、不能添加软件安装信息…)
  2. 向C盘写入文件 (这意味着你不能创建开始菜单捷径、不能在AppData文件夹中写入数据、不能固定到任务栏…)
    综上所述,如果你的程序没有管理员权限,那基本就是废的了

传统解决办法

强制使用管理员权限启动;或者在运行过程中通过 WinAPI 请求用户给予管理员权限


新思路

想法由来

一次偶然的偶然,我在使用 System.Diagnostics.Process.Start() 方法启动一个外部程序时,惊奇地发现,即使那个外部程序要求使用管理员权限启动,且我的程序是在用户权限下运行,任然可以启动那个外部程序(登录账户是管理员账户)
于是,一个大胆的念头在我脑海中诞生了:
为什么不把所有需要管理员权限的代码写在一个外部程序中呢

实现

  1. 新建一个C#控制台程序,在项目上右键–>属性–>应用程序–>输出类型–>选择 Windows 应用程序 (这一步保证调用这个程序时不会弹出命令行的窗口)
  2. 将需要管理员权限才能执行的代码写在这个项目中,然后在主程序中添加此项目的引用 (这样这个项目就会生成在主程序目录下)
  3. 接着在主程序需要执行那串代码的地方写:

    1
    System.Diagnostics.Process.Start($"{Environment.CurrentDirectory}\\引用项目名称.exe", "参数")
  4. 在引用项目中添加参数处理 例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    static void Main(string[] args)
    {
    string filePath = "";
    if ((args != null) && (args.Length > 0))
    {
    for (int i = 0; i < args.Length; i++)
    {
    // 对于路径中间带空格的会自动分割成多个参数传入
    filePath += " " + args[i];
    }

    filePath.Trim(); //去掉首尾的空格
    if (filePath.Contains("-r")) //如果参数中包含 -r 则执行以下代码
    {
    System.Threading.Thread.Sleep(5 * 1000);
    System.Diagnostics.Process.Start($"{Environment.CurrentDirectory}\\abc.exe");
    }
    }
    }
  5. 在项目上右键–>属性–>安全性–>勾选后又取消勾选 启用ClickOnce安全设置
  6. 项目中 Properties 下新增了一个 app.manifest 文件,双击打开
  7. 找到如下代码段:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
    <!-- UAC 清单选项
    如果想要更改 Windows 用户帐户控制级别,请使用
    以下节点之一替换 requestedExecutionLevel 节点。n
    <requestedExecutionLevel level="asInvoker" uiAccess="false" />
    <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
    <requestedExecutionLevel level="highestAvailable" uiAccess="false" />

    指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
    如果你的应用程序需要此虚拟化来实现向后兼容性,则删除此
    元素。
    -->
    <requestedExecutionLevel level="asInvoker" uiAccess="false" />
    </requestedPrivileges>
  8. requestedExecutionLevel 节点的 level 属性修改为 highestAvailable
  9. 大功告成
  10. 此时,你可以发现,这个外部程序的图标在资源管理器中已经带上了一个小盾牌的标志