1

コードに、バッチ ファイルを呼び出して特定の引数を指定するセクションがあります。このバッチ ファイルは、別のバッチ ファイルなどを呼び出します。プロセス全体が完了するまでに約 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());
}

このコードは基本的に、最初のバッチ ファイルの 1 つの些細な部分になり、そこで停止します。バッチ ファイルの実行時間は 1 分未満です。なぜこれが起こっているのかわかりません。標準出力がリダイレクトされずにウィンドウが作成された場合は完全に実行されますが、ウィンドウが非表示で標準出力がリダイレクトされた場合は何もしませんか?

4

2 に答える 2

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();
于 2011-02-28T16:51:06.403 に答える
1

最後に出力を取得してもよければ (つまり、プロセスの実行時に出力を表示する必要がない場合)、単純に CMD.EXE を使用してリダイレクトします。

Process.Start("cmd.exe /c my.bat > my.log").WaitForExit();
于 2011-02-28T16:53:10.773 に答える