2010-12-15 20 views
6

Mam problem z przekierowaniem wyjścia konsoli do pola tekstowego formularzy systemu Windows. Problem dotyczy wątków. Używam app konsoli w następujący sposóbJak przekierować wyjście programu konsoli do pola tekstowego w sposób bezpieczny dla wątków?

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

a następnie przechwytywać i przetwarzać dane z tego programu obsługi wyjścia,

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

Tekst konsola jest następnie dołączany tak,

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

Z całej dokumentacji i przykładów, które widziałem, wydaje się, że jest to właściwa metoda, z tym wyjątkiem, że powoduje awarię aplikacji, gdy wywołano funkcję out_txtbx.Invoke.

Co można zepsuć i jakie są alternatywne sposoby, aby to zrobić?


Rozwiązanie (jak podkreślił Hans Passant)

Problemem jest to, że aplikacja jest zatrzymany w "śmiertelnym uścisku" w wyniku linii,

proc.WaitForExit(); 

że linia powinna być usunięta i metoda powinna wyglądać tak,

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

oraz obsługi zdarzenia należy zapewnić,

/// <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

Jakiego błędu dostałeś? – SLaks

+0

I na jakiej linii ?? – decyclone

+0

Zrobiłem to bez błędu. Jeśli nie ma odpowiedniej odpowiedzi, zanim dostanę próbkę kodu, opublikuję. Zauważ, że to będzie późnym wieczorem, kiedy wrócę do domu. Stworzyłem obiekt TextWriter, który zapisał do pola tekstowego, a następnie polecił Console.SetOut do TextWriter. Zobacz http://msdn.microsoft.com/en-us/library/system.console.setout%28v=VS.90%29.aspx – IAbstract

Odpowiedz

9
proc.WaitForExit(); 

Nazywa impasu. Twój główny wątek jest zablokowany, czekając na zakończenie procesu. To powstrzymuje go od dbania o podstawowe obowiązki. Jak aktualizowanie interfejsu użytkownika. I upewniając się, że wywoływane są żądania Control.Invoke(). To zatrzymuje metodę AppendText() od ukończenia. Co zatrzymuje proces wychodzenia. Co powstrzymuje wątek interfejsu użytkownika od przechodzenia przez wywołanie WaitForExit(). "Śmiertelne objęcie", czyli impas.

Nie można zablokować głównego wątku. Zamiast tego użyj zdarzenia Process.Exited.

+0

Dzięki Hans, to ma sens (teraz, kiedy to wytłumaczyłeś). –

0

spróbować

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

To nie działa –

Powiązane problemy