1. ホーム
  2. c#

[解決済み】ProcessStartInfoが "WaitForExit "でハングアップ?なぜですか?

2022-05-09 09:23:57

質問

次のようなコードがあります。

info = new System.Diagnostics.ProcessStartInfo("TheProgram.exe", String.Join(" ", args));
info.CreateNoWindow = true;
info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
System.Diagnostics.Process p = System.Diagnostics.Process.Start(info);
p.WaitForExit();
Console.WriteLine(p.StandardOutput.ReadToEnd()); //need the StandardOutput contents

私が起動しているプロセスからの出力は7MB程度であることが分かっています。Windowsコンソールで実行しても問題なく動作します。しかし、プログラム上では、このプロセスは WaitForExit . また、このコードは3KBのような小さな出力ではハングしないことに注意してください。

もしかして、内部の StandardOutputProcessStartInfo は7MBをバッファリングできないのでしょうか?もしそうなら、代わりに何をすればいいのでしょうか?そうでない場合、私は何を間違えているのでしょうか?

解決方法は?

この問題は StandardOutput および StandardError の場合、内部バッファが満杯になることがあります。どのような順番で使用しても、問題が発生する可能性があります。

  • プロセスの終了を待って読み込む場合 StandardOutput に書き込もうとするとブロックされてしまうので、プロセスが終了することはありません。
  • から読み込むと StandardOutput を使用すると、ReadToEnd あなたの を閉じない場合、プロセスがブロックされる可能性があります。 StandardOutput (への書き込みがブロックされた場合など)。 StandardError ).

解決策は、非同期読み取りを使用して、バッファがいっぱいにならないようにすることです。デッドロックを回避し、すべての出力を集めるために StandardOutputStandardError とすると、こんなことができます。

編集部:この後の回答を参照してください。 ObjectDisposedException タイムアウトが発生した場合

using (Process process = new Process())
{
    process.StartInfo.FileName = filename;
    process.StartInfo.Arguments = arguments;
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;

    StringBuilder output = new StringBuilder();
    StringBuilder error = new StringBuilder();

    using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
    using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
    {
        process.OutputDataReceived += (sender, e) => {
            if (e.Data == null)
            {
                outputWaitHandle.Set();
            }
            else
            {
                output.AppendLine(e.Data);
            }
        };
        process.ErrorDataReceived += (sender, e) =>
        {
            if (e.Data == null)
            {
                errorWaitHandle.Set();
            }
            else
            {
                error.AppendLine(e.Data);
            }
        };

        process.Start();

        process.BeginOutputReadLine();
        process.BeginErrorReadLine();

        if (process.WaitForExit(timeout) &&
            outputWaitHandle.WaitOne(timeout) &&
            errorWaitHandle.WaitOne(timeout))
        {
            // Process completed. Check process.ExitCode here.
        }
        else
        {
            // Timed out.
        }
    }
}