Правильный способ работы с UAC в С#

У меня есть приложение (служба Windows), которое устанавливается в каталог в папке Program Files. Наряду с этим приложением является еще одно приложение WinForms, которое используется для настройки службы (среди прочего). Когда он выполняет настройку, он сохраняет изменения в файле конфигурации, который живет рядом с сервисом.

При работе в Vista/Win7 UAC запрещает пользователю сохранять файл конфигурации. Я хотел бы сделать следующее:

  • установите значок экрана рядом с пунктом меню, используемым для настройки
  • запрашивает разрешения UAC при выборе этого элемента
  • показывает только значок/подсказку, когда на ОС, которая его требует.
  • показывает только значок/приглашение, когда требуются разрешения (например, если приложение установлено где-то, не требующее разрешения UAC)

Я действительно не хочу запускать все приложение в качестве администратора, так как оно также используется для других целей, для которых не требуются разрешения UAC (поэтому установка файла манифеста приложения не является правильным решением). Я также предполагаю (исправьте меня, если я ошибаюсь), что после разрешения UAC мой существующий процесс не сможет выполнить действие и что мне нужно будет начать новый процесс.

Как я могу достичь этого?

Ответы

Ответ 1

Это довольно легко. Поместите значок экрана на кнопку, которая сохраняет изменения в файле конфигурации, а не пункт меню. Это следует за поведением 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), потому что он просто запускается как администратор, если это возможно, или запрашивает учетные данные. Вы можете проверить, требует ли ОС UAC, просто делая Environment.OSVersion.Version.Major == 6. 6 - это как Windows Vista, так и 7. Вы можете убедиться, что используете Windows, глядя на Environment.OSVersion.Platform.

Для определения того, являетесь ли вы уже приложением, вы можете сделать это:

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

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

    return false;
}

Ответ 2

Ответ Мэтью Феррейры рассказывает о том, почему вам нужно перезагрузить все приложение и что делать, когда его перезагружать, однако он не рассказал, как показать значок щита. Вот какой код я использую (я думаю, что я изначально получил его из другого ответа где-то на этом сайте), который будет показывать значок экрана, когда программа не будет поднята

/// <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);
    }
}

Кнопка проверяет, когда она создается, если она находится в административном контексте, и если она не рисует значок экрана на кнопке.

Если вы хотите использовать окна значков экрана, вот трюк трюков, который возвращает значок экрана как объект Bitmap.