System.Diagnostics.Process RedirectStandardOutput 实时出现在 Winforms 文本框中的问题
我在 Winforms 文本框中实时呈现控制台应用程序的重定向输出时遇到问题。消息是逐行生成的,但是一旦需要与表单交互,就不会显示任何内容。
按照 Stackoverflow 和其他论坛上的许多示例,在流程完成之前,我似乎无法将流程的重定向输出显示在表单的文本框中。
通过将调试行添加到“stringWriter_StringWritten”方法并将重定向的消息写入调试窗口,我可以看到在进程运行期间到达的消息,但在进程完成之前这些消息不会出现在表单的文本框中。
感谢您对此的任何建议。
这是代码的摘录
public partial class RunExternalProcess : Form
{
private static int numberOutputLines = 0;
private static MyStringWriter stringWriter;
public RunExternalProcess()
{
InitializeComponent();
// Create the output message writter
RunExternalProcess.stringWriter = new MyStringWriter();
stringWriter.StringWritten += new EventHandler(stringWriter_StringWritten);
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.FileName = "myCommandLineApp.exe";
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.CreateNoWindow = true;
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
using (var pProcess = new System.Diagnostics.Process())
{
pProcess.StartInfo = startInfo;
pProcess.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(RunExternalProcess.Process_OutputDataReceived);
pProcess.EnableRaisingEvents = true;
try
{
pProcess.Start();
pProcess.BeginOutputReadLine();
pProcess.BeginErrorReadLine();
pProcess.WaitForExit();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
pProcess.OutputDataReceived -= new System.Diagnostics.DataReceivedEventHandler(RunExternalProcess.Process_OutputDataReceived);
}
}
}
private static void Process_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
{
if (!string.IsNullOrEmpty(e.Data))
{
RunExternalProcess.OutputMessage(e.Data);
}
}
private static void OutputMessage(string message)
{
RunExternalProcess.stringWriter.WriteLine("[" + RunExternalProcess.numberOutputLines++.ToString() + "] - " + message);
}
private void stringWriter_StringWritten(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine(((MyStringWriter)sender).GetStringBuilder().ToString());
SetProgressTextBox(((MyStringWriter)sender).GetStringBuilder().ToString());
}
private delegate void SetProgressTextBoxCallback(string text);
private void SetProgressTextBox(string text)
{
if (this.ProgressTextBox.InvokeRequired)
{
SetProgressTextBoxCallback callback = new SetProgressTextBoxCallback(SetProgressTextBox);
this.BeginInvoke(callback, new object[] { text });
}
else
{
this.ProgressTextBox.Text = text;
this.ProgressTextBox.Select(this.ProgressTextBox.Text.Length, 0);
this.ProgressTextBox.ScrollToCaret();
}
}
}
public class MyStringWriter : System.IO.StringWriter
{
// Define the event.
public event EventHandler StringWritten;
public MyStringWriter()
: base()
{
}
public MyStringWriter(StringBuilder sb)
: base(sb)
{
}
public MyStringWriter(StringBuilder sb, IFormatProvider formatProvider)
: base(sb, formatProvider)
{
}
public MyStringWriter(IFormatProvider formatProvider)
: base(formatProvider)
{
}
protected virtual void OnStringWritten()
{
if (StringWritten != null)
{
StringWritten(this, EventArgs.Empty);
}
}
public override void Write(char value)
{
base.Write(value);
this.OnStringWritten();
}
public override void Write(char[] buffer, int index, int count)
{
base.Write(buffer, index, count);
this.OnStringWritten();
}
public override void Write(string value)
{
base.Write(value);
this.OnStringWritten();
}
}
I'm having problems with the redirected output from a console application being presented in a Winforms textbox in real-time. The messages are being produced line by line however as soon as interaction with the Form is called for, nothing appears to be displayed.
Following the many examples on both Stackoverflow and other forums, I can't seem to get the redirected output from the process to display in the textbox on the form until the process completes.
By adding debug lines to the 'stringWriter_StringWritten' method and writing the redirected messages to the debug window I can see the messages arriving during the running of the process but these messages will not appear on the form's textbox until the process completes.
Grateful for any advice on this.
Here's an extract of the code
public partial class RunExternalProcess : Form
{
private static int numberOutputLines = 0;
private static MyStringWriter stringWriter;
public RunExternalProcess()
{
InitializeComponent();
// Create the output message writter
RunExternalProcess.stringWriter = new MyStringWriter();
stringWriter.StringWritten += new EventHandler(stringWriter_StringWritten);
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.FileName = "myCommandLineApp.exe";
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.CreateNoWindow = true;
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
using (var pProcess = new System.Diagnostics.Process())
{
pProcess.StartInfo = startInfo;
pProcess.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(RunExternalProcess.Process_OutputDataReceived);
pProcess.EnableRaisingEvents = true;
try
{
pProcess.Start();
pProcess.BeginOutputReadLine();
pProcess.BeginErrorReadLine();
pProcess.WaitForExit();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
pProcess.OutputDataReceived -= new System.Diagnostics.DataReceivedEventHandler(RunExternalProcess.Process_OutputDataReceived);
}
}
}
private static void Process_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
{
if (!string.IsNullOrEmpty(e.Data))
{
RunExternalProcess.OutputMessage(e.Data);
}
}
private static void OutputMessage(string message)
{
RunExternalProcess.stringWriter.WriteLine("[" + RunExternalProcess.numberOutputLines++.ToString() + "] - " + message);
}
private void stringWriter_StringWritten(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine(((MyStringWriter)sender).GetStringBuilder().ToString());
SetProgressTextBox(((MyStringWriter)sender).GetStringBuilder().ToString());
}
private delegate void SetProgressTextBoxCallback(string text);
private void SetProgressTextBox(string text)
{
if (this.ProgressTextBox.InvokeRequired)
{
SetProgressTextBoxCallback callback = new SetProgressTextBoxCallback(SetProgressTextBox);
this.BeginInvoke(callback, new object[] { text });
}
else
{
this.ProgressTextBox.Text = text;
this.ProgressTextBox.Select(this.ProgressTextBox.Text.Length, 0);
this.ProgressTextBox.ScrollToCaret();
}
}
}
public class MyStringWriter : System.IO.StringWriter
{
// Define the event.
public event EventHandler StringWritten;
public MyStringWriter()
: base()
{
}
public MyStringWriter(StringBuilder sb)
: base(sb)
{
}
public MyStringWriter(StringBuilder sb, IFormatProvider formatProvider)
: base(sb, formatProvider)
{
}
public MyStringWriter(IFormatProvider formatProvider)
: base(formatProvider)
{
}
protected virtual void OnStringWritten()
{
if (StringWritten != null)
{
StringWritten(this, EventArgs.Empty);
}
}
public override void Write(char value)
{
base.Write(value);
this.OnStringWritten();
}
public override void Write(char[] buffer, int index, int count)
{
base.Write(buffer, index, count);
this.OnStringWritten();
}
public override void Write(string value)
{
base.Write(value);
this.OnStringWritten();
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
你正在挂起 UI 线程。在进程退出之前,它不会开始执行其正常职责,例如绘制文本框。同样,BeginInvoke() 委托目标在 UI 线程再次空闲之前无法运行。它们将堆积在调用队列中,在极端情况下,您将耗尽内存。即使进程退出后,UI 线程在清空队列时也会有一段时间没有响应。
你在这里不需要它。如有必要,请改用 Process.Exited 事件。
You are hanging the UI thread with that. It won't get around to doing its normal duties, like paint the text box, until the process has exited. Similarly, the BeginInvoke() delegate target cannot run until the UI thread goes idle again. They'll stack up in the invoke queue, in extreme cases you'll run out of memory. Even after the process exits, the UI thread will be unresponsive for some time while it works on emptying the queue.
You don't need it here. If necessary, use the Process.Exited event instead.