c# - ¿Hay algún equivalente asíncrono de Process.Start?
async-await c#-5.0 (3)
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");
}
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<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;
}
Aquí hay otro enfoque. Concepto similar a svick y Ohad''s respuestas Ohad''s 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<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;
}
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
}
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)
{
// there is no non-generic TaskCompletionSource
var tcs = new TaskCompletionSource<bool>();
var process = new Process
{
StartInfo = { FileName = fileName },
EnableRaisingEvents = true
};
process.Exited += (sender, args) =>
{
tcs.SetResult(true);
process.Dispose();
};
process.Start();
return tcs.Task;
}