Metoda WaitForExit() w klasie Proscess blokuje działaanie programu

0

Usunąłem "this.process1.SynchronizingObject = this;" zastosowałem "invoke" Niestety poniższy kod dalej blokuje program, a nie mogę usunąć "process1.WaitForExit();", bo proces który chcę uruchomić wykonuje się bardzo długo, a program musi poczekać na wyjście standardowe tego procesu.

using System;
using System.Windows.Forms;

namespace DISMassistant
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            process1.StartInfo.RedirectStandardError = true;
            process1.StartInfo.RedirectStandardOutput = true;
            process1.StartInfo.UseShellExecute = false;
            process1.StartInfo.FileName = "test.exe";
            //process1.StartInfo.Arguments = "/?";
        }

        public void button1_Click(object sender, EventArgs e)
        {
            //MessageBox.Show("OK");
            process1.Start();

            process1.BeginOutputReadLine();
            process1.BeginErrorReadLine();

            process1.WaitForExit();
        }

        private void process1_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
        {
            if (string.IsNullOrEmpty(e.Data)) return;

            richTextBox1.Invoke((MethodInvoker)delegate { richTextBox1.Text += e.Data + "\n"; });
        }

        private void process1_ErrorDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
        {
            if (string.IsNullOrEmpty(e.Data)) return;

            richTextBox1.Invoke((MethodInvoker)delegate { richTextBox1.Text += e.Data + "\n"; });
        }
    }
}

Poniższe rozwiązanie poprzez uruchomienie procesu w osobnym wątku rozwiązuje problem. Ale chciałbym wiedzieć dlaczego w konstruktorze "Form1" działa, a w "button1.Click()" już nie, jak jest wywoływane w tym samym wątku.

Thread th = new Thread(() =>
            {
                process1.Start();

                process1.BeginOutputReadLine();
                process1.BeginErrorReadLine();

                process1.WaitForExit();
            });
            th.Start();
0

To jak chcesz wiedzieć kiedy proces zakończy działanie i ma nie blokować aktualnego wątku to w dokumentacji przy metodzie Process.WaitForExit jest: "To avoid blocking the current thread, use the Exited event.".

w Form1.Designer.cs dodajesz:

this.process1.EnableRaisingEvents = true;
this.process1.Exited += new EventHandler(this.process1_ProcessExitHandler);

a w Form1.cs

private void process1_ProcessExitHandler(object sender, EventArgs e)
{

}
0

Dzięki Pablo8. Wykorzystanie zdarzenia "Exited" obeszło problem. Ale i tak nie rozumiem czemu poniższy kod działa w 100%, tzn.: Proces się kończy. W związku z czym "WaitForExit() też się kończy i nie blokuje niczego. Wyjście standardowe procesu zostaje skopiowane do richTextBox oraz wyświetla się "messageBox" generowany w zdarzeniu "Exited"

using System;
using System.Windows.Forms;

namespace DISMassistant
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            process1.StartInfo.RedirectStandardError = true;
            process1.StartInfo.RedirectStandardOutput = true;
            process1.StartInfo.UseShellExecute = false;
            process1.StartInfo.FileName = "cmd.exe";
            process1.StartInfo.Arguments = "/?";

            test();
        }

        public void test()
        {
            process1.Start();

            process1.BeginOutputReadLine();
            process1.BeginErrorReadLine();

            process1.WaitForExit();
            process1.CancelOutputRead();
            process1.CancelErrorRead();
            process1.Close();
        }

        private void process1_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
        {
            if (string.IsNullOrEmpty(e.Data)) return;

            if (richTextBox1.InvokeRequired)
            {
                richTextBox1.Invoke((MethodInvoker)delegate { richTextBox1.Text += e.Data + "\n"; });
            }
            else
            {
                richTextBox1.Text += e.Data + "\n";
            }
        }

        private void process1_ErrorDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
        {
            if (string.IsNullOrEmpty(e.Data)) return;

            if (richTextBox1.InvokeRequired)
            {
                richTextBox1.Invoke((MethodInvoker)delegate { richTextBox1.Text += e.Data + "\n"; });
            }
            else
            {
                richTextBox1.Text += e.Data + "\n";
            }
        }

        private void process1_Exited(object sender, EventArgs e)
        {
            process1.CancelOutputRead();
            process1.CancelErrorRead();
            MessageBox.Show("Proces zakończył się");
        }
    }
}

