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

16

我有一个应用程序(Windows服务),安装在Program Files文件夹中的一个目录下。在这个应用程序旁边还有另一个WinForms应用程序,用于配置服务(以及其他事情)。进行配置时,它会将更改保存到与服务并存的配置文件中。

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

  • 在用于配置的菜单项旁边放置盾牌图标
  • 选择此项目时提示UAC权限
  • 仅在需要时显示图标/提示(例如,如果应用程序安装在不需要UAC权限的位置)

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

我应该如何最好地实现这一点?


我必须经过一些困难的跳跃才能正确地编写代码,但多亏了马修,我得到了与上述描述类似的东西。 - adrianbanks
很高兴听到这个消息。UAC有时会让事情变得很麻烦,但是您的软件确实做得更好。 - user203570
2个回答

23
这很容易。在保存更改到配置文件的按钮上放置一个盾牌图标,而不是菜单项。这遵循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;
}

你应该检测用户当前是否正在以管理员身份运行。如果是,你可能希望采取不同的行为(例如不启动单独的实例,可能会省略盾牌图标)。 - Brian
@Brian,正如他所指出的那样,他不想让整个应用程序以管理员身份运行,因此在这种情况下,没有什么可以检测的。除非您指的是实际的管理员帐户,在这种情况下,即使Windows仍然使用盾牌。 - user203570
@Matthew:是的,我知道 OP 不想让应用程序以管理员身份运行。但这并不意味着用户不会以管理员身份运行它,例如因为用户正在使用 Windows XP,故意使用右键“以管理员身份运行”,或者用户已禁用 UAC。 - Brian
4
啊!C# 里的 Yoda 表达式太让人头疼了!我的眼睛!(除此之外是个好答案) - Trillian
@Trillian,这是一个不可能打破的C/C++习惯。我知道在C#中它们是不必要的。 - user203570
显示剩余3条评论

9

Matthew Ferreira的回答详细介绍了为什么需要重新启动整个应用程序以及何时重新启动它,但他没有涉及如何显示盾牌图标。这里是我使用的一些代码(我认为最初是从这个站点上的另一个答案中获得的),只有在程序未被提升权限时才会显示盾牌图标。

/// <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);
    }
}
在构建时会检查是否处于管理上下文中,如果不是,则在按钮上绘制盾牌图标。
如果您想使用 Windows 使用的盾牌图标,这里有一个巧妙的技巧,它将盾牌图标作为 Bitmap 对象返回。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接