пятница, 16 мая 2014 г.

Вызов внешней программы из приложения C#

В практике программирования бывает возникает необходимость вызова из приложения некоторой внешней программы возможно с передачей ей списка аргументов. В C# эта задача решается средствами System.Diagnostics.Process. Приведу небольшой пример, показывающий имеющиеся в распоряжении программиста возможности.

В одном из C# проектов мне требовалось перемещать каталог со всем его содержимым из одного места в другое. В рамках одного тома с этой задачей успешно справлялся статический класс System.IO.Directory и его метод Move(). Однако перемещать между разными томами он отказывался, генерируя исключение. Я вспомнил, что в Windows есть утилита командной строки с до боли знакомым названием XCOPY, которая умеет копировать как файлы так и целые каталоги и имеет приличный набор аргументов управляющих разными аспектами ее поведения. При перемещении каталога из одного места в другое между разными томами надо сначала источник скопировать, и только затем приступать к его удалению. Первый этап реализуется совсем просто. Сначала создаем новый процесс
var proc = new System.Diagnostics.Process();
 Далее, задаем имя программы которую необходимо запустить на исполнение
proc.StartInfo.FileName = "XCOPY";
и указываем аргументы если таковые имеются
proc.StartInfo.Arguments = '"' + pathSrc + '"' + " " + '"' + pathDest + '"' + " /e /c /Y /I";
В  данном случае параметр pathSrc - это каталог источник, pathDest - каталог приемник, и каждый из них обрамлен кавычками. Далее идут аргументы, управляющие поведением XCOPY, а именно - копировать каталог с подкаталогами включая пустые, продолжать копировать вне зависимости от наличия ошибок, подавлять запрос подтверждения на перезапись существующего целевого файла. Последний аргумент предписывает трактовать целевой объект как каталог и позволяет подавить неудобный запрос утилиты перед началом процесса копирования, если целевой объект не существует.

Определившись с аргументами, можно дополнительно избавиться от окна командного процессора, установив стиль окна
proc.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
Запуск процесса осуществляет метод
proc.Start();
который выполняется в неблокирующем режиме. Этот вызов однако может доставить определенную проблему, если вызывающему коду надо реагировать на момент завершения процесса. Поймать отпущенный на свободу процесс за хвост можно через функцию обратного вызова, которую задают перед вызовом Start()
proc.EnableRaisingEvents = true;
proc.Exited += new EventHandler(XMovePathExited);
Сигнатура функции соответствует стандартному обработчику void EventHandler(object sender, EventArgs e).

Запустить новый процесс можно и в блокирующем режиме, для этого достаточно после Start() вызвать proc.WaitForExit().