用户账户控制(User Account Control,简称UAC)是Windows操作系统中的一项安全功能,它要求用户在执行需要提升权限的操作时进行确认。例如,写入注册表、修改系统受保护文件夹下的文件等。为了支持这种行为,应用程序需要在设计时就考虑到权限提升的需求。
通常,有两种方法可以实现这种行为:
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<assemblyIdentity version="1.0.0.0" name="MyApplication" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
</asmv1:assembly>
通常,需要提升权限的程序部分是一个设置表单,它在写入Windows注册表时,核心功能不需要。另一个案例是一个控制台应用程序,其中核心功能需要提升权限,而一些辅助功能(如显示帮助信息)不需要。
让声明这样一个控制台应用程序的骨架。入口点方法解析输入的命令行参数,并根据解析结果包装成对象来分支流程控制:
static void Main(string[] args)
{
// 将命令行参数包装到一个对象中
Core.Settings = ApplicationSettingsParser.Parse(args);
if (Core.Settings.UsageFlag || (args.Length == 0))
{
// 无需提升权限即可运行
PrintUsage();
}
else if (!Core.Settings.EngageFlag)
{
// 以相同的参数运行,加上标志,说明主要动作执行
var info = new ProcessStartInfo(
Assembly.GetEntryAssembly().Location,
String.Join(" ", Enumerable.Concat(args, new[] { "--engage" }))
)
{
Verb = "runas", // 指示提升权限
};
var process = new Process
{
EnableRaisingEvents = true, // 启用 WaitForExit()
StartInfo = info
};
process.Start();
process.WaitForExit(); // 调用进程线程直到被调用进程退出
}
else if (Core.Settings.EngageFlag)
{
// 在提升的权限下执行操作
Console.WriteLine("Elevated privileges gained={0}", Core.IsElevated);
}
}
以下是一个仅显示示例帮助信息的方法:
private static void PrintUsage()
{
Console.WriteLine("Usage: ElevatedPrivilegesOnDemand.exe --do [--engage] | -?");
}
解析方法被放置在一个专用的辅助类中:
public static ApplicationSettings Parse(IEnumerable args)
{
var settings = new ApplicationSettings();
foreach (var arg in args)
{
switch (arg)
{
case "-?":
{
// 停止进一步解析并返回唯一的标志
return new ApplicationSettings() { UsageFlag = true };
}
case "--do":
{
settings.DoFlag = true;
break;
}
case "--engage":
{
settings.EngageFlag = true;
break;
}
}
}
return settings;
}
它返回一个POCO对象来保持设置:
class ApplicationSettings
{
public ApplicationSettings()
{
// 默认设置设置
}
public bool DoFlag { get; set; }
public bool EngageFlag { get; set; }
public bool UsageFlag { get; set; }
}
内部单例设置实例附加到一个枢轴类。它还包含一个额外的辅助方法来检查获得提升的权限:
static class Core
{
public static bool IsElevated
{
get
{
return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
}
}
public static ApplicationSettings Settings { get; set; }
}
执行流程被分为两部分:第一部分是确定是否需要提升权限,这取决于是否指定了命令行参数。如果需要,将添加一个额外的参数并传递给同一个应用程序,但以管理员权限启动并重定向控制台输出。第二部分,核心功能在获得完全权限后执行。
现在,当不需要时,没有必要始终以提升的权限模式运行整个应用程序。