从Windows Vista开始,为了防止操作系统被恶意软件破坏,Windows加入了UAC机制,在没有关闭UAC的时候,用户的程序都没有管理员权限去执行。但是如果软件需要做一些操作,比如读写注册表,就需要以管理员身份启动。然而普通用户并不清楚这一点,这有可能导致我们的程序被差评。
幸好,用.NET开发的程序可以很方便的做到自动要求以管理员身份运行。
首先为了达到演示效果,我需要在程序界面上通知当前环境是不是以管理员身份运行,为此我找了一个UACHelper,这个东西挺实用的,就算不是以管理员身份运行的,也不会在用户面前直接爆掉。
public static class UacHelper
{
private const string uacRegistryKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
private const string uacRegistryValue = "EnableLUA";
private static uint STANDARD_RIGHTS_READ = 0x00020000;
private static uint TOKEN_QUERY = 0x0008;
private static uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, uint TokenInformationLength, out uint ReturnLength);
public enum TOKEN_INFORMATION_CLASS
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
TokenElevationType,
TokenLinkedToken,
TokenElevation,
TokenHasRestrictions,
TokenAccessInformation,
TokenVirtualizationAllowed,
TokenVirtualizationEnabled,
TokenIntegrityLevel,
TokenUIAccess,
TokenMandatoryPolicy,
TokenLogonSid,
MaxTokenInfoClass
}
public enum TOKEN_ELEVATION_TYPE
{
TokenElevationTypeDefault = 1,
TokenElevationTypeFull,
TokenElevationTypeLimited
}
public static bool IsUacEnabled
{
get
{
var uacKey = Registry.LocalMachine.OpenSubKey(uacRegistryKey, false);
var result = uacKey.GetValue(uacRegistryValue).Equals(1);
return result;
}
}
public static bool IsProcessElevated
{
get
{
if (IsUacEnabled)
{
IntPtr tokenHandle;
if (!OpenProcessToken(Process.GetCurrentProcess().Handle, TOKEN_READ, out tokenHandle))
{
throw new ApplicationException("Could not get process token. Win32 Error Code: " + Marshal.GetLastWin32Error());
}
var elevationResult = TOKEN_ELEVATION_TYPE.TokenElevationTypeDefault;
var elevationResultSize = Marshal.SizeOf((int)elevationResult);
uint returnedSize = 0;
var elevationTypePtr = Marshal.AllocHGlobal(elevationResultSize);
var success = GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenElevationType, elevationTypePtr, (uint)elevationResultSize, out returnedSize);
if (success)
{
elevationResult = (TOKEN_ELEVATION_TYPE)Marshal.ReadInt32(elevationTypePtr);
var isProcessAdmin = elevationResult == TOKEN_ELEVATION_TYPE.TokenElevationTypeFull;
return isProcessAdmin;
}
throw new ApplicationException("Unable to determine the current elevation.");
}
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
var result = principal.IsInRole(WindowsBuiltInRole.Administrator);
return result;
}
}
}
于是,在窗体加载的时候就能知道是不是以管理员身份启动了:
private void Form1_Load(object sender, EventArgs e)
{
label1.Text = UacHelper.IsProcessElevated ? "Running as administrator" : "Not running as administrator";
}
不要试图用Visual Studio的debug模式去run,因为debug是肯定在管理员身份下的,测不出来结果。可以用Ctrl+F5或者直接build以后去debug或release目录下执行。默认情况是这样的(前提是你的系统没有关闭UAC)

右击以管理员身份运行

才能达到我们的想要的效果

其实我们通过一个简单的配置让Windows知道我们的程序要求管理员权限,而无需用户每次去显式的以管理员身份运行。
在项目里加入一个Application Manifest File文件。

然后打开这个文件,找到“requestedExecutionLevel”节点,将level属性的值改成“requireAdministrator”。
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />

重新编译应用程序,现在你应该能看到程序图标右下角多了一个UAC的盾牌标记,并且每次启动都会自动要求获得管理员权限。
