Есть ли асинхронный эквивалент Process.Start?

Как следует из названия, есть ли эквивалентProcess.Start (позволяет запустить другое приложение или командный файл), что я могу ждать?

Я играю с небольшим консольным приложением, и мне показалось, что это идеальное место для использования async и await, но я не могу найти какую-либо документацию для этого сценария.

Я думаю, что-то вроде этого:

void async RunCommand()
{
    var result = await Process.RunAsync("command to run");
}
 linkerro29 мая 2012 г., 08:21
Хорошо, я обновил свой пост, чтобы сделать его более понятным. Объяснение, почему я хочу это, просто. Представьте себе сценарий, в котором вам нужно выполнить внешнюю команду (что-то вроде 7zip), а затем продолжить выполнение приложения. Это как раз то, что async / await должно было облегчить, и, тем не менее, похоже, нет способа запустить процесс и ожидать его завершения.
 aquinas28 мая 2012 г., 20:40
ОП говорит о новых асинхронных / ожидающих ключевых слов в C # 5
 SimpleVar28 мая 2012 г., 20:30
Почему вы просто не используете WaitForExit для возвращенного объекта Process?
 Oded28 мая 2012 г., 20:32
@YoryeNathan - лол. В самом деле,Process.Start is async и OP, кажется, хочет синхронную версию.
 SimpleVar28 мая 2012 г., 20:31
И, кстати, звучит больше похоже на то, что вы ищете "синхронизированный" решение, а не «асинхронный» решение, поэтому название вводит в заблуждение.

Ответы на вопрос(4)

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

public static class ProcessExtensions
{
    public static Task WaitForExitAsync(this Process process)
    {
        var tcs = new TaskCompletionSource<object>();
        process.EnableRaisingEvents = true;
        process.Exited += (s, e) => tcs.TrySetResult(null);
        return process.HasExited ? Task.CompletedTask : tcs.Task;
    }        
}

Затем используйте это так:

public static async Task<int> ExecAsync(string command, string args)
{
    ProcessStartInfo psi = new ProcessStartInfo();
    psi.FileName = command;
    psi.Arguments = args;

    using (Process proc = Process.Start(psi))
    {
        await proc.WaitForExitAsync();
        return proc.ExitCode;
    }
}

Вот мое мнение, основанное наответ svick'а, Это добавляет перенаправление вывода, сохранение кода выхода и немного лучшую обработку ошибок (Process объект, даже если он не может быть запущен):

public static async Task<int> RunProcessAsync(string fileName, string args)
{
    using (var process = new Process
    {
        StartInfo =
        {
            FileName = fileName, Arguments = args,
            UseShellExecute = false, CreateNoWindow = true,
            RedirectStandardOutput = true, RedirectStandardError = true
        },
        EnableRaisingEvents = true
    })
    {
        return await RunProcessAsync(process).ConfigureAwait(false);
    }
}    
private static Task<int> RunProcessAsync(Process process)
{
    var tcs = new TaskCompletionSource<int>();

    process.Exited += (s, ea) => tcs.SetResult(process.ExitCode);
    process.OutputDataReceived += (s, ea) => Console.WriteLine(ea.Data);
    process.ErrorDataReceived += (s, ea) => Console.WriteLine("ERR: " + ea.Data);

    bool started = process.Start();
    if (!started)
    {
        //you may allow for the process to be re-used (started = false) 
        //but I'm not sure about the guarantees of the Exited event in such a case
        throw new InvalidOperationException("Could not start process: " + process);
    }

    process.BeginOutputReadLine();
    process.BeginErrorReadLine();

    return tcs.Task;
}
 04 июл. 2018 г., 08:44
Вы назначаете обработчики событий для некоторыхprocess События (Exited, OutputDataReceived, ErrorDataReceived). Обработчики не удаляются из событий, поэтому в этом коде есть утечка памяти. Пожалуйста, поправьте меня, если я ошибаюсь.
 06 июл. 2016 г., 10:12
@marrrschine Я не совсем понимаю, что вы имеете в виду, возможно, вам следует начать новый вопрос с некоторого кода, чтобы мы могли увидеть, что вы пытались, и продолжить с этого момента.
 05 июл. 2016 г., 16:30
