Esiste un equivalente async di Process.Start?

Come suggerisce il titolo, esiste un equivalente a Process.Start (consente di eseguire un’altra applicazione o un file batch) che posso attendere?

Sto giocando con una piccola app per console e questo mi è sembrato il posto perfetto per usare async e attendere ma non trovo documentazione per questo scenario.

Quello che sto pensando è qualcosa in questo senso:

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

Process.Start() avvia solo il processo, non attende fino al termine, quindi non ha molto senso renderlo async . Se vuoi ancora farlo, puoi fare qualcosa come await Task.Run(() => Process.Start(fileName)) .

Tuttavia, se si desidera attendere in modo asincrono il completamento del processo, è ansible utilizzare l’evento TaskCompletionSource insieme a 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; } 

Ecco la mia versione, basata sulla risposta di svick . Aggiunge il reindirizzamento dell’output, la ritenzione del codice di uscita e una gestione degli errori leggermente migliore (eliminando l’object Process anche se non può essere avviato):

 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; } 

Ecco un altro approccio. Concetto simile a svick e alle risposte di Ohad ma usando un metodo di estensione sul tipo Process .

Metodo di estensione:

 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; } 

Esempio di caso d’uso in un metodo di contenimento:

 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 }