需要很长时间的运行过程
问题描述:
我在我的代码中调用了一个批处理文件并给出了某些参数。这个批处理文件调用另一个批处理文件等。整个过程大约需要45分钟才能完成。我还需要等待批处理文件完成,然后再继续执行其余的代码(清理批处理文件等)。需要很长时间的运行过程
我的问题是,虽然我已经尝试了几个不同的东西,但我无法获得批处理文件,既要完成其运行,也要将其输出写入日志文件。
这里是我做了什么让批处理文件来完成其运行:
Process process = new Process();
process.StartInfo.WorkingDirectory = Path.GetDirectoryName(filename);
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = false;
process.StartInfo.FileName = filename;
process.Start();
process.WaitForExit();
在尝试启用日志记录我已经尝试了不少的东西。这只是最新的尝试。
Process process = new Process();
process.StartInfo.WorkingDirectory = Path.GetDirectoryName(filename);
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.FileName = filename;
process.Start();
StreamReader sr = process.StandardOutput;
StreamWriter sw = new StreamWriter(exelocation + @"Logs\" + version + "\\" + outputname + ".txt");
while (!process.HasExited)
{
sw.Write(sr.ReadToEnd());
}
此代码基本上它使一个微不足道的部分首批文件中的一个,并停在那里。批处理文件在一分钟之内运行。我不明白为什么会发生这种情况。如果标准输出没有被重定向并且窗口被创建,它会完美运行,但如果窗口被隐藏并且标准输出被重定向,它什么也不做?
答
最可能的原因是批处理文件正在写入StandardError流,并且缓冲区已满。
管理标准流的重定向非常棘手。您需要异步读取两个流,如果要捕获所有输出,可能需要合并它们。我有一个类,这是否:
/// <summary>
/// Reads streams from a process's <see cref="Process.StandardOutput"/> and <see cref="Process.StandardError"/>
/// streams.
/// </summary>
public class ProcessOutputReader
{
/// <summary>
/// Builds the combined output of StandardError and StandardOutput.
/// </summary>
private readonly StringBuilder combinedOutputBuilder = new StringBuilder();
/// <summary>
/// Object that is locked to control access to <see cref="combinedOutputBuilder"/>.
/// </summary>
private readonly object combinedOutputLock = new object();
/// <summary>
/// Builds the error output string.
/// </summary>
private readonly StringBuilder errorOutputBuilder = new StringBuilder();
/// <summary>
/// The <see cref="Process"/> that this instance is reading.
/// </summary>
private readonly Process process;
/// <summary>
/// Builds the standard output string.
/// </summary>
private readonly StringBuilder standardOutputBuilder = new StringBuilder();
/// <summary>
/// Flag to record that we are already reading asynchronously (only one form is allowed).
/// </summary>
private bool readingAsync;
/// <summary>
/// Initializes a new instance of the <see cref="ProcessOutputReader"/> class.
/// </summary>
/// <param name="process">
/// The process.
/// </param>
public ProcessOutputReader(Process process)
{
if (process == null)
{
throw new ArgumentNullException("process");
}
this.process = process;
}
/// <summary>
/// Gets the combined output of StandardOutput and StandardError, interleaved in the correct order.
/// </summary>
/// <value>The combined output of StandardOutput and StandardError.</value>
public string CombinedOutput { get; private set; }
/// <summary>
/// Gets the error output.
/// </summary>
/// <value>
/// The error output.
/// </value>
public string StandardError { get; private set; }
/// <summary>
/// Gets the standard output.
/// </summary>
/// <value>
/// The standard output.
/// </value>
public string StandardOutput { get; private set; }
/// <summary>
/// Begins the read process output.
/// </summary>
public void BeginReadProcessOutput()
{
if (this.readingAsync)
{
throw new InvalidOperationException("The process output is already being read asynchronously");
}
this.readingAsync = true;
this.CheckProcessRunning();
this.process.OutputDataReceived += this.OutputDataReceived;
this.process.ErrorDataReceived += this.ErrorDataReceived;
this.process.BeginOutputReadLine();
this.process.BeginErrorReadLine();
}
/// <summary>
/// Ends asynchronous reading of process output.
/// </summary>
public void EndReadProcessOutput()
{
if (!this.process.HasExited)
{
this.process.CancelOutputRead();
this.process.CancelErrorRead();
}
this.process.OutputDataReceived -= this.OutputDataReceived;
this.process.ErrorDataReceived -= this.ErrorDataReceived;
this.StandardOutput = this.standardOutputBuilder.ToString();
this.StandardError = this.errorOutputBuilder.ToString();
this.CombinedOutput = this.combinedOutputBuilder.ToString();
}
/// <summary>
/// Reads the process output.
/// </summary>
public void ReadProcessOutput()
{
if (this.readingAsync)
{
throw new InvalidOperationException("The process output is already being read asynchronously");
}
this.BeginReadProcessOutput();
this.process.WaitForExit();
this.EndReadProcessOutput();
}
/// <summary>
/// Appends a line of output to the specified builder and to the combined output.
/// </summary>
/// <param name="builder">The target builder.</param>
/// <param name="line">The line of output.</param>
private void AppendLine(StringBuilder builder, string line)
{
builder.AppendLine(line);
lock (this.combinedOutputLock)
{
this.combinedOutputBuilder.AppendLine(line);
}
}
/// <summary>
/// Checks that the process is running.
/// </summary>
private void CheckProcessRunning()
{
// process.Handle will itself throw an InvalidOperationException if the process hasn't been started.
if (this.process.HasExited)
{
throw new InvalidOperationException("Process has exited");
}
}
/// <summary>
/// Handles the ErrorDataReceived event on the monitored process.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Diagnostics.DataReceivedEventArgs"/> instance containing the event data.</param>
private void ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
this.AppendLine(this.errorOutputBuilder, e.Data);
}
}
/// <summary>
/// Handles the OutputDataReceived event on the monitored process.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.Diagnostics.DataReceivedEventArgs"/> instance containing the event data.</param>
private void OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
this.AppendLine(this.standardOutputBuilder, e.Data);
}
}
}
下面是它的用法的例子:
var batchFile = Path.Combine(this.TestContext.TestSupportFileDir, "WriteToStandardError.bat");
var process = StartProcess(batchFile);
var reader = new ProcessOutputReader(process);
reader.ReadProcessOutput();
process.WaitForExit();
答
如果你感到快乐拿起在最后的输出(即你不需要作为进程运行,以显示它),只需使用CMD.EXE重定向它:
Process.Start("cmd.exe /c my.bat > my.log").WaitForExit();
把一个破发点线'sw.Write(sr.ReadToEnd());',看看是否过线完成执行并进入下一个循环 – 2011-02-28 16:38:11