在 Winforms 中操作进程输出 (C#)
Manupilating Process Output in Win Forms (C#)
我有一个 WinForms 应用程序,它 运行 多个进程 运行 作为后台工作程序。我为每个新进程创建一个后台工作者
BackgroundWorker background = new BackgroundWorker();
background.DoWork += new DoWorkEventHandler(bkWorker_DoWork);
background.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bkWorker_Complete);
background.WorkerReportsProgress = false;
background.WorkerSupportsCancellation = true;
我的 Dowork 代码如下所示:
private void bkWorker_DoWork(object sender, DoWorkEventArgs e) {
WorkerArgument obj = (WorkerArgument) e.Argument;
BackgroundWorker worker = (BackgroundWorker) sender;
Process proc = new Process();
proc.StartInfo.FileName = "cmdprocess"; //"cmd.exe";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardInput = true;
proc.StartInfo.RedirectStandardError = true;
proc.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
proc.ErrorDataReceived += new DataReceivedEventHandler(ErrorOutputHandler);
proc.Start();
process.Add(obj.Row, proc.Id);
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
proc.WaitForExit();
proc.Close();
}
您可能会注意到,我正在使用事件处理程序对 cmdProcess (SortOutputHandler) 给出的输出进行排序,与错误输出相同。
我在文本框中显示此输出
private void SortOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) {
if (!String.IsNullOrEmpty(outLine.Data)) {
if (txtLog.InvokeRequired) {
this.Invoke((MethodInvoker) delegate {
string[] lines = txtLog.Lines;
if (lines.Length > 200) {
string[] newlines = lines.Skip(200) as string[];
txtLog.Lines = newlines;
}
txtLog.AppendText(Environment.NewLine + outLine.Data);
});
} else {
string[] lines = txtLog.Lines;
if (lines.Length > 200) {
string[] newlines = lines.Skip(200) as string[];
txtLog.Lines = newlines;
}
txtLog.AppendText(Environment.NewLine + outLine.Data);
}
}
}
错误处理程序如下:
private void ErrorOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) {
if (!String.IsNullOrEmpty(outLine.Data)) {
if (txtLog.InvokeRequired) {
this.Invoke((MethodInvoker) delegate {
string[] lines = txtLog.Lines;
if (lines.Length > 200) {
string[] newlines = lines.Skip(200) as string[];
txtLog.Lines = newlines;
}
txtLog.AppendText(Environment.NewLine + outLine.Data);
});
} else {
string[] lines = txtLog.Lines;
if (lines.Length > 200) {
string[] newlines = lines.Skip(200) as string[];
txtLog.Lines = newlines;
}
txtLog.AppendText(Environment.NewLine + outLine.Data);
}
}
}
问题是,当进程数增加时,日志会迅速启动 运行ning。
我创建了一个新的表单 frmLog,里面有一个多行文本框,你能告诉我如何使用它从一个进程中获取输出吗?就像可以有一个 "View Log" 按钮,当我单击它时只显示该进程的日志。
是否可以将所有进程的输出分别写入唯一的文件?
首先,请考虑以下类型来保存进程输出的示例
interface IProcessOutput
{
IReadOnlyCollection<string> Cerr { get; }
IReadOnlyCollection<string> Cout { get; }
void OnError(string text);
void OnOutput(string text);
}
然后,启动一个新进程,实例化一个实现该接口的类型和"bind"事件到相应的对象。
如果您想滚动日志(使用 2000 或任何限制),我鼓励您使用 Queue(如 IReadOnlyCollection),因为 Queue 在内部保存头部和尾部,因此如果您保持一定的容量限制,它不会 allocate/move 内存。它足够快了。
更改 GUI,使带有日志输出的 ListBox 可以属于任何 运行 个进程(类似于 master/detail)。所以,一旦你 select 一个进程,你就会看到相应的日志行。
请使用 ListBox 而不是文本框。在将字符串加载到其中时使用 BeginUpdate() 和 EndUpdate()。如果您的输出量很大,请在列表框中使用自绘制,这样您就不需要将字符串存储到它的 Items 集合中,而是直接访问队列对象。
PS:准备好接收不构成完整行的字符串(没有换行符)。当进程刷新文件句柄(cerr、cout 也是文件句柄)时,会在内部调用带输出的事件。因此,如果进程在字符串中间刷新它 - 您将获得字符串的一半。
我有一个 WinForms 应用程序,它 运行 多个进程 运行 作为后台工作程序。我为每个新进程创建一个后台工作者
BackgroundWorker background = new BackgroundWorker();
background.DoWork += new DoWorkEventHandler(bkWorker_DoWork);
background.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bkWorker_Complete);
background.WorkerReportsProgress = false;
background.WorkerSupportsCancellation = true;
我的 Dowork 代码如下所示:
private void bkWorker_DoWork(object sender, DoWorkEventArgs e) {
WorkerArgument obj = (WorkerArgument) e.Argument;
BackgroundWorker worker = (BackgroundWorker) sender;
Process proc = new Process();
proc.StartInfo.FileName = "cmdprocess"; //"cmd.exe";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardInput = true;
proc.StartInfo.RedirectStandardError = true;
proc.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
proc.ErrorDataReceived += new DataReceivedEventHandler(ErrorOutputHandler);
proc.Start();
process.Add(obj.Row, proc.Id);
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
proc.WaitForExit();
proc.Close();
}
您可能会注意到,我正在使用事件处理程序对 cmdProcess (SortOutputHandler) 给出的输出进行排序,与错误输出相同。
我在文本框中显示此输出
private void SortOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) {
if (!String.IsNullOrEmpty(outLine.Data)) {
if (txtLog.InvokeRequired) {
this.Invoke((MethodInvoker) delegate {
string[] lines = txtLog.Lines;
if (lines.Length > 200) {
string[] newlines = lines.Skip(200) as string[];
txtLog.Lines = newlines;
}
txtLog.AppendText(Environment.NewLine + outLine.Data);
});
} else {
string[] lines = txtLog.Lines;
if (lines.Length > 200) {
string[] newlines = lines.Skip(200) as string[];
txtLog.Lines = newlines;
}
txtLog.AppendText(Environment.NewLine + outLine.Data);
}
}
}
错误处理程序如下:
private void ErrorOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) {
if (!String.IsNullOrEmpty(outLine.Data)) {
if (txtLog.InvokeRequired) {
this.Invoke((MethodInvoker) delegate {
string[] lines = txtLog.Lines;
if (lines.Length > 200) {
string[] newlines = lines.Skip(200) as string[];
txtLog.Lines = newlines;
}
txtLog.AppendText(Environment.NewLine + outLine.Data);
});
} else {
string[] lines = txtLog.Lines;
if (lines.Length > 200) {
string[] newlines = lines.Skip(200) as string[];
txtLog.Lines = newlines;
}
txtLog.AppendText(Environment.NewLine + outLine.Data);
}
}
}
问题是,当进程数增加时,日志会迅速启动 运行ning。
我创建了一个新的表单 frmLog,里面有一个多行文本框,你能告诉我如何使用它从一个进程中获取输出吗?就像可以有一个 "View Log" 按钮,当我单击它时只显示该进程的日志。
是否可以将所有进程的输出分别写入唯一的文件?
首先,请考虑以下类型来保存进程输出的示例
interface IProcessOutput
{
IReadOnlyCollection<string> Cerr { get; }
IReadOnlyCollection<string> Cout { get; }
void OnError(string text);
void OnOutput(string text);
}
然后,启动一个新进程,实例化一个实现该接口的类型和"bind"事件到相应的对象。 如果您想滚动日志(使用 2000 或任何限制),我鼓励您使用 Queue(如 IReadOnlyCollection),因为 Queue 在内部保存头部和尾部,因此如果您保持一定的容量限制,它不会 allocate/move 内存。它足够快了。
更改 GUI,使带有日志输出的 ListBox 可以属于任何 运行 个进程(类似于 master/detail)。所以,一旦你 select 一个进程,你就会看到相应的日志行。
请使用 ListBox 而不是文本框。在将字符串加载到其中时使用 BeginUpdate() 和 EndUpdate()。如果您的输出量很大,请在列表框中使用自绘制,这样您就不需要将字符串存储到它的 Items 集合中,而是直接访问队列对象。
PS:准备好接收不构成完整行的字符串(没有换行符)。当进程刷新文件句柄(cerr、cout 也是文件句柄)时,会在内部调用带输出的事件。因此,如果进程在字符串中间刷新它 - 您将获得字符串的一半。