- PVSM.RU - https://www.pvsm.ru -

Осмысленное использование консольных приложений в C#

Когда что-то уже написано, оттестировано и достойно справляется со своей работой, то лучше использовать это средство, нежели изобретать велосипед. Например, есть консольная утилита cpctest.exe [1], которая позволяет выполнять все те-же действия что и графическая оболочка, и масса других утилит из стандартного набора Windows. На разработку, отладку и покрытие тестами аналогичной функциональности уйдет драгоценное время. Так зачем его прожигать? Приступим.

Для начала нам нужно залезть на MSDN [2]и посмотреть синтаксис стандартных команд. В результате мы увидим, что запуск любого консольного приложения состоит из имени приложения и его параметров. Приложение инстанцируется системным классом Process [3]. Прототип нашей функции для запуска консольного приложения будет выглядеть следующим образом:

	bool Execute(string commandName, IEnumerable<string> paramsList)

Если мы захотим получать результат выполнения запущенного приложения и обрабатывать его, нам потребуется соответствующий метод:

string GetResult (string commandName, IEnumerable<string> paramsList)

Далее идем снова на MSDN и смотрим ProcessStartInfo Arguments [4], UseShellExecute [5], RedirectStandardOutput [6]и RedirectStandardError [7], если Вы будете строго обрабатывать исключения как в Java. В итоге для инициализации процесса нам потребуется свойство, которое будет определять режим запуска консольного приложения и метод для инициации процесса. Для своего класса я использовал паттерн Facade.

