GUI обертка для консольного приложения на C#

Для тех кто не в курсе, GUI (Graphical user interface) — графический интерфейс пользователя. То есть это интерфейс в котором элементы управления представлены в виде изображений, анимации и так далее. Но существуют программы, которые не используют GUI. Обычно их называют «консольные приложения».

Сегодня я расскажу вам как создать GUI  оболочку для консольного приложения. В этом нет ничего сложного, главное знать аргументы командной строки, которые принимает приложение.

Заинтересованных прошу под кат.

И так, для начала напишем маленький класс, со статистической функцией, которая будет запускать наше приложение. Я буду использовать Visual Studio 2010, C# и .NET 4.0. Создадим отдельный файл и в нем напишем каркас класса.

using System;

namespace jLibrary
{
    static class EXEStarter
    {
        static public void Start();
    }
}

В .NET есть классы для запуска *.exe файлов. Что бы их использовать, необходимо подключить пространство имен System.Diagnostics. Допишем в начало файла:

using System.Diagnostics;

Для запуска любого *.exe файла я использовал класс Process. Простой запуск *.exe файла можно сделать так:

        static public void Start(string filename)
        {
            using (Process MyProc = new Process())
            {
                MyProc.StartInfo.FileName = filename;
                MyProc.Start();
            }
        }

Что произойдет во время MyProc.Start()? Наша программа запустит *.exe файл указанный в filename, и продолжит выполнение. Есть и другие варианты, но я использовал такой, так как далее, с помощью свойства StartInfo удобно задавать другие параметры запуска.

А что если нужно, что бы управляющая программа(программа которая запускает) дождалась завершения запускаемой программы? Для этого есть метод у класса Process, который называется WaitForExit. Он не возвращает управление, пока запущенное приложение не завершиться. Его недостаток проявиться, если запущенное приложение зависнет — из-за него зависнет управляющая программа, или поток который ее запустил в случае использования многопоточности. WaitForExit можно использовать после вызова Start, например вот так:

static public void Start(string filename)
{
	using (Process MyProc = new Process())
	{
		MyProc.StartInfo.FileName = filename;
		MyProc.Start();
		MyProc.WaitForExit();
	}
}

Для удобства, необходимо изменить метод Start нашего класса EXEStarter так, что бы программист мог сам задать «ждать завершение процесса или нет». Это совсем не трудно:

static public void Start(string filename, bool waitforexit)
{
	using (Process MyProc = new Process())
	{
		MyProc.StartInfo.FileName = filename;
		MyProc.Start();
		if (waitforexit)
			MyProc.WaitForExit();
	}
}

Как видно из фрагмента кода, если waitforexit равно true, значит метод будет ждать завершения процесса, ну и наоборот. Идем дальше. Для задания параметров консольному приложению, нам нужно использовать аргументы командной строки. У свойства StartInfo класса Process, есть свойство Arguments, которое принимает строку содержащую все аргументы командной строки, для этого приложения. снова допишем наш метод Start.

static public void Start(string filename, string arguments, bool waitforexit)
{
	using (Process MyProc = new Process())
	{
		MyProc.StartInfo.FileName = filename;
		MyProc.StartInfo.Arguments = arguments;
		MyProc.Start();
		if (waitforexit)
			MyProc.WaitForExit();
	}
}

Неплохо было бы еще скрыть окно, которое открывается при запуске приложения, так как оно не несет никакой полезной нагрузки в данном случае. Для этого в StartInfo есть булевое свойство CreateNoWindow. Как вы уже догадались, если присвоить ему true — окно открываться не будет. Добавим перед запуском приложения строчку:

MyProc.StartInfo.CreateNoWindow = true;

Теперь консольное окошко не будет открываться. Можно так же добавить строку:

MyProc.StartInfo.UseShellExecute = false;

Свойство UseShellExecute как вы уже догадались булевое и отвечает за то, будет ли при запуске использоваться оболочка ОС. Если поставить его в true, то можно в FileName задать не только *.exe файл, а и любой другой, или даже ссылку, при этом будет запущенно приложение, которое установлено по умолчанию в ОС для этого типа файлов.

Опять для удобства, можно перегрузить метод Start нашего класса, что бы он мог принимать меньше аргументов. Например вот так:

        static public void Start(string filename, bool waitforexit)
        {
            Start(filename, "", waitforexit);
        }

        static public void Start(string filename)
        {
            Start(filename, "", false);
        }

Работа с аргументами командной строки все еще остается не очень удобной. Я решил написать маленький класс, который стал бы для меня удобным средством создания аргументов командной строки. Из чего состоит аргумент командной строки? Из имени и значения. Обычно это выглядит например так: «-t 60», то есть «-t» имя, а «60» — значения. Много аргументов могут не принимать значения. Начнем написание класса.

    class ComandLineArgument
    {
        public string Name { get; set; }
        public string Value { get; set; }
    }

