web-dev-qa-db-ja.com

プロセスが実行されているにもかかわらず、Process.HasExitedがtrueを返しますか?

プロセスがまだ実行されている場合でも、_Process.HasExited_がtrueを返すことがあるのを観察しています。

以下のコードは、 "testprogram.exe"という名前のプロセスを開始し、終了するのを待ちます。問題は、例外がスローされることがあるということです。 HasExitedtrueを返しますが、プロセス自体はシステム内でまだ生きています-これはどうですか?

プログラムが終了する直前にログファイルに書き込むため、このログファイルが存在すること(プロセスが終了/終了したこと)を確認してから読み取る必要があります。存在を継続的に確認することはできません。

_// Create new process object
process = new Process();

// Setup event handlers
process.EnableRaisingEvents = true;
process.OutputDataReceived += OutputDataReceivedEvent;
process.ErrorDataReceived += ErrorDataReceivedEvent;
process.Exited += ProgramExitedEvent;

// Setup start info
ProcessStartInfo psi = new ProcessStartInfo
                           {
                               FileName = ExePath,
                               // Must be false to redirect IO
                               UseShellExecute = false,
                               RedirectStandardOutput = true,
                               RedirectStandardError = true,
                               Arguments = arguments
                           };

process.StartInfo = psi;

// Start the program
process.Start();

while (!process.HasExited)
    Thread.Sleep( 500 );

Process[] p = Process.GetProcessesByName( "testprogram" );

if ( p.Length != 0 )
    throw new Exception("Oh oh");
_

更新:ポーリングループの代わりにprocess.WaitForExit()で待機してみましたが、結果はまったく同じです。

加えて:上記のコードは、同様に「より明確な」問題を示すためだけのものでした。それを明確にするために;私の問題は、HasExitedをtrueに設定した後でも、Process.GetProcessesByName( "testprogram" );によってプロセスを保持できることではありません。

本当の問題は、私が外部で実行しているプログラムが、ファイルを(丁寧に)終了する直前に書き込むことです。私はHasExitedを使用してプロセスが終了したことを確認し、ファイルを読み取ることができることを確認しています(プロセスが終了したためです)が、HasExitedtrueを返すようですプログラムがファイルをまだディスクに書き込んでいない場合もあります。正確な問題を示すコード例を次に示します。

_// Start the program
process.Start();

while (!process.HasExited)
    Thread.Sleep( 500 );
// Could also be process.WaitForExit(), makes no difference to the result

// Now the process has quit, I can read the file it has exported
if ( !File.Exists( xmlFile ) )
{
    // But this exception is thrown occasionally, why?
    throw new Exception("xml file not found");
}
_
31
johnrl

私はこれが古い投稿であることを認識していますが、アプリが開く前にExitedイベントを実行しているアプリをなぜ探しているのかを調べるために、将来この問題を経験する人々にとって役立つかもしれないことがわかりました。

プロセスが開始されると、PIDが割り当てられます。 ユーザーが[ユーザーアカウント制御]ダイアログでプロンプトが表示され、[はい]を選択すると、プロセスが再開され、新しいPIDが割り当てられます。

私はこれを数時間座っていました。うまくいけば、誰かの時間を節約できます。

11
Dave

この方法を試すことをお勧めします:

process.Start();

while (!process.HasExited)
{
    // Discard cached information about the process.
    process.Refresh();

    // Just a little check!
    Console.WriteLine("Physical Memory Usage: " + process.WorkingSet64.ToString());

    Thread.Sleep(500);
}

foreach (Process current in Process.GetProcessesByName("testprogram"))
{
    if ((current.Id == process.Id) && !current.HasExited)
        throw new Exception("Oh oh!");
}

とにかく... HasExitedのMSDNページで、次のハイライトされたメモを読んでいます。

標準出力が非同期イベントハンドラーにリダイレクトされている場合、このプロパティがtrueを返すときに出力処理が完了しない可能性があります。非同期イベント処理が完了していることを確認するには、HasExitedを確認する前に、パラメーターを取らないWaitForExit()オーバーロードを呼び出します。

すべてをリダイレクトしているので、それは何らかの形で問題にリンクしている可能性があります。

8

したがって、問題の根本的な原因をさらに調査するには、 Process Monitor を使用して、実際に何が起こっているのかを確認する必要があります。起動して、外部プログラムと独自のツールを含め、何が起こったかを記録させてください。

ログ内で、外部ツールが出力ファイルに書き込む方法と、そのファイルを開く方法を確認できます。ただし、このログ内では、これらすべてのアクセスが発生する順序を確認できます。

最初に頭に浮かんだのは、Processクラスが嘘をついていないということです。そうすれば、プロセスは本当になくなってしまいます。そのため、現時点では、ファイルがまだ完全に利用可能ではないようです。これはOSの問題だと思います。ディスクに完全に書き込まれていないキャッシュ内にファイルの一部を保持しているためです。ツールは、ファイルハンドルをフラッシュせずに単に終了しました。