public  class CommandHelpers
    {
        public CommandHelpers()
        {
            Invisible = true;
        }

public bool Invisible { get; set; }

private Process CreateProcess(string commandName, IEnumerable<string> paramsList, bool output = false)
        {
            string paramString = paramsList.Aggregate<string, string>(null,
                (current, param) => current + " " + param);
            return new Process
            {
                StartInfo =
                {
                    FileName = commandName,
                    Arguments = paramString,
                    UseShellExecute = output ? !output : !Invisible, 
                    RedirectStandardOutput = output
                }
            };
        }

Необходимо учесть, что запускаемое приложение может работать бесконечно долго, например, ping –t youwebsite.org. Для его запуска нам потребуется соответствующий метод:

public Task<bool> ExecuteAsync(string commandName, IEnumerable<string> paramsList)

Исходный код:

Пример использования:

public class CspHelpers
    {
        private readonly CommandHelpers _cryptoconsole;
        private readonly string _command = @"c:Program FilesCrypto ProCSPcsptest";

        public CspHelpers()
        {
            _cryptoconsole = new CommandHelpers();
        }
        /// <summary>
        /// Импорт сертификата в контейнер
        /// </summary>
        /// <param name="driveName">Имя сменного носителя</param>
        /// <param name="containerName">Имя контейнера</param>
        /// <param name="type">Тип импортируемого серификата exchange или signature</param>
        /// <param name="certPath">Полный путь до сертификата</param>
        /// <param name="password">Пароль на контейнер</param>
        /// <returns>Сведения о выполнении</returns>
        public string ImportToContainer(string driveName, int containerName, KeyType type, string certPath,
            string password)
        {
            var Params = new List<string>();
            Params.Add("-keyset");
            Params.Add("-container");
            Params.Add(string.Format(@"\.FAT12_{0}{1}", driveName[0], containerName));
            Params.Add("-password");
            Params.Add(password);
            Params.Add("-keytype");
            Params.Add(type.ToString());
            Params.Add("-impcert");
            Params.Add(certPath);
            return _cryptoconsole.GetResult(_command, Params);
        }
    }

CommandHelpers.cs

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Common.Security
{
    /// <summary>
    ///     See for correct use https://technet.microsoft.com/en-us/library/bb491070.aspx
    /// </summary>
    public class CommandHelpers
    {
        public CommandHelpers()
        {
            Invisible = true;
        }

        /// <summary>
        ///     Not show CMD window
        /// </summary>
        public bool Invisible { get; set; }

        /// <summary>
        ///     Execete CMD command
        /// </summary>
        /// <param name="commandName">Command name only</param>
        /// <param name="paramsList">Params and keys for command</param>
        public bool Execute(string commandName, IEnumerable<string> paramsList)
        {
            return CreateProcess(commandName, paramsList).Start();
        }

        /// <summary>
        ///     Async execete CMD command
        /// </summary>
        /// <param name="commandName">Command name only</param>
        /// <param name="paramsList">Params and keys for command</param>
        public Task<bool> ExecuteAsync(string commandName, IEnumerable<string> paramsList)
        {
            return Task<bool>.Factory.StartNew(() => CreateProcess(commandName, paramsList).Start());
        }

        /// <summary>
        ///     Returns result of command execution
        /// </summary>
        /// <param name="commandName">Command name only</param>
        /// <param name="paramsList">Params and keys for command</param>
        /// <returns></returns>
        public string GetResult(string commandName, IEnumerable<string> paramsList)
        {
            var bufer = new StringBuilder();
            using (var proc = CreateProcess(commandName, paramsList, true))
            {
                proc.Start();
                while (!proc.StandardOutput.EndOfStream)
                {
                    bufer.AppendLine(proc.StandardOutput.ReadLine());
                }
            }
            return bufer.ToString();
        }

        /// <summary>
        ///     Returns result of command execution
        ///     Experemental. Not Tested.
        /// </summary>
        /// <param name="commandName">Command name only</param>
        /// <param name="paramsList">Params and keys for command</param>
        /// <returns></returns>
        public string GetResultAsync(string commandName, IEnumerable<string> paramsList)
        {
            var bufer = new StringBuilder();
            using (var proc = CreateProcess(commandName, paramsList, true))
            {
                proc.OutputDataReceived += (sender, e) =>
                {
                    if (!string.IsNullOrEmpty(e.Data))
                    {
                        bufer.AppendLine(e.Data);
                    }
                };
                proc.BeginOutputReadLine();
                proc.EnableRaisingEvents = true;
                proc.WaitForExit();
            }
            return bufer.ToString();
        }


        private Process CreateProcess(string commandName, IEnumerable<string> paramsList, bool output = false)
        {
            var paramString = paramsList.Aggregate<string, string>(null,
                (current, param) => current + " " + param);
            return new Process
            {
                StartInfo =
                {
                    FileName = commandName,
                    Arguments = paramString,
                    UseShellExecute = output ? !output : !Invisible,
                    RedirectStandardOutput = output
                }
            };
        }
    }
}

UPD:

Дополнительные материалы:

Источник вдохновения для статьи:
От пользователя vedmaka [8]: toster.ru/q/7644 [9]

GUI обертка для консольного приложения: ru.jakeroid.com/gui-obertka-dlya-konsolnogo-prilozheniya-na-csharp.html [10]

От пользователя evgenyl [11]: habrahabr.ru/post/136766 [12]

Автор: ColorCast

Источник [13]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/c-2/111087

Ссылки в тексте:

[1] cpctest.exe: http://cmd.readthedocs.org/ru/latest/csptest.html

[2] MSDN : https://technet.microsoft.com/en-us/library/bb491070.aspx

[3] Process: https://msdn.microsoft.com/ru-ru/library/system.diagnostics.process.start(v=vs.110).aspx

[4] Arguments: https://msdn.microsoft.com/ru-ru/library/system.diagnostics.processstartinfo.arguments(v=vs.110).aspx

[5] UseShellExecute: https://msdn.microsoft.com/ru-ru/library/system.diagnostics.processstartinfo.useshellexecute(v=vs.110).aspx

[6] RedirectStandardOutput : https://msdn.microsoft.com/ru-ru/library/system.diagnostics.processstartinfo.redirectstandardoutput(v=vs.110).aspx

[7] RedirectStandardError: https://msdn.microsoft.com/ru-ru/library/system.diagnostics.processstartinfo.redirectstandarderror(v=vs.110).aspx

[8] vedmaka: https://habrahabr.ru/users/vedmaka/

[9] toster.ru/q/7644: https://toster.ru/q/7644

[10] ru.jakeroid.com/gui-obertka-dlya-konsolnogo-prilozheniya-na-csharp.html: http://ru.jakeroid.com/gui-obertka-dlya-konsolnogo-prilozheniya-na-csharp.html

[11] evgenyl: https://habrahabr.ru/users/evgenyl/

[12] habrahabr.ru/post/136766: https://habrahabr.ru/post/136766/

[13] Источник: https://habrahabr.ru/post/276443/