我的代码中有一个部分,我调用批处理文件并给它一些参数。此批处理文件调用另一个批处理文件,依此类推。整个过程大约需要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());
}
此代码基本上使其成为第一个批处理文件之一的一个微不足道的部分并停在那里。批处理文件运行不到一分钟。我不明白为什么会这样。如果标准输出没有被重定向并且创建了一个窗口但是如果窗口被隐藏并且标准输出被重定向则什么都不做呢?它运行得很好?
答案 0 :(得分:1)
最可能的原因是批处理文件正在写入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();
答案 1 :(得分:1)
如果您很乐意在结尾处获取输出(即您不需要在进程运行时显示它),只需使用CMD.EXE重定向它:
Process.Start("cmd.exe /c my.bat > my.log").WaitForExit();