これを念頭に置いて、外部ツールがファイルを作成し、終了したことと、ファイルがフラッシュ/クローズされたことをログ内で確認する必要があります(OSによって[ログ内でこのポイントを見つけたら、フィルターを削除する可能性があります])。

したがって、私の仮定が正しい場合、根本的な原因は変更できない外部ツールの悪い動作であり、プロセスが終了した後に少しだけ待機し、タイムアウトがファイルをフラッシュするのに十分な長さであることを期待しています/ OSによって閉じられます(おそらく、ファイルが成功するまでタイムアウトでループしてファイルを開こうとします)。

4
Oliver

これは古い投稿ですが、誰かを助けることができるかもしれません。
Processクラスが予期しない動作をする可能性があります!プロセスが終了した場合、HasExitedはtrueを返しますorプロセスが管理者特権で実行され、プログラムにユーザー特権しかない場合。

4
Michael Plainer

最初に、 testprogramが独自のプロセスを生成せず、そのプロセスの終了を待たずに終了しないことを確認しますか?ここでは、ある種の競合状態を扱っているため、テストプログラムが重要になる場合があります。

私が作りたい第二のポイントはこれについてです-「私はこのログファイルが存在することを絶対に確認する必要があります」。まあ、そんなことはありません。チェックを行うと、ファイルはなくなります。これに対処する一般的な方法は、チェックではなく、ファイルに対して実行したいことを実行することです。先に進んで、それを読んで、例外をキャッチし、状況が不安定になり、何も変更したくない場合は、再試行してください。システムに複数のアクター(スレッドなど)がある場合、機能的なチェックアンドドゥはうまく機能しません。

ランダムなアイデアの束が続きます。

プロセスの完了に依存せずにFileSystemWatcherを使用してみましたか?

Process.Exitedイベントでファイルを読み取ってみて(存在するかどうかをチェックせず、代わりにacting)試してみると、さらに良くなりますか? [すべきではない]

システムは正常ですか?イベントログで疑わしいものはありますか?

本当に攻撃的なウイルス対策ポリシーが含まれる可能性がありますか?

(すべてのコードを見て、testprogramを調べなければ、多くを語ることはできません。)

4
Mikhail

2つの可能性があります。プロセスオブジェクトは、プロセスへの参照を保持し続けるため、終了しましたが、まだ削除されていません。または、実行中のプロセスの2番目のインスタンスがあります。また、プロセスIDを比較して確認する必要もあります。これを試して。

    ....

    // Start the program
    process.Start();


    while (!process.HasExited)
        Thread.Sleep( 500 );

    Process[] p = Process.GetProcessesByName( "testprogram" );
    if ( p.Length != 0 && p[0].Id == process.id && ! p[0].HasExited)
        throw new Exception("Oh oh");
2
John Knoeller

MSDNドキュメント _HasExitedのとおり。

ハンドルがプロセスに対して開いている場合、オペレーティングシステムは、プロセスが終了したときにプロセスメモリを解放しますが、ハンドル、終了コード、終了時間などのプロセスに関する管理情報は保持します。

おそらく関連はありませんが、注目に値します。

それが問題の1/10の時間だけであり、いずれにしてもプロセスが1秒後に消える場合は、HasExitedの使用方法に応じて、HasExitedチェックが機能した後、次のように遅延を追加してみてください。

_while (!process.HasExited)
    DoStuff();
Thread.Sleep(500);
Cleanup();
_

問題が解決しないかどうかを確認します。

個人的には、あらゆる種類のポーリングの代わりに常にExitedイベントハンドラーを使用し、_System.Diagnostics.Process_の単純なカスタムラッパーを使用して、スレッドセーフなどの処理を行い、CloseMainWindow()の後にWaitForExit(timeout)が続き、最後にKill()、ロギングなどが続き、問題は発生しませんでした。

1
Tanzelax

まず、ポーリングではなくProcess.WaitForExitの使用に問題がありますか?

とにかく、プロセスが使用可能な観点から終了することは技術的には可能ですが、ディスクキャッシュのフラッシュなどの処理を行っている間、プロセスはまだ短時間です。ログファイルは特に大きいですか(またはディスクの書き込みで大量に実行されている操作)。

1
tyranid

多分問題はテストプログラムにありますか?このコードはうまくフラッシュ/クローズなどしますか? testprogramがファイルをディスクに書き込む場合、私にはそのファイルが少なくとも利用可能である必要があります(空かどうか)

1
Roy

プロセスが終了したかどうかを確認する前にprocess_name.Refresh()を使用してください。 Refresh()は、プロセスに関連するキャッシュされた情報をすべてクリアします。

1
Mohammad Shakir

Webアプリケーションがあり、外部プログラム/プロセスがファイルを生成している(ディスクに書き込む)場合は、IISプロパティにセキュリティがない場合、そのフォルダーに書き込む権限があるかどうかを確認してください= IISユーザー、それが私の場合の理由でした。プロセスを受け取っていました。HasExited= trueですが、しばらくの間苦労した後、プロセスに生成されたファイルが完了しませんでした。ここで、プロセスはwrithingおよびprocess.Refresh()であり、Zarathosは上記から説明し、すべてが期待どおりに機能していました。

0
No Name