【C#】起動した外部プロセスの終了を待機する

目次

概要

外部アプリケーションやコマンドを Process.Start で起動した後、その処理が完全に完了するまでプログラムの実行を一時停止する方法です。 バックグラウンドではなく、同期的に処理を行いたい場合(例:ファイルの解凍が終わってから読み込む、バッチ処理を順番に実行するなど)に使用します。

仕様(入出力)

  • 入力
    • 実行するプロセス(Process オブジェクト)
  • 出力
    • 待機完了後のプロセス終了コード(ExitCode
  • 動作
    • 対象のプロセスが終了するまで、呼び出し元のスレッドをブロック(停止)します。

基本の使い方

プロセスインスタンスに対して WaitForExit() メソッドを呼び出します。

using System.Diagnostics;

// メモ帳を起動
var proc = Process.Start("notepad.exe");

// メモ帳が閉じられるまでここで待機
proc.WaitForExit();

// 終了後の処理
Console.WriteLine("メモ帳が閉じられました。");

コード全文

Windowsの ping コマンドを実行し、通信テストが完了するまで待機してから、コマンドの成功・失敗(終了コード)を判定するコンソールアプリケーションです。

using System;
using System.Diagnostics;

class Program
{
    static void Main()
    {
        Console.WriteLine("--- 外部プロセス起動 ---");

        // 1. プロセスの起動設定
        // localhost (127.0.0.1) に3回 ping を打つコマンド
        // Windowsの場合は "-n", Linux/macOSの場合は "-c" を使用する
        string fileName = "ping";
        string arguments = "127.0.0.1 -n 3";

        try
        {
            // Process.Startでコマンドを実行
            using (Process proc = Process.Start(fileName, arguments))
            {
                if (proc != null)
                {
                    Console.WriteLine("コマンド実行中...終了を待機します。");

                    // 2. プロセスの終了を待機
                    // このメソッドを呼ぶと、pingコマンドが終わるまで次の行に進まない
                    proc.WaitForExit();

                    // 3. 結果の確認
                    // ExitCode: 0 は通常、正常終了を表す
                    Console.WriteLine("-----------------------------");
                    Console.WriteLine($"プロセスは終了しました。");
                    Console.WriteLine($"終了コード (ExitCode): {proc.ExitCode}");

                    if (proc.ExitCode == 0)
                    {
                        Console.WriteLine("結果: 成功");
                    }
                    else
                    {
                        Console.WriteLine("結果: 失敗またはエラー");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"起動エラー: {ex.Message}");
        }
    }
}

実行結果例

--- 外部プロセス起動 ---
コマンド実行中...終了を待機します。

127.0.0.1 に ping を送信しています 32 バイトのデータ:
127.0.0.1 からの応答: バイト数 =32 時間 <1ms TTL=128
127.0.0.1 からの応答: バイト数 =32 時間 <1ms TTL=128
127.0.0.1 からの応答: バイト数 =32 時間 <1ms TTL=128

127.0.0.1 の Ping 統計:
    パケット数: 送信 = 3、受信 = 3、損失 = 0 (0% の損失)、
-----------------------------
プロセスは終了しました。
終了コード (ExitCode): 0
結果: 成功

カスタムポイント

  • タイムアウトの設定
    • proc.WaitForExit(5000); のようにミリ秒を指定することで、最大待ち時間を設定できます。指定時間内に終了しなかった場合は false が返るため、プロセスを強制終了(Kill)するなどの対処が可能です。
  • 非同期待機 (.NET 5以降)
    • WaitForExitAsync() を使用することで、メインスレッドをフリーズさせずに(await を使って)終了を待つことができます。GUIアプリではこちらが必須です。

注意点

  1. デッドロックの回避
    • 標準出力や標準エラー出力をリダイレクト(RedirectStandardOutput = true)している場合、単純に WaitForExit() だけを呼ぶとバッファが溢れてデッドロック(処理が永遠に止まる)することがあります。その場合は非同期読み取りを行うか、読み取り処理を完了させてから待機する必要があります。
  2. UIスレッドでの使用
    • Windows FormsやWPFのボタン処理などで WaitForExit() を呼ぶと、アプリ全体がフリーズして「応答なし」になります。UIを持つアプリでは必ず非同期処理 (Task.RunWaitForExitAsync) を検討してください。

応用

タイムアウト付きの待機処理

指定時間内に終わらない場合はプロセスを強制終了させる堅牢な実装例です。

using (Process p = Process.Start("notepad.exe"))
{
    // 10秒待機
    if (p.WaitForExit(10000))
    {
        Console.WriteLine("正常に終了しました。");
    }
    else
    {
        Console.WriteLine("タイムアウトしました。プロセスを強制終了します。");
        p.Kill(); // 強制終了
    }
}

まとめ

Process.WaitForExit は、バッチ処理や連続したコマンド実行において、順序正しく処理を進めるための必須メソッドです。外部ツールの実行結果(終了コード)を判定して次の処理を分岐させる場合に極めて有効ですが、GUIアプリケーションで使用する際はフリーズを防ぐためにタイムアウト設定や非同期メソッドへの置き換えを行う配慮が必要です。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次