none
powershellで実行した外部アプリケーションが固まってしまう。 RRS feed

  • 質問

  • いつもお世話になっております。

    powershellにて外部プログラムを実行し、標準出力・エラーをリダイレクトするプログラムを作成しています。

    WaitForExit()メソッドを使用して外部プログラムの終了を待ち合わせているのですが、外部プログラムが途中で固まり、

    いつまでも外部プログラムが終了しない事象(※)が発生しました。

    ※プロセスは起動しているが、フリーズしているように見え(cpu使用率が0%だった)、戻り値を返却してこない。

    以下のサイトにて「子プロセスの出力によりパイプがいっぱいになっている」ことが原因の可能性があるらしいのですが、

    powershellではどのように対応すれば良いのかが分かっておりません。

    http://www.atmarkit.co.jp/fdotnet/dotnettips/805pipeasync/pipeasync.html


    外部プログラムが終了しない事象に対するpowershellの対応方法、或いはその事象が発生する原因が別にあるのであればご教授いただきたく思います。

    ----以下、プログラム----

    $strCmd = "C:\Windows\System32\calc.exe"
    $proc = [System.Diagnostics.Process];
    $pinfo = [System.Diagnostics.ProcessStartInfo];

    $pinfo = New-Object System.Diagnostics.ProcessStartInfo;

    $pinfo.FileName = $strCmd;         #プログラム名
    $pinfo.UseShellExecute = $false;   #標準出力を受け取る場合は Falseでないとだめ
    $pinfo.RedirectStandardOutput = $true;  #標準出力を受け取る
    $proc = [System.Diagnostics.Process]::Start($pinfo);  #コマンド実行
    $output = ""
    $output = $proc.StandardOutput.ReadToEnd();  #プログラムの標準出力
    $rc = $proc.exitcode
    $proc.WaitForExit();
    $proc.Close()
     

    2013年2月17日 7:19

回答

  • MSDNライブラリのSystem.Diagnostics.Process.StandardOutputプロパティの解説に、次のような記述があります。

    「同期読み取り操作により、StandardOutput ストリームから読み取る呼び出し元と、そのストリームに書き込む子プロセスの間に依存関係が発生します。この依存関係によってデッドロック状態が発生する場合があります(…中略…)
    標準出力および標準エラー ストリームの両方からすべてのテキストを読み取る場合にも類似の問題が発生します(…中略…)
    p.StandardOutput.ReadToEnd の後に p.StandardError.ReadToEnd を呼び出し、エラー ストリームが満杯になるまで子プロセスがテキストを書き込む場合、デッドロック状態が発生する可能性があります。」

    標準出力・エラーをリダイレクトするプログラムを作成されているとのことですが、ご質問のコードには標準エラーを取得する部分が見当たりません。
    私の見当違いかもしれませんが、もしかしたら標準出力と標準エラーを両方ともReadToEndメソッドで取得しようとしてデッドロックを起こしている、という可能性はないでしょうか。

    ご参考になれば幸いです。

    (追記)

    PowerShellでの標準出力の非同期読み取りの方法は、おそらくC#等と同じだと思います。
    例えば、Register-ObjectEventコマンドレットを使ってProcess.OutputDataReceivedイベントにスクリプトを設定し、その後Process.BeginOutputReadLineメソッドを呼び出せばよさそうな気がします。
    (具体例がなくて申し訳ありません。)


    2013年2月17日 9:36
  • Process を使わずに Job を使ってはダメですか?

    exitcode が必要な場合は $LastExitCode 変数に入りますので、それをスクリプトスコープから外に返してあげる必要があります。

    # 外部プログラムをバックグラウンドで実行
    $job = Start-Job { calc.exe }
    # 終了を待機
    Wait-Job $job
    # 結果を取得(標準出力)
    $job.ChildJobs[0].Output
    # 結果を取得(標準エラー出力)
    $job.ChildJobs[0].Error
    # 終了したプログラムを閉じる
    Remove-Job $job

    2013年2月23日 8:41

すべての返信

  • MSDNライブラリのSystem.Diagnostics.Process.StandardOutputプロパティの解説に、次のような記述があります。

    「同期読み取り操作により、StandardOutput ストリームから読み取る呼び出し元と、そのストリームに書き込む子プロセスの間に依存関係が発生します。この依存関係によってデッドロック状態が発生する場合があります(…中略…)
    標準出力および標準エラー ストリームの両方からすべてのテキストを読み取る場合にも類似の問題が発生します(…中略…)
    p.StandardOutput.ReadToEnd の後に p.StandardError.ReadToEnd を呼び出し、エラー ストリームが満杯になるまで子プロセスがテキストを書き込む場合、デッドロック状態が発生する可能性があります。」

    標準出力・エラーをリダイレクトするプログラムを作成されているとのことですが、ご質問のコードには標準エラーを取得する部分が見当たりません。
    私の見当違いかもしれませんが、もしかしたら標準出力と標準エラーを両方ともReadToEndメソッドで取得しようとしてデッドロックを起こしている、という可能性はないでしょうか。

    ご参考になれば幸いです。

    (追記)

    PowerShellでの標準出力の非同期読み取りの方法は、おそらくC#等と同じだと思います。
    例えば、Register-ObjectEventコマンドレットを使ってProcess.OutputDataReceivedイベントにスクリプトを設定し、その後Process.BeginOutputReadLineメソッドを呼び出せばよさそうな気がします。
    (具体例がなくて申し訳ありません。)


    2013年2月17日 9:36
  • Process を使わずに Job を使ってはダメですか?

    exitcode が必要な場合は $LastExitCode 変数に入りますので、それをスクリプトスコープから外に返してあげる必要があります。

    # 外部プログラムをバックグラウンドで実行
    $job = Start-Job { calc.exe }
    # 終了を待機
    Wait-Job $job
    # 結果を取得(標準出力)
    $job.ChildJobs[0].Output
    # 結果を取得(標準エラー出力)
    $job.ChildJobs[0].Error
    # 終了したプログラムを閉じる
    Remove-Job $job

    2013年2月23日 8:41