首页 > 解决方案 > 如何从进程同步中捕获日志?

问题描述

我想使用进程将日志打印到richtextbox,但它不起作用,我不知道为什么。

当我使用 LogWithColor 时,它会阻塞程序,无法打印任何东西。

当我使用richTextBox1.AppendText 或richTextBox1.Text += 时,它会打印,但会自动关闭程序,不要打印“Finished”。而且VS2019 Debuger进不去,会导致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.");
        }

标签: c#processsynchronization

解决方案


问题是因为您正在使用 UI 线程等待流程执行完成。它将阻塞 UI/主线程,直到进程退出。但是,该进程将永远不会退出,因为您正在重定向输出/错误数据并且侦听器线程被阻塞。请阅读有关WaitForExit的更多信息。

有一些解决方案可以解决这个问题。例如,您可以使用ThreadPoolTask或新Thread。但是,如果您使用 C# 5 和 .NET Framework 4.5 或更高版本,我建议您使用async/await

这是使用异步编程的代码片段:

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

推荐阅读