web-dev-qa-db-fra.com

Process.start: comment obtenir le résultat?

Je souhaite exécuter un programme de ligne de commande externe à partir de mon application Mono/.NET. Par exemple, je voudrais lancer mencoder . C'est possible:

  1. Pour obtenir la sortie Shell en ligne de commande et l'écrire dans ma zone de texte?
  2. Pour obtenir la valeur numérique pour afficher une barre de progression avec le temps écoulé?
243
stighy

Lorsque vous créez votre objet Process, définissez StartInfo de manière appropriée:

var proc = new Process 
{
    StartInfo = new ProcessStartInfo
    {
        FileName = "program.exe",
        Arguments = "command line arguments to your executable",
        UseShellExecute = false,
        RedirectStandardOutput = true,
        CreateNoWindow = true
    }
};

puis démarrez le processus et lisez-le:

proc.Start();
while (!proc.StandardOutput.EndOfStream)
{
    string line = proc.StandardOutput.ReadLine();
    // do something with line
}

Vous pouvez utiliser int.Parse() ou int.TryParse() pour convertir les chaînes en valeurs numériques. Vous devrez peut-être commencer par manipuler des chaînes si les chaînes que vous lisez contiennent des caractères numériques non valides.

372
Ferruccio

Vous pouvez traiter votre sortie de manière synchrone ou de manière asynchrone.

1: exemple synchrone

static void runCommand()
{
    Process process = new Process();
    process.StartInfo.FileName = "cmd.exe";
    process.StartInfo.Arguments = "/c DIR"; // Note the /c command (*)
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;
    process.Start();
    //* Read the output (or the error)
    string output = process.StandardOutput.ReadToEnd();
    Console.WriteLine(output);
    string err = process.StandardError.ReadToEnd();
    Console.WriteLine(err);
    process.WaitForExit();
}

Remarque qu'il est préférable de traiter à la fois sortie et erreurs: elles doivent être traitées séparément.

(*) Pour certaines commandes (ici StartInfo.Arguments), vous devez ajouter le /cdirective , sinon le processus se fige dans la WaitForExit().

2: exemple asynchrone

static void runCommand() 
{
    //* Create your Process
    Process process = new Process();
    process.StartInfo.FileName = "cmd.exe";
    process.StartInfo.Arguments = "/c DIR";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;
    //* Set your output and error (asynchronous) handlers
    process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
    process.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler);
    //* Start process and handlers
    process.Start();
    process.BeginOutputReadLine();
    process.BeginErrorReadLine();
    process.WaitForExit();
}

static void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine) 
{
    //* Do your stuff with the output (write to console/log/StringBuilder)
    Console.WriteLine(outLine.Data);
}

Si vous n'avez pas besoin d'effectuer des opérations compliquées avec la sortie, vous pouvez contourner la méthode OutputHandler, en ajoutant simplement les gestionnaires directement en ligne:

//* Set your output and error (asynchronous) handlers
process.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);
process.ErrorDataReceived += (s, e) => Console.WriteLine(e.Data);
195
T30

Très bien, pour ceux qui veulent lire les erreurs et les sorties, mais qui obtiennent des blocages avec l'une des solutions fournies dans d'autres réponses (comme moi), voici une solution que j'ai construite après avoir lu l'explication MSDN de la propriété StandardOutput.

La réponse est basée sur le code de T30:

static void runCommand()
{
    //* Create your Process
    Process process = new Process();
    process.StartInfo.FileName = "cmd.exe";
    process.StartInfo.Arguments = "/c DIR";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;
    //* Set ONLY ONE handler here.
    process.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler);
    //* Start process
    process.Start();
    //* Read one element asynchronously
    process.BeginErrorReadLine();
    //* Read the other one synchronously
    string output = process.StandardOutput.ReadToEnd();
    Console.WriteLine(output);
    process.WaitForExit();
}

static void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine) 
{
    //* Do your stuff with the output (write to console/log/StringBuilder)
    Console.WriteLine(outLine.Data);
}
8
cubrman

La méthode standard .NET consiste à lire le flux Process ' StandardOutput . Il existe un exemple dans les documents MSDN liés. De même, vous pouvez lire depuis StandardError et écrire dans StandardInput .

7
driis

vous pouvez utiliser la mémoire partagée pour que les 2 processus puissent communiquer, consultez MemoryMappedFile

