从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的盾牌标记,并且每次启动都会自动要求获得管理员权限。