C#, запуск программы от имени администратора

Или как я сделал недо-sudo для Windows 7

Преамбула

У меня для работы понаписана куча батников для всяких мелких задач: перенастройки сетевых соединений, включения/отключения устройств и т.д. Некоторые команды требуют повышения уровня доступа до администратора, потому батники отказались на семерке работать.

Поскольку работаю я в основном в консоли, да и рабочий стол захламлять не охота, то от создания кучи ярлыков на нужные батники я отказался. Про штатный runas в Windows я знаю, но он меня тоже не устроил. Хотелось, чтоб как в новом Far Manager’е — при необходимости выполнить операцию от имени администратора, выскакивало стандартное окошко виндового UAC, запрашивалось подтверждение и выполнялась нужная команда.
В общем решил я написать быстренько свой недо-sudo на C#, благо действительно получилось быстро и удобно.

Рассуждения и подготовка

Способ, вполне штатный, произвести такое дело с помощью C# есть, но о нем ближе к концу. Пока скажу, что для запуска внешнего процесса нужен будет полный путь к исполняемому файлу, а прописывать его каждый раз очень сильно влом, потому батники пусть лежат в каталоге, доступном в PATH, а нам придется немного «прикинуться операционкой» и вести себя как она. А операционка действует просто. Если просто введена команда без расширения, то она ищет сначала в текущем каталоге, а потом в каталогах из переменной PATH файл с расширением .com, .exe, .bat, .cmd — нашла, значит выполняет.
Так и будем делать.

1. Заводим два массива, со списком расширений и для хранения списка директорий, в которых будем искать файл:

private static string[] FindDirs = null;
private static string[] Extensions = new string[] {"com","exe","bat","cmd"};

2. Функцию для получения списка каталогов из PATH Копия
3. Функцию, которая будет собирать список каталогов, в которых будет производиться поиск. Не буду здесь ее приводить, она просто добавляет текущую директорию (с помощью Directory.GetCurrentDirectory()) и список, полученный на предыдущем шаге в массив. См. в классе FindApp функцию GetFindDirs()
4. Понадобится функция, определяющая по расширению, является ли файл исполняемым. Она тоже довольно проста. См. ссылку выше private static bool IsExecutable(FileInfo fi). Структуру FileInfo сформируем на шаге поиска далее. Она в цикле сравнивает расширение файла, если оно указано, со списком расширений исполняемых файлов.
5. Функцию, которая ищет файл, если расширение не задано private static string FindNoExt(string path, string FileName) Она проверяет, заканчивается ли путь к директории символом \, в цикле подставляет расширения и проверяет наличие файла по указанному пути с помощью File.Exists(). Если файл найден — возвращает полный путь, если нет — null.

Поиск файла

Функция Find (string Command) в классе FindApp

1. Получаем список каталогов, в которых будем искать, с помощью ранее описанной функции GetFindDirs()
2. Создаем класс FileInfo:

FileInfo fi = new FileInfo(Command);

3. Проверяем, был ли задан путь к исполняемому файлу. Тут надо остановиться немного подробнее. Если в команде Command путь задан не был, то свойство DirectoryName класса FileInfo будет содержать текущий каталог, что никак нам не поможет. Поэтому, надо проверить, был ли задан полный путь. Проще всего это сделать, проверив, содержала ли команда Command символ \:
if (Command.Contains("\\"))
...

4. Если каталог указан, то проверяем, указано ли расширение. Тут можно воспользоваться свойством Extension структуры FileInfo. Оно будет пустым, если расширение не задано. Если расширение задано, то проверяем, является ли файл исполняемым, и в случае, если да — возвращаем его полный путь. Если расширение не указано, пытаемся найти в каталоге исполняемый файл с помощью ранее созданной функции FindNoExt.
5. Если каталог не задан в команде, то выполняем шаг 4 для каждого каталога из списка поиска, пока не найдем файл. Если файл так и не будет найден, значит возвращаем null.

Запуск программы

Выполнение программы организовано в отдельном классе RunApp

Проверка админских прав

Сначала мы должны проверить, а нет ли прав администратора у нас уже. Для этого надо будет подключить пространство имен using System.Security.Principal;
Сама проверка вынесена в отдельную функцию:

public static bool IsAdmin()
{
    WindowsPrincipal pricipal = new WindowsPrincipal(
        WindowsIdentity.GetCurrent());

    return pricipal.IsInRole(
        WindowsBuiltInRole.Administrator);
}


Выполнение

Подключаем пространство имен using System.Diagnostics;

public static bool Excecute(string path, string parameters)
{
    ProcessStartInfo psi = new ProcessStartInfo();
    psi.FileName = path;
    psi.Arguments = parameters;            
    bool admin = IsAdmin();

    if (!admin)
    {
        psi.Verb = "runas";
    }

    try
    {
        Process.Start(psi);
    }
    catch (Exception ex)
    {
        if (admin) //админские права уже были, что-то испортилось
        {
            ErrorMessage = ex.Message;
            return false;
        }
       //иначе может быть просто нажали отмену в UAC
    }
    return true;
}

Сначала формируем структуру ProcessStartInfo из пути к программе и параметров. Далее проверяем наличие админских прав, если их нет, то указываем системе, что процесс-программу нужно запустить с правами администратора с помощью psi.Verb = "runas";
Далее запускаем процесс.
Проверки, правда, минимальные. И наверняка, кривые.

Основная программа

Ну тут вообще все элементарно. Это консольное приложение, при запуске которого ему передаются в командной строке параметры. Первый — имя вызываемой команды, остальные — ее параметры. См. здесь

Источники

Статья на Cyberforum

Скачать

Исходники на GitHub
Готовую программу с GitHub

One Response to C#, запуск программы от имени администратора

  1. Pingback: Глюк с текущим рабочим каталогом в wsudo | Персональный блог Толика Панкова

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *