0

I want to use process to print log to richtextbox, but it does not work, I don't know why.

When I use LogWithColor, It will block the program, can't print anything.

When I use richTextBox1.AppendText, or richTextBox1.Text +=, It will print, but will auto close the program, do not print "Finished." And VS2019 Debuger can't get inside, It will cause Exception: System.InvalidOperationException”(In System.Windows.Forms.dll)

        public readonly string ffmpegExe = @"C:\Users\jared\AppData\Local\ffmpeg-4.4-full_build\bin\ffmpeg.exe";

        private void OutputHandler(object sendingProcess, DataReceivedEventArgs oneLine)
        {
            // LogWithColor(richTextBox1, Color.Black, oneLine.Data); // does not work
            // richTextBox1.AppendText(oneLine.Data); // it print, but I don’t know why the program will be closed auto
        }
        private void ErrorHandler(object sendingProcess, DataReceivedEventArgs oneLine)
        {
            LogWithColor(richTextBox1, Color.Red, oneLine.Data); // does not work
            // richTextBox1.AppendText(oneLine.Data); // it print, but I don’t know why the program will be closed auto
        }

        private delegate void LogWithColorDelegate(RichTextBox rtb, Color color, string text);
        private void LogWithColor(RichTextBox rtb, Color color, string text)
        {
            if (InvokeRequired)
            {
                if (rtb.IsHandleCreated)
                {
                    rtb.Invoke(new LogWithColorDelegate(LogWithColor),
                        new object[] { rtb, color, text });
                }
            }
            else
            {
                rtb.AppendText(Environment.NewLine);
                rtb.SelectionColor = color;
                rtb.AppendText(text);

                // rtb.Text += Environment.NewLine + text; // still does not work
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(ffmpegExe) || !File.Exists(ffmpegExe))
            {
                return;
            }
            LogWithColor(richTextBox1, Color.Black, "Start..."); // work properly.
            using (Process p = new Process())
            {
                // RunCommand(p, ffmpegExe, "-h");
                // ffmpeg.exe -h
                p.StartInfo.FileName = ffmpegExe;
                p.StartInfo.Arguments = "-h";
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.RedirectStandardOutput = true;
                p.StartInfo.RedirectStandardError = true;
                p.StartInfo.CreateNoWindow = true;
                p.EnableRaisingEvents = true; // update for user9938 comment

                p.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
                p.ErrorDataReceived += new DataReceivedEventHandler(ErrorHandler);

                p.Start();
                p.BeginOutputReadLine();
                p.BeginErrorReadLine();
                p.WaitForExit(); 
            }
            LogWithColor(richTextBox1, Color.Black, "Finished.");
        }
Jared DC
  • 35
  • 6
  • [How do I get output from a command to appear in a control on a Form in real-time?](https://stackoverflow.com/a/51682585/7444103). See the notes and some comments in code. -- Whether or not this can work for depends on how the program you're starting writes to stdout. Try different options. -- See also the notes related to the `Process.SynchronizingObject`. I'd avoid `WaitForExit()`. – Jimi Aug 02 '21 at 02:11
  • You're missing `EnableRaisingEvents`. The following may be helpful: https://stackoverflow.com/questions/68208059/can-i-suppress-the-cr-crlf-sequence-in-a-cmd-exe-error-message/68215903#68215903 – Tu deschizi eu inchid Aug 02 '21 at 02:13
  • @user9938 Sorry, It still does not work. I have try yet. – Jared DC Aug 02 '21 at 02:26

1 Answers1

2

The issue is because you're waiting for the process execution completion using the UI thread. It will block the UI/main thread until the process has exited. However, the process will never exit because you're redirecting the output/error data and the listener thread is blocked. Please, read more about WaitForExit.

There are some solutions to solve the issue. You can use for example the ThreadPool, a Task or a new Thread. But, if you're using C# 5 and .NET Framework 4.5 or greater, I would recommend you to use the async/await.

Here is a code snip using asynchronous programming:

private async void button1_Click(object sender, EventArgs e)
{
    if (string.IsNullOrEmpty(ffmpegExe) || !File.Exists(ffmpegExe))
    {
        return;
    }

    LogWithColor(richTextBox1, Color.Black, "Start...");

    await Task.Run(() =>
    {
        using (var p = new Process())
        {
            p.StartInfo = new ProcessStartInfo(ffmpegExe, "-h")
            {
                UseShellExecute = false,
                RedirectStandardError = true,
                RedirectStandardOutput = true,
                CreateNoWindow = true,
            };

            p.EnableRaisingEvents = true;

            p.OutputDataReceived += (_, data) =>
            {
                LogWithColor(richTextBox1, Color.Black, data.Data);
            };

            p.ErrorDataReceived += (_, data) =>
            {
                LogWithColor(richTextBox1, Color.Red, data.Data);
            };

            p.Start();
            p.BeginOutputReadLine();
            p.BeginErrorReadLine();
            p.WaitForExit();
        }
    });

    LogWithColor(richTextBox1, Color.Black, "Finished.");
}

private void LogWithColor(RichTextBox rtb, Color color, string text)
{
    if (text == null)
    {
        return;
    }

    if (InvokeRequired)
    {
        Invoke(new Action(() => LogWithColor(rtb, color, text)));
        return;
    }

    rtb.AppendText(Environment.NewLine);
    rtb.SelectionColor = color;
    rtb.AppendText(text);
}
denys-vega
  • 3,522
  • 1
  • 19
  • 24