NightElf00
@NightElf00

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

C# タイムアウト処理について

解決したいこと

言語C#
ツール1: Windows Forms, .exeファイル
ツール2: 他人作成のため詳細不明。.exeファイル、戻り値true/false のみ
開発環境:Visual Studio 2019

ツール1起動し、ボタンをクリックすると、ツール2(.exe)を起動します。
ツール2 が起動して、ずっと終わらないことを避けるため、10秒以内にツール2 が終了できなければタイムアウトとし、ツール2 を強制終了させます。

ツール2 を手動で起動する場合、3秒ぐらいで終了するので、タイムアウト時間を10秒に設定した場合、タイムアウトにならず正常終了になります。
ほんとにタイムアウト処理が機能しているかを検証したいです。ツール2の実行時間が3秒ぐらいなので、タイムアウト時間を0.1秒に設定すれば、必ずタイムアウト処理に入ると思います。

発生している問題・エラー

タイムアウト処理を0.1sに設定しても、WaitforExit(100)の処理結果がtrueになりタイムアウト処理には入りません。
コードが間違っているか、それともそもそも考え方が間違っているかがわからないので、質問いたしました。

該当するソースコード

try
{
    bool bMetRet = false;   // メソッド戻り値
    string sStdOutput = ""; // 標準出力
    string sStdError = "";  // エラー出力  

    // メソッド名を設定する
    sMethodName = String.Join(".", GetType().Name, MethodBase.GetCurrentMethod().Name);

    Program.StartLog(sMethodName);

    System.Diagnostics.Process p = new System.Diagnostics.Process();

    // 起動するアプリケーションを設定する
    p.StartInfo.FileName = @"C:\test.exe";

    // コマンドライン引数を設定する
    p.StartInfo.Arguments = "test1 test2 test3";

    // 出力を設定する
    p.StartInfo.RedirectStandardOutput = true;
    p.StartInfo.RedirectStandardError = true;

    // エンコーディングを設定する
    p.StartInfo.StandardOutputEncoding = Encoding.UTF8;
    p.StartInfo.StandardErrorEncoding = Encoding.UTF8;

    // シェル機能を使用しない
    p.StartInfo.UseShellExecute = false;

    // 新しいウィンドウを開かない
    p.StartInfo.CreateNoWindow = false;

    // プロセスを起動する
    bMetRet = p.Start();

    // ツール実行結果が失敗の場合
    if (bMetRet != true)
    {
        // メッセージウィンドウ表示
        DialogResult Result = MessageBox.Show(
            "ツール実行失敗",
            "エラー",
            MessageBoxButtons.OK,
            MessageBoxIcon.Error
        );

        return bRet;
    }

    // 標準出力を取得する
    sStdOutput = p.StandardOutput.ReadToEnd();

    // エラー出力を取得する
    sStdError = p.StandardError.ReadToEnd();


    //-----------タイムアウト処理開始------------
    //p.WaitForExit(0);
    
    // ツール実行プロセスが終了まで10秒待つ
    // if (p.WaitForExit(10000) == false)
    if (p.WaitForExit(100) == false)
    {
        // 強制に終了する
        p.Kill();
        
        // タイムアウトになった場合、メッセージウィンドウ表示
        DialogResult Result = MessageBox.Show(
            "タイムアウト",
            "エラー",
            MessageBoxButtons.OK,
            MessageBoxIcon.Error
        );

        return bRet;
    }
    

    //-----------タイムアウト処理終了------------

自分で試したこと

上記方法のみ、ほかの方法があれば教えていただきたいです。

参考

WaitForExit(Int32) の内容

0

5Answer

Comments

  1. @NightElf00

    Questioner

    確認しまして、確かに子プロセス処理終了を待っています。解決できました。
    ありがとうございました。

WaitForExitの行にブレークポイント仕掛けてデバッグ実行すればすぐわかると思いますが、標準出力の取得でブロッキングしているんじゃないでしょうか。(exeが終了するまで、WaitForExitまで到達していない)
標準出力の取得をWaitForExitの後に移動してみてください。

1Like

if (p.WaitForExit(100) == false)

これが間違いでは?

上記サンプルは別の方法でキャッチしてます。
p.WaitForExit(100);
if (p.HasExited)

私は起動したwindowのハンドルを取得して、別の非同期プロセスで監視して、killしてました。

1Like

Comments

  1. @NightElf00

    Questioner

    よく確認しまして、確かにこの処理はこの場所では不適切でした。
    ありがとうございました。:smiley:

何を作っているか(Windows Forms? WPF? ASP.NET Web アプリ? その他?)と開発環境(OS, Visual Studio のバージョン・エディション、.NET Framework or .NET Core/.NET どっちなのかとそのバージョンなど)を書いてください。

それから、そもそも何がしたいのかを説明することはできますか? そうしてもらえると、たとえばその「何がしたいのか」を実現するために、今の「別アプリケーション」で処理していることを、別プロセスではなく非同期アプリで別スレッドで実行するように変更し、CancellationToken を引数に渡せる非同期メソッドで処理を行い、CancellationTokenSource.CancelAfter メソッドでタイムアウトを設定するというような案が出てくるかもしれません。

0Like

結論、タイムアウト処理をp.start()の直後に移動すればうまく動いてくれた。

(省略)
// プロセスを起動する
    bMetRet = p.Start();

//-----------タイムアウト処理開始------------
    
    // ツール実行プロセスが終了まで10秒待つ
    if (p.WaitForExit(1000) == false)
    {
        // 強制に終了する
        p.Kill();
        
        // タイムアウトになった場合、メッセージウィンドウ表示
        DialogResult Result = MessageBox.Show(
            "タイムアウト",
            "エラー",
            MessageBoxButtons.OK,
            MessageBoxIcon.Error
        );

        return bRet;
    }
(省略)

回答いただいたかた、ありがとうございました。

0Like

Comments

  1. パイプのバッファは有限かつ4KB程度と小さい(環境依存でもっと小さいかも)と思うので、子プロセスの出力が多くなると途中で詰まって止まる可能性があります。すると正常系なのにタイムアウトしてキルで終了する形になるかもしれません。

    なので、普通は後で読むというような形にはしません。手で動かしてその場限りとかならいいのですが、タイムアウトまで書いてるような手堅いプログラムで、後で読む実装をしてたら、人によってはバグの範疇に含まれると思います(私もバグという認識)。

  2. @NightElf00

    Questioner

    パイプのバッファは4KB程度のことは知りませんでした。知識不足です。今回は大量に出力する場合もあるので、それならは確かにバグになります。
    勉強になりました。
    ありがとうございます。:thumbsup:

Your answer might help someone💌