Но в таком состоянии это не класс, а лишний код. Добавим метод ToString. Кстати, очень часто консольные утилиты в качестве аргументом принимают путь к какому-либо файлу. Если в этом пути есть пробелы, нужно ставить кавычки. Необходимо это учесть.

    class ComandLineArgument
    {
        public string Name { get; set; }
        public string Value { get; set; }
        public override string ToString()
        {
            string result = Name;
            if (Value != null)
                result += " "" + Value + """;
            return result;
        }
    }

Я перегрузил метод ToString, который достался классу от Object. Если Value существует, добавляю кавычки и возвращаю строку. Допишем ему еще 2 конструктора, один принимает только имя, другой имя и значение.

        public ComandLineArgument(string name)
        {
            this.Name = name;
        }
        public ComandLineArgument(string name, string value)
        {
            this.Name = name;
            this.Value = value;
        }

Теперь класс закончен. Но его все равно будет не достаточно, ведь для добавления 10 аргументов в запуск *.exe придется создать 10 объектов этого класса и для каждого вызвать ToString. Напишем еще один класс на основе List из System.Collections.Generic. Вот что у меня получилось:

    class ComandLineArgumentsList : List<ComandLineArgument>
    {
        public override string ToString()
        {
            string result = string.Empty;
            foreach (ComandLineArgument arg in this)
                result += arg.ToString() + ' ';
            return result;
        }
    }

Методы List будут служить для добавления элементов, а перегруженный ToString вернет правильную строку с пробелами.

С классами все. Напишем маленький пример, что бы понять, как это работает. Для примера я использовал архиватор 7zip. Что бы за архивировать файл необходимо выполнить следующую команду:

%7zip Path% a %Output Path% %Input Path%

Где %7zip Path% — путь к 7z.exe, «a» — опция, которая означает добавления файлов в архив, %Output Path% — путь результата, %Input Path% — путь для входного файла. Список всех аргументов командной строки для 7zip вы можете получит, запустив 7zip через консоль. Я создал небольшую форму с помощью WinForms. Простая форма Упаковщика Поместил на нее 1 кнопку. Подключил к проекту свои файлы с классами EXEStarter, ComanLineArgument и ComandLineArgumentsList. После этого написал обработчик нажатия кнопки. Он вызывает сначала диалоги выбора файлов, а потом с помощью только что написанных классов архивирует выбранный файл. Код получился вот такой:

 

using System;
using System.Windows.Forms;
using jLibrary;

namespace _7z_GUI
{
    public partial class formMain : Form
    {
        public formMain()
        {
            InitializeComponent();
        }

        private void btnPack_Click(object sender, EventArgs e)
        {
            string _7zPath; //путь к 7z.exe
            string InPath; //путь к исходному файлу
            string OutPath; //путь к результату

            OpenFileDialog ofd = new OpenFileDialog(); //объект диалога выбора файлов
            ofd.Title = "Выберите 7z.exe"; //заголовок окна диалога
            ofd.ShowDialog(); //показываем диалог
            _7zPath = ofd.FileName; //присваеваем выбранный путь переменной

            //аналогично предыдущему
            ofd.Title = "Выберите исходный файл";
            ofd.ShowDialog();
            InPath = ofd.FileName;

            //аналогично предыдущему, только используется SaveFileDialog
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Filter = "Архив ZIP (*.zip)|*.zip"; //Фильтр файлов
            sfd.ShowDialog();
            OutPath = sfd.FileName;

            //создаем список аргументов
            ComandLineArgumentsList args = new ComandLineArgumentsList();
            args.Add(new ComandLineArgument("a")); //первый аргумент
            args.Add(new ComandLineArgument(""" + OutPath + """)); //путь к исходному файлу
            args.Add(new ComandLineArgument(""" + InPath + """)); //путь к архиву

            EXEStarter.Start(_7zPath, args.ToString(), true); //запускаем 7z.exe
        }
    }
}

 

Вот собственно и все. Задавайте свои ответы. Пишите пожелания, критику и сообщайте о моих орфографических ошибках.

Комментарии 6

  • Ну хорошо это если программа запустилась и отработала, а как быть если я использую что то типа консольного ftp? как тогда?

    • Такого плана приложения еще не делал. Но! Думаю можно перехватить стандартный поток ввода и отсюда уже отталкиваться. Потому что поток вывода и ошибок легко через C# перехватывается.

      Попробуй поискать сам, если не получиться — пиши, я как освобожусь поищу информацию.

  • А как например запустить cmd в TabControl в приложении на C#? Тоесть чтобы cmd был виден, но только в табе.

  • А как можно считать в C# поток данных, возвращаемых консольным приложением? Например, я посылаю команду ipconfig в cmd, как мне перенести данные из cmd в свой GUI?

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

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

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