Natomiast w poniższym kodzie proces nie kończy się, dlatego wykonanie programu zatrzymuje się na "WaitForExit()", i nawet formularz się nie wyświetla. Różnica między kodami jest taka, że w pierwszym przypadku wywołuję metodę "test()" w konstruktorze klasy "Form1", a w drugim w zdarzeniu "Load" formularza.

using System;
using System.Windows.Forms;

namespace DISMassistant
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            process1.StartInfo.RedirectStandardError = true;
            process1.StartInfo.RedirectStandardOutput = true;
            process1.StartInfo.UseShellExecute = false;
            process1.StartInfo.FileName = "cmd.exe";
            process1.StartInfo.Arguments = "/?";
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            test();
        }

        public void test()
        {
            process1.Start();

            process1.BeginOutputReadLine();
            process1.BeginErrorReadLine();

            process1.WaitForExit();
            process1.CancelOutputRead();
            process1.CancelErrorRead();
            process1.Close();
        }

        private void process1_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
        {
            if (string.IsNullOrEmpty(e.Data)) return;

            if (richTextBox1.InvokeRequired)
            {
                richTextBox1.Invoke((MethodInvoker)delegate { richTextBox1.Text += e.Data + "\n"; });
            }
            else
            {
                richTextBox1.Text += e.Data + "\n";
            }
        }

        private void process1_ErrorDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
        {
            if (string.IsNullOrEmpty(e.Data)) return;

            if (richTextBox1.InvokeRequired)
            {
                richTextBox1.Invoke((MethodInvoker)delegate { richTextBox1.Text += e.Data + "\n"; });
            }
            else
            {
                richTextBox1.Text += e.Data + "\n";
            }
        }

        private void process1_Exited(object sender, EventArgs e)
        {
            process1.CancelOutputRead();
            process1.CancelErrorRead();
            MessageBox.Show("Proces zakończył się");
        }
    }
}
0

W obu przypadkach na głównym wątku wywołuje się process1.WaitForExit() i ten wątek zostaje zablokowany.
W pierwszym przypadku do metody process1_OutputDataReceived wchodzi z innego wątku i z tego wątku może dodać tekst do richTextBoxa.
W drugim przypadku proces cały czas się wykonuje, a do metody process1_OutputDataReceived nie wchodzi, ponieważ na innym wątku wykonuje cały czas funkcję OutputReadNotifyUser. Próbuje odwołać się (Invoke) do wątku głównego, który jest zablokowany.

0

Tylko w pierwszym przypadku uruchamiany proces się kończy o czym świadczy wystąpienie zdarzenia "Exited", więc prosess1."WaitForExit()" nie blokuje niczego.

0

WaitForExit() blokuje tylko do czasu zakończenia procesu. Zdarzenie Exited jest wykonywane po zakończeniu procesu, więc w tym momencie wątek nie jest już zablokowany, bo proces skończył już swoje działanie.

Zablokowanie programu nastąpi w takiej sytuacji:
Próbujesz zmodyfikować richTextBox.Text, ale nie możesz tego zrobić z aktualnego wątku i musisz odwołać się do innego wątku. Jeśli na tym innym wątku jest wywołana funkcja WaitForExit(), która blokuje wątek to wywołując Invoke z aktualnego wątku zablokujesz też ten wątek. W takim przypadku WaitForExit nigdy się nie skończy bo proces nie zakończy swojego działania.

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0