c# - 如何从进程同步中捕获日志?
问题描述
我想使用进程将日志打印到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.");
}
解决方案
问题是因为您正在使用 UI 线程等待流程执行完成。它将阻塞 UI/主线程,直到进程退出。但是,该进程将永远不会退出,因为您正在重定向输出/错误数据并且侦听器线程被阻塞。请阅读有关WaitForExit的更多信息。
有一些解决方案可以解决这个问题。例如,您可以使用ThreadPool、Task或新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);
}
推荐阅读
- pyspark - Pyspark ..滞后于生成/计算的列
- rest - 子 api 的包装器 api (ASP.NET Core2)
- c# - 将怪物变量传递给战斗控制器
- forms - 我想创建一个在多页中使用的页眉和页脚。我可以编写一次代码并在 xamarin 表单的多个页面上调用它吗
- android - Scrollview 中的 Viewpager 不起作用
- c# - 打开一个 Windows 资源管理器进程,然后再次终止它
- aframe - 使用联网 A-Frame (NAF) 组件时,如何禁用 LERPing?
- android - 当用户不活动时如何使用Android中所有活动的通用方法注销登录活动
- amazon-web-services - 使用 Athena 从云端日志中检测位置、浏览器和设备类型
- scala - Akka Stream TLS 服务器日志记录和故障排除