在C#中处理UAC的正确方法

时间:2010-10-13 14:59:52

标签: c# .net uac

我有一个应用程序(Windows服务),它安装在Program Files文件夹的目录中。除此应用程序外,还有另一个WinForms应用程序,用于配置服务(以及其他内容)。在进行配置时,它会将更改保存到与服务一起存在的配置文件中。

在Vista / Win7上运行时,UAC会阻止用户保存到配置文件。我想做的是:

  • 将屏蔽图标放在用于配置
  • 的菜单项旁边
  • 在选择此项目时提示UAC权限
  • 仅在需要它的操作系统上显示图标/提示
  • 仅在需要权限时显示图标/提示(例如,如果应用程序安装在不需要UAC权限的地方)

我真的不想以管理员身份运行整个应用程序,因为它还用于不需要UAC权限的其他目的(因此设置应用程序清单文件不是正确的解决方案)。我也假设(如果我错了,请纠正我),一旦授予UAC权限,我的现有流程就无法执行操作,我需要启动一个新流程。

我怎样才能最好地实现这一目标?

2 个答案:

答案 0 :(得分:21)

这很容易。在按钮上放置一个屏蔽图标,用于保存对配置文件的更改,而不是菜单项。这遵循Windows在最后一刻之前不请求UAC权限的行为。该按钮实际上将以管理员身份再次启动您的可执行文件,并使用特殊命令行(您决定)执行配置文件保存。如果您不想使用命令行进行数据传递,请使用命名管道(确保为其提供正确的权限)将配置数据传递给第二个实例。

启动可执行文件:

ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "YOUR EXE";
info.UseShellExecute = true;
info.Verb = "runas"; // Provides Run as Administrator
info.Arguments = "YOUR SPECIAL COMMAND LINE";

if (Process.Start(info) != null)
{ 
    // The user accepted the UAC prompt.
}

当UAC不存在时(Windows XP)也可以使用,因为如果可能,它只会以管理员身份运行,或者提示输入凭据。您只需执行Environment.OSVersion.Version.Major == 6即可检查操作系统是否需要UAC。 6是Windows Vista和7.您可以通过查看Environment.OSVersion.Platform来确保使用Windows。

要检测您的应用程序是否已经是管理员,您可以执行以下操作:

public static bool IsAdministrator()
{
    WindowsIdentity identity = WindowsIdentity.GetCurrent();

    if (identity != null)
    {
        WindowsPrincipal principal = new WindowsPrincipal(identity);
        return principal.IsInRole(WindowsBuiltInRole.Administrator);
    }

    return false;
}

答案 1 :(得分:9)

Matthew Ferreira's answer详细介绍了为什么需要重新启动整个应用程序以及重启时应该怎么做,但是他没有介绍如何显示盾牌图标。这是我使用的一些代码(我想我最初是从本网站上的某个地方得到的)只会在程序未提升时显示盾牌图标

/// <summary>
/// Is a button with the UAC shield
/// </summary>
public partial class ElevatedButton : Button
{
    /// <summary>
    /// The constructor to create the button with a UAC shield if necessary.
    /// </summary>
    public ElevatedButton()
    {
        FlatStyle = FlatStyle.System;
        if (!IsElevated()) ShowShield();
    }


    [DllImport("user32.dll")]
    private static extern IntPtr
        SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    private uint BCM_SETSHIELD = 0x0000160C;

    private bool IsElevated()
    {
        WindowsIdentity identity = WindowsIdentity.GetCurrent();
        WindowsPrincipal principal = new WindowsPrincipal(identity);
        return principal.IsInRole(WindowsBuiltInRole.Administrator);
    }


    private void ShowShield()
    {
        IntPtr wParam = new IntPtr(0);
        IntPtr lParam = new IntPtr(1);
        SendMessage(new HandleRef(this, Handle), BCM_SETSHIELD, wParam, lParam);
    }
}

如果按钮处于管理上下文中,按钮会检查它何时构建,如果不是,它会在按钮上绘制屏蔽图标。

如果您希望屏蔽图标窗口使用,here is a sneaky trick将屏蔽图标作为Bitmap对象返回。