¿Hay algún equivalente asíncrono de Process.Start?

Como sugiere el título, ¿hay un equivalente a Process.Start (le permite ejecutar otra aplicación o archivo por lotes) que puedo esperar?

Estoy jugando con una pequeña aplicación de consola y este parece ser el lugar perfecto para usar asincrónica y esperar, pero no puedo encontrar ninguna documentación para este escenario.

Lo que estoy pensando es algo en esta línea:

 void async RunCommand() { var result = await Process.RunAsync("command to run"); } 

Process.Start() solo inicia el proceso, no espera hasta que finaliza, por lo que no tiene mucho sentido hacerlo async . Si aún desea hacerlo, puede hacer algo así como await Task.Run(() => Process.Start(fileName)) .

Pero, si desea esperar asíncronamente para que el proceso finalice, puede usar el evento TaskCompletionSource junto con TaskCompletionSource :

 static Task RunProcessAsync(string fileName) { var tcs = new TaskCompletionSource(); var process = new Process { StartInfo = { FileName = fileName }, EnableRaisingEvents = true }; process.Exited += (sender, args) => { tcs.SetResult(process.ExitCode); process.Dispose(); }; process.Start(); return tcs.Task; } 

Aquí está mi opinión, basada en la respuesta de svick . Agrega una redirección de salida, retención de código de salida y un manejo de error ligeramente mejor (eliminando el objeto de Process incluso si no se pudo iniciar):

 public static async Task 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 RunProcessAsync(Process process) { var tcs = new TaskCompletionSource(); 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; } 

Aquí hay otro enfoque. Concepto similar a svick y las respuestas de Ohad pero utilizando un método de extensión en el tipo de Process .

Método de extensión:

 public static Task RunAsync(this Process process) { var tcs = new TaskCompletionSource(); 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; } 

Ejemplo de caso de uso en un método contenedor:

 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 }