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