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

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

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

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

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

我怎样才能做到最好?

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

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

 ///  /// Is a button with the UAC shield ///  public partial class ElevatedButton : Button { ///  /// The constructor to create the button with a UAC shield if necessary. ///  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); } } 

如果按钮处于管理上下文中,按钮将检查它何时构建,如果不是,则按钮上的按钮上会显示屏蔽图标。

如果你想要屏幕图标窗口使用, 这里是一个偷偷摸摸的技巧 ,将盾牌图标作为Bitmap对象返回。