vous créerez principalement un fichier mappé en mémoire mmf dans le processus parent à l'aide de l'instruction "using", puis créerez le deuxième processus jusqu'à ce qu'il se termine et le laissera écrire le résultat dans mmf à l'aide de BinaryWriter, puis lira le résultat à partir de mmf en utilisant le processus parent , vous pouvez également passer le nom mmf en utilisant des arguments de ligne de commande ou le coder en dur.

lorsque vous utilisez le fichier mappé dans le processus parent, assurez-vous que le processus enfant écrit le résultat dans le fichier mappé avant que le fichier mappé ne soit publié dans le processus parent.

Exemple: Processus parent

    private static void Main(string[] args)
    {
        using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("memfile", 128))
        {
            using (MemoryMappedViewStream stream = mmf.CreateViewStream())
            {
                BinaryWriter writer = new BinaryWriter(stream);
                writer.Write(512);
            }

            Console.WriteLine("Starting the child process");
            // Command line args are separated by a space
            Process p = Process.Start("ChildProcess.exe", "memfile");

            Console.WriteLine("Waiting child to die");

            p.WaitForExit();
            Console.WriteLine("Child died");

            using (MemoryMappedViewStream stream = mmf.CreateViewStream())
            {
                BinaryReader reader = new BinaryReader(stream);
                Console.WriteLine("Result:" + reader.ReadInt32());
            }
        }
        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();
    }

Processus enfant 

    private static void Main(string[] args)
    {
        Console.WriteLine("Child process started");
        string mmfName = args[0];

        using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting(mmfName))
        {
            int readValue;
            using (MemoryMappedViewStream stream = mmf.CreateViewStream())
            {
                BinaryReader reader = new BinaryReader(stream);
                Console.WriteLine("child reading: " + (readValue = reader.ReadInt32()));
            }
            using (MemoryMappedViewStream input = mmf.CreateViewStream())
            {
                BinaryWriter writer = new BinaryWriter(input);
                writer.Write(readValue * 2);
            }
        }

        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();
    }

pour utiliser cet exemple, vous devez créer une solution avec 2 projets à l'intérieur, puis extraire le résultat de la construction du processus enfant de% childDir%/bin/debug et le copier dans% parentDirectory%/bin/debug, puis exécuter le projet parent

childDir et parentDirectory sont les noms de dossiers de vos projets sur le PC bonne chance :)

4
shakram02
  1. Il est possible d'obtenir la sortie Shell d'un processus en ligne de commande comme décrit ici: http://www.c-sharpcorner.com/UploadFile/edwinlima/SystemDiagnosticProcess12052005035444AM/SystemDiagnosticProcess.aspx

  2. Cela dépend de mencoder. Si elle affiche cet état sur la ligne de commande, alors oui :)

3
basarat

Comment lancer un processus (tel qu'un fichier bat, un script Perl, un programme console) et afficher sa sortie standard sur un formulaire Windows:

processCaller = new ProcessCaller(this);
//processCaller.FileName = @"..\..\hello.bat";
processCaller.FileName = @"commandline.exe";
processCaller.Arguments = "";
processCaller.StdErrReceived += new DataReceivedHandler(writeStreamInfo);
processCaller.StdOutReceived += new DataReceivedHandler(writeStreamInfo);
processCaller.Completed += new EventHandler(processCompletedOrCanceled);
processCaller.Cancelled += new EventHandler(processCompletedOrCanceled);
// processCaller.Failed += no event handler for this one, yet.

this.richTextBox1.Text = "Started function.  Please stand by.." + Environment.NewLine;

// the following function starts a process and returns immediately,
// thus allowing the form to stay responsive.
processCaller.Start();    

Vous pouvez trouver ProcessCaller sur ce lien: Lancement d’un processus et affichage de sa sortie standard

1
Los Pollos Hermanos

Vous pouvez enregistrer la sortie du processus en utilisant le code ci-dessous:

ProcessStartInfo pinfo = new ProcessStartInfo(item);
pinfo.CreateNoWindow = false;
pinfo.UseShellExecute = true;
pinfo.RedirectStandardOutput = true;
pinfo.RedirectStandardInput = true;
pinfo.RedirectStandardError = true;
pinfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
var p = Process.Start(pinfo);
p.WaitForExit();
Process process = Process.Start(new ProcessStartInfo((item + '>' + item + ".txt"))
{
    UseShellExecute = false,
    RedirectStandardOutput = true
});
process.WaitForExit();
string output = process.StandardOutput.ReadToEnd();
if (process.ExitCode != 0) { 
}
0
SHUBHAM PATIDAR