Получение данных из cmd при запуске скрипта python в приложении c#

Всем доброго дня!

Подскажите, пожалуйста, как получать данные данные из командной строки при запуске ее из приложения с#. Проблема в том, что результаты выполнения появляются по завершении работы скрипта, мне же нужно получать данные из cmd по ходу выполнения программы. Запускаю скрипт python. Скрипт выполняется в отдельном потоке.

Возможно можно сделать это как-то проще, но я нашел только такое решение.

Код такой:

 private Task consoleTask;
        private CancellationTokenSource consoleToken;

        private void RunConsoleCommand(string command)
        {
            try
            {
                using (var process = new Process()
                {
                    StartInfo =
                        {
                            CreateNoWindow = true,
                            UseShellExecute = false,
                            RedirectStandardOutput = true,
                            StandardOutputEncoding = Encoding.GetEncoding(866),
                            Arguments = "/c " + command,
                            FileName = "cmd.exe"
                        }
                })
                {
                    // Процесс еще не стартовал, просто выходим из функции...
                    if (consoleToken.IsCancellationRequested)
                        return;

                    process.Start();

                    using (StreamReader reader = process.StandardOutput)
                    {
                        while (!reader.EndOfStream)
                        {
                            if (consoleToken.IsCancellationRequested)
                            {
                                // Если функцию отменили принудительно, убиваем процесс.
                                process.Kill();
                                return;
                            }

                            var text = reader.ReadLine();
                            this.AppendLine(text);
                            Thread.Sleep(1);
                        }
                    }

                    // Если вывод закончился, ждем завершения процесса.
                    process.WaitForExit();
                } // Закрываем хендл
            }
            finally
            {
                this.EnableRun();
            }
        }

        private delegate void AppendLineDelegate(string text);

        private void AppendLine(string text)
        {
            if (!this.InvokeRequired)
            {
                richTextBox1.AppendText(text + Environment.NewLine);
            }
            else
            {
                // На всякий случай, делаем вызов отложенным...
                this.BeginInvoke(new AppendLineDelegate(this.AppendLine), new object[] { text });
            }
        }

        private delegate void EnableRunDelegate();

        private void EnableRun()
        {
            if (!this.InvokeRequired)
            {

            }
            else
            {
                // Чтобы не войти в дедлок с ГУИ потоком, делаем вызов отложенным.
                this.BeginInvoke(new EnableRunDelegate(this.EnableRun));
            }
        }

        private void OnClosed(object sender, FormClosedEventArgs e)
        {
            // Просим поток завершиться.
            consoleToken.Cancel();
            // И даем ему возможность завершиться.
            consoleTask.Wait();
        }

        private void Button3_Click(object sender, EventArgs e)
        {
            string command = "python C:\\test.py";

            consoleToken = new CancellationTokenSource();
            consoleTask = new Task(() => RunConsoleCommand(command), consoleToken.Token);
            consoleTask.Start();
        }

При попытке сделать тоже самое немного иначе получаю точно такой же результат - строка появляется в label1 только по завершении работы скрипта python.

 private void Button2_Click(object sender, EventArgs e)
        {
            label1.Text = "Calling Python...";
            this.Refresh();
            Tuple<String, String> python = GoPython(@"C:\test.py");
            label1.Text = python.Item1; // Show result.
            if (python.Item2.Length > 0) MessageBox.Show("Sorry, there was an error:" + Environment.NewLine + python.Item2);
        }

        public Tuple<String, String> GoPython(string pythonFile, string moreArgs = "")
        {
            ProcessStartInfo PSI = new ProcessStartInfo();
            PSI.FileName = "py.exe";
            PSI.Arguments = string.Format("\"{0}\" {1}", pythonFile, moreArgs);
            PSI.CreateNoWindow = true;
            PSI.UseShellExecute = false;
            PSI.RedirectStandardError = true;
            PSI.RedirectStandardOutput = true;
            using (Process process = Process.Start(PSI))
            using (StreamReader reader = process.StandardOutput)
            {
                string stderr = process.StandardError.ReadToEnd(); // Error(s)!!
                string result = reader.ReadToEnd(); // What we want.
                return new Tuple<String, String>(result, stderr);
            }
        }

Ответы (1 шт):

Автор решения: rdorn

Рабочий пример с бесконечным пингом. На всякий случай добавил демонстрацию перенаправления потока ошибок. Все построено на событиях потока, окно не блокируется.

Только учтите, что ни TextBox, ни Label, не способен адекватно обрабатывать большие объемы текста и добавление каждой следующей строки будет становиться медленнее, потому что при изменении текста он перерисовывает текст полностью, независимо от того, какой фрагмент реально отображается, а какой нет, хотя первое время это не заметно. Сколько кб|Мб текста он может съесть без заметного проседания производительности не проверял.

  • Нет, это не лечится.
  • Замена на RichTextBox поможет не надолго.

Реально помочь может только собственная или сторонняя реализация аналогичного контрола с поддержкой виртуализации отображаемого текста.

public partial class Form1 : Form
{
    private Process proc = null;
    private TextBox tb = new TextBox();

    public Form1()
    {
        var btn = new Button() { Text = "Ping -t" };
        Controls.Add(btn);
        btn.Dock = DockStyle.Bottom;
        btn.Click += (s, e) =>
        {
            if (proc != null) return;
            ProcessStartInfo startInfo = new ProcessStartInfo()
            {
                FileName = @"ping",
                Arguments = @"8.8.8.8 -t",
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };
            proc = new Process();
            proc.StartInfo = startInfo;
            proc.OutputDataReceived += Proc_DataReceived;
            proc.ErrorDataReceived += Proc_DataReceived;
            proc.Start();
            proc.BeginOutputReadLine();
        };
        Controls.Add(tb);
        tb.Multiline = true;
        tb.Dock = DockStyle.Fill;
    }

    private void Proc_DataReceived(object sender, DataReceivedEventArgs e)
    {
        tb.Invoke((Action)(() => { tb.AppendText(e.Data + Environment.NewLine); }));
    }

    protected override void OnClosing(CancelEventArgs e)
    {
        if (proc != null) proc.Kill();
        proc.Dispose();
        base.OnClosing(e);
    }
}
→ Ссылка