172 votes

Existe-t-il un équivalent asynchrone de Process.Start ?

Comme le titre le suggère, existe-t-il un équivalent à Process.Start (permet d'exécuter une autre application ou un fichier batch) que je peux attendre ?

Je joue avec une petite application console et cela semblait être l'endroit idéal pour utiliser async et await mais je ne trouve aucune documentation pour ce scénario.

Ce que je pense, c'est quelque chose comme ça :

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

2 votes

Pourquoi n'utilisez-vous pas simplement WaitForExit sur l'objet Process retourné ?

3 votes

D'ailleurs, il semble que vous recherchiez une solution "synchronisée" plutôt qu'une solution "asynchrone". Le titre est donc trompeur.

2 votes

@YoryeNathan - lol. En effet, Process.Start es asynchrone et le PO semble vouloir une version synchrone.

3voto

Konstantin S. Points 143

Je pense que tout ce que vous devriez utiliser est ceci :

using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace Extensions
{
    public static class ProcessExtensions
    {
        public static async Task<int> WaitForExitAsync(this Process process, CancellationToken cancellationToken = default)
        {
            process = process ?? throw new ArgumentNullException(nameof(process));
            process.EnableRaisingEvents = true;

            var completionSource = new TaskCompletionSource<int>();

            process.Exited += (sender, args) =>
            {
                completionSource.TrySetResult(process.ExitCode);
            };
            if (process.HasExited)
            {
                return process.ExitCode;
            }

            using var registration = cancellationToken.Register(
                () => completionSource.TrySetCanceled(cancellationToken));

            return await completionSource.Task.ConfigureAwait(false);
        }
    }
}

Exemple d'utilisation :

public static async Task<int> StartProcessAsync(ProcessStartInfo info, CancellationToken cancellationToken = default)
{
    path = path ?? throw new ArgumentNullException(nameof(path));
    if (!File.Exists(path))
    {
        throw new ArgumentException(@"File is not exists", nameof(path));
    }

    using var process = Process.Start(info);
    if (process == null)
    {
        throw new InvalidOperationException("Process is null");
    }

    try
    {
        return await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false);
    }
    catch (OperationCanceledException)
    {
        process.Kill();

        throw;
    }
}

1 votes

Quel est l'intérêt d'accepter un CancellationToken si l'annuler ne fait pas Kill le processus ?

1 votes

CancellationToken dans le WaitForExitAsync est nécessaire simplement pour pouvoir annuler une attente ou définir un délai d'attente. La mort d'un processus peut être effectuée dans StartProcessAsync : ``` try { await process.WaitForExitAsync(cancellationToken) ; } catch(OperationCanceledException) { process.Kill() ; } ```

1 votes

Mon opinion est que lorsqu'une méthode accepte un CancellationToken l'annulation du jeton doit aboutir à l'annulation de l'opération, et non à l'annulation de l'attente. C'est ce que l'appelant de la méthode attend normalement. Si l'appelant souhaite annuler uniquement l'attente, et laisser l'opération se poursuivre en arrière-plan, il est assez facile de le faire en externe ( ici est une méthode d'extension AsCancelable qui fait exactement cela).

2voto

Shahin Dohan Points 743

En .NET 5, vous pouvez appeler WaitForExitAsync mais cette méthode n'existe pas dans .NET Framework.

Je suggérerais (même si vous utilisez .NET 5+) l'option CliWrap qui fournit un support asynchrone dès le départ (et, espérons-le, gère toutes les conditions de course) et facilite l'exécution de tâches telles que le piping et le routage de la sortie.

Je ne l'ai découvert que récemment et je dois dire que je l'aime beaucoup jusqu'à présent !

Exemple stupide :

var cmd = Cli.Wrap(@"C:\test\app.exe")
    .WithArguments("-foo bar")
    .WithStandardOutputPipe(PipeTarget.ToFile(@"C:\test\stdOut.txt"))
    .WithStandardErrorPipe(PipeTarget.ToDelegate(s => Debug.WriteLine(s)));

var result = await cmd.ExecuteAsync(cancellationToken);
Debug.WriteLine(result.ExitCode);

-1voto

Johann Medina Points 131

Je suis vraiment inquiet au sujet de l'élimination du processus, qu'en est-il de l'attente de la sortie asynchrone ? voici ma proposition (basée sur la précédente) :

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

Ensuite, utilisez-le comme ceci :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X