только что нашел это интересное решение. Поскольку я новичок в c #, я не уверен, как использоватьasync Task<int> RunProcessAsync(string fileName, string args), Я адаптировал этот пример и передал три объекта один за другим. Как я могу ждать повышения событий? например. прежде чем мое приложение останавливается .. большое спасибо
 21 сент. 2016 г., 18:34
Фантастический ответ. Спасибо, Свик, за то, что заложили основу, и спасибо Огаду за это очень полезное расширение.
 05 июл. 2018 г., 20:18
@SuperJMN читает код (referencesource.microsoft.com/#System/services/monitoring/…Я не верюDispose обнуляет обработчик события, так что теоретически, если вы вызвалиDispose но сохранил ссылку вокруг, я считаю, что это будет утечка. Тем не менее, когда больше нет ссылок наProcess объект и он (мусор) собирается, нет никого, кто указывает на список обработчиков событий. Таким образом, он собирается, и теперь нет никаких ссылок на делегатов, которые раньше были в списке, поэтому, в конце концов, они получают мусор.

Вот другой подход. Подобная концепцияsvick а такжеОгад & APOS; s ответы, но с использованием метода расширения наProcess тип.

Метод расширения:

public static Task RunAsync(this Process process)
{
    var tcs = new TaskCompletionSource<object>();
    process.EnableRaisingEvents = true;
    process.Exited += (s, e) => tcs.TrySetResult(null);
    // not sure on best way to handle false being returned
    if (!process.Start()) tcs.SetException(new Exception("Failed to start process."));
    return tcs.Task;
}

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

public async Task ExecuteAsync(string executablePath)
{
    using (var process = new Process())
    {
        // configure process
        process.StartInfo.FileName = executablePath;
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.CreateNoWindow = true;
        // run process asynchronously
        await process.RunAsync();
        // do stuff with results
        Console.WriteLine($"Process finished running at {process.ExitTime} with exit code {process.ExitCode}");
    };// dispose process
}
Решение Вопроса

Process.Start() только запускает процесс, он не ждет его завершения, поэтому не имеет особого смысла делать егоasync, Если вы все еще хотите сделать это, вы можете сделать что-то вродеawait Task.Run(() => Process.Start(fileName)).

Но, если вы хотите асинхронно ждать завершения процесса, вы можете использоватьExited event вместе сTaskCompletionSource:

static Task<int> RunProcessAsync(string fileName)
{
    var tcs = new TaskCompletionSource<int>();

    var process = new Process
    {
        StartInfo = { FileName = fileName },
        EnableRaisingEvents = true
    };

    process.Exited += (sender, args) =>
    {
        tcs.SetResult(process.ExitCode);
        process.Dispose();
    };

    process.Start();

    return tcs.Task;
}
 21 янв. 2018 г., 06:03
@svick В форме окна,process.SynchronizingObject должен быть установлен на компонент формы, чтобы избежать методов, которые обрабатывают события (такие как Exited, OutputDataReceived, ErrorDataReceived), вызываемые в отдельном потоке.
 20 мая 2016 г., 00:36
Если метод & quot; RunProcessAsync & quot; также был другой вызов (скажем, для метода File, записывающего файл журнала, который использует некоторый вывод процесса), это нормально, чтобы включить его в этот метод?
 30 авг. 2014 г., 01:47
Эта функциональность также доступна вMedallionShell Пакет NuGet
 01 окт. 2014 г., 08:15
Действительно важно: порядок, в котором вы устанавливаете различные свойстваprocess а такжеprocess.StartInfo меняет то, что происходит, когда вы запускаете его с.Start(), Если вы, например, позвоните.EnableRaisingEvents = true перед установкойStartInfo свойства, как видно здесь, все работает, как ожидалось. Если вы установите его позже, например, чтобы сохранить его вместе с.Exitedдаже если вы называете это раньше.Start(), он не работает должным образом -.Exited срабатывает немедленно, а не в ожидании выхода из процесса. Не знаю почему, просто слово предостережения.
 03 дек. 2012 г., 06:54
Я наконец-то нашел способ закрепить что-то на github для этого - у него нет поддержки отмены / тайм-аута, но он, по крайней мере, соберет для вас стандартный вывод и стандартную ошибку.github.com/jamesmanning/RunProcessAsTask

Ваш ответ на вопрос