2010-12-15 115 views
6

J'ai des problèmes pour rediriger la sortie de la console vers une zone de texte Windows Forms. Le problème est lié au thread. Je dirigeais une application console de la manière suivante,Comment puis-je rediriger la sortie d'un programme de console vers une zone de texte de manière sûre pour les threads?

private void RunConsoleApp() 
{ 
    Process proc = new Process(); 
    proc.StartInfo.FileName = "app.exe"; 
    proc.StartInfo.Arguments = "-a -b -c"; 
    proc.StartInfo.UseShellExecute = false; 

    // set up output redirection 
    proc.StartInfo.RedirectStandardOutput = true; 
    proc.StartInfo.RedirectStandardError = true;  
    proc.EnableRaisingEvents = true; 
    proc.StartInfo.CreateNoWindow = true; 

    // Set the data received handlers 
    proc.ErrorDataReceived += proc_DataReceived; 
    proc.OutputDataReceived += proc_DataReceived; 

    proc.Start(); 
    proc.BeginErrorReadLine(); 
    proc.BeginOutputReadLine(); 
    proc.WaitForExit(); 

    if (proc.ExitCode == 0) 
    { 
     out_txtbx.AppendText("Success." + Environment.NewLine); 
    } 
    else 
    { 
     out_txtbx.AppendText("Failed." + Environment.NewLine); 
    } 
} 

puis capturer et traiter les données avec ce gestionnaire de sortie,

// Handle the date received by the console process 
void proc_DataReceived(object sender, DataReceivedEventArgs e) 
{ 
    if (e.Data != null) 
    { 
     if ((e.Data.EndsWith("DONE.")) || (e.Data.EndsWith("FAILED.")) || 
      (e.Data.StartsWith("RESET"))) 
     { 
      // This crashes the application, but is supposedly the correct method 
      this.AppendText(e.Data + Environment.NewLine); 

      // This works, but the debugger keeps warning me that the call 
      // is not thread safe 
      //out_txtbx.AppendText(e.Data + Environment.NewLine); 
     } 
    } 
} 

Le texte de la console est alors ajouté comme celui-ci,

delegate void AppendTextDelegate(string text); 

// Thread-safe method of appending text to the console box 
private void AppendText(string text) 
{ 
    // Use a delegate if called from a different thread, 
    // else just append the text directly 
    if (this.out_txtbx.InvokeRequired) 
    { 
     // Application crashes when this line is executed 
     out_txtbx.Invoke(new AppendTextDelegate(this.AppendText), new object[] { text }); 
    } 
    else 
    { 
     this.out_txtbx.AppendText(text); 
    } 
} 

De toute la documentation et des exemples que j'ai vu cela semble être la méthode correcte, sauf que cela plante l'application quand out_txtbx.Invoke est appelée.

Qu'est-ce qui pourrait être brisé et quelles autres façons de faire?


Solution (comme l'a souligné Hans Passant)

Le problème est que l'application est coincé dans une "étreinte mortelle" en raison de la ligne,

proc.WaitForExit(); 

Cette ligne doit être supprimée et la méthode doit ressembler à ceci,

private void RunConsoleApp() 
{ 
    Process proc = new Process(); 
    proc.StartInfo.FileName = "app.exe"; 
    proc.StartInfo.Arguments = "-a -b -c"; 
    proc.StartInfo.UseShellExecute = false; 

    // set up output redirection 
    proc.StartInfo.RedirectStandardOutput = true; 
    proc.StartInfo.RedirectStandardError = true;  
    proc.EnableRaisingEvents = true; 
    proc.StartInfo.CreateNoWindow = true; 

    // Set the data received handlers 
    proc.ErrorDataReceived += proc_DataReceived; 
    proc.OutputDataReceived += proc_DataReceived; 

    // Configure the process exited event 
    proc.Exited += new EventHandler(ProcExited); 

    proc.Start(); 
    proc.BeginErrorReadLine(); 
    proc.BeginOutputReadLine(); 

    // This blocks the main thread and results in "deadly embrace" 
    // The Process.Exited event should be used to avoid this. 
    //proc.WaitForExit(); 
} 

et un gestionnaire d'événements doit être fourni,

/// <summary> 
/// Actions to take when console process completes 
/// </summary> 
private void ProcExited(object sender, System.EventArgs e) 
{ 
    Process proc = (Process)sender; 

    // Wait a short while to allow all console output to be processed and appended 
    // before appending the success/fail message. 
    Thread.Sleep(40); 

    if (proc.ExitCode == 0) 
    { 
     this.AppendText("Success." + Environment.NewLine); 
     ExitBootloader(); 
    } 
    else 
    { 
     this.AppendText("Failed." + Environment.NewLine); 
    } 

    proc.Close(); 
} 
+2

Quelle erreur obtenez-vous? – SLaks

+0

Et sur quelle ligne ?? – decyclone

+0

Je l'ai fait sans erreur. S'il n'y a pas de réponse adéquate au moment où je pourrai accéder à mon exemple de code, je posterai. Notez que ce sera plus tard ce soir quand je rentre à la maison. J'avais créé un objet TextWriter qui écrivait dans une zone de texte, puis dirigeais Console.SetOut vers TextWriter. Voir http://msdn.microsoft.com/en-us/library/system.console.setout%28v=VS.90%29.aspx – IAbstract

Répondre

9
proc.WaitForExit(); 

Il est appelé impasse. Votre thread principal est bloqué, en attente de la sortie du processus. Cela l'empêche de s'occuper des tâches essentielles. Comme garder l'interface utilisateur mise à jour. Et en vérifiant que les demandes Control.Invoke() sont distribuées. Cela arrête la méthode AppendText() de terminer. Ce qui arrête le processus de sortie. Ce qui empêche votre thread d'interface utilisateur de passer l'appel WaitForExit(). "Embrasement mortel", aka impasse.

Vous ne pouvez pas bloquer votre thread principal. Utilisez plutôt l'événement Process.Exited.

+0

Merci Hans, cela fait sens (maintenant que vous l'avez expliqué). –

0

essayer

out_txtbx.Invoke(new AppendTextDelegate(this.AppendText), text); 
+1

Cela ne fonctionne pas –