С# Отображение стандартного вывода дочернего процесса в текстовом поле во время работы процесса

У меня есть форма с текстовым полем output_txtB, и я хочу запустить cmd, выполнить некоторые команды и отобразить стандартный вывод в текстовом поле. Я хочу получить стандартный вывод после выполнения каждой команды, пока работает дочерний процесс. Я нашел несколько решений здесь:

Перенаправить вывод (stdout, stderr) дочернего процесса в окно вывода в Visual Studio

C# получает вывод процесса во время работы

Но это не решило мою конкретную проблему, потому что я хочу отображать стандартный вывод в текстовом поле (а не в консоли или где-либо еще) и делать это сразу после выполнения каждой команды - я не хочу получать полный вывод после выхода из процесса.

Я пытался использовать событие OutputDataReceived в дочернем процессе, как было предложено в приведенных выше ссылках, но есть проблема, когда я хочу сослаться на текстовое поле, созданное в другом потоке, - оно выдает исключение InvalidOperationException. Вот мой код:

        Process process = new Process();
        process.EnableRaisingEvents = true;

        process.StartInfo.FileName = "cmd";
        process.StartInfo.CreateNoWindow = true;
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.RedirectStandardInput = true;
        process.OutputDataReceived += (sender, args) =>
            {
                //here throws an exception
                output_txtB.AppendText(args.Data + Environment.NewLine);
            };

        process.Start();
        process.BeginOutputReadLine();

        //commands I want to execute
        process.StandardInput.WriteLine("example.exe");
        process.StandardInput.WriteLine("script.bat");
        process.StandardInput.WriteLine("ipconfig /all");
        process.StandardInput.WriteLine("dir");
        process.StandardInput.Close();

        process.WaitForExit();

Дополнительная информация об исключении:

Недопустимая межпоточная операция: доступ к элементу управления 'output_txtB' получен из потока, отличного от потока, в котором он был создан.

Любые идеи, как получить стандартный вывод дочернего процесса и отобразить его в текстовом поле, пока дочерний процесс работает?

РЕДАКТИРОВАТЬ: см. код "example.exe":

        Console.WriteLine("line 1");
        System.Threading.Thread.Sleep(2000);
        Console.WriteLine("line 2");
        System.Threading.Thread.Sleep(2000);
        Console.WriteLine("line 3");
        System.Threading.Thread.Sleep(2000);
        Console.WriteLine("line 4");
        System.Threading.Thread.Sleep(2000);
        Console.WriteLine("line 5");

Чего я пытаюсь добиться, так это отображать в текстовом поле строку X каждый раз, когда процесс получает его в стандартном выводе. Но даже если я использую решение Стефано, кажется, что событие OutputDataReceived срабатывает и отображает полный вывод процесса после выхода из процесса.


person Darko    schedule 25.09.2015    source источник
comment
Вы пробовали с Invoke или BeginInvoke?   -  person Alessandro D'Andria    schedule 26.09.2015
comment
Я пробовал решение JTY, и оно работает. В любом случае, спасибо за предложение.   -  person Darko    schedule 26.09.2015


Ответы (2)


Вы должны использовать Invoke().

В другом потоке вы не можете обновлять свои представления. Логика изменения ваших представлений должна выполняться в потоке пользовательского интерфейса, и Invoke() используется только для этого.

Скорее всего, у вас есть фоновый поток, который работает и считывает выходные данные вашего процесса. Из этой темы, если вы хотите обновить пользовательский интерфейс, используйте метод Control.Invoke(), как показано ниже.

Я предпочитаю этот синтаксис только потому, что он "естественен":

    myControl.Invoke((Action)delegate
    {
        //You can put your UI update logic here
        myControl.Text = "Hello World from a different thread";
    });

Кроме того, вам не нужно вызывать Invoke непосредственно для изменяемого элемента управления. Вызов вызова в любом элементе управления (или непосредственно в вашей форме) будет означать, что код внутри будет выполняться в потоке пользовательского интерфейса, что означает, что вы также можете обновить другой пользовательский интерфейс.

    myControl.Invoke((Action)delegate
    {
        //You can put your UI update logic here
        myControl.Text = "Hello World from a different thread";
        myOtherControl.Text = "Look I can update other controls";
    });

    myForm.Invoke((Action)delegate
    {
        myControl.Text = "Can even call invoke from my Form";
    }
person JTY    schedule 26.09.2015

Это распространенная ошибка в Windows Forms и WPF при работе с объектами пользовательского интерфейса вне потока пользовательского интерфейса.

Измените Process.OutputDataReceived на:

process.OutputDataReceived += (s, args) =>
{
    this.output_txtB.BeginInvoke((MethodInvoker)delegate() { this.output_txtB.AppendText(args.Data + Environment.NewLine); });
};
person Stefano Castriotta    schedule 25.09.2015
comment
Я могу ссылаться на текстовое поле таким образом, но вывод не отображается до завершения процесса. См. РЕДАКТИРОВАТЬ под первым сообщением. - person Darko; 26.09.2015