PowerShell を使っていると、突然の PowerShell.exe の死にあうことがあります。
今回は、Register-ObjectEvent
の Dispose に関してです。
目次
PSEventJob のDispose() に失敗する例
さて、以下の指定したプロセスを実行する関数があります。Start-Process では困ることが多いので重宝するのです。
こんな感じで呼び出します。
Invoke-Process -FileName 'git' -Arguments "pull" -WorkingDirectory "c:\GitHub\Repogitory"
しかし、これで git を呼び出すと Unhandled Error が起こって、例外で死ぬことなく PowerShell.exe ホストごと死ぬ場合があることに気づきました。さいてーです。
原因は、Unregister-ObjectEvent
後に .Dispose()
をしているここにあります。
https://gist.github.com/guitarrapc/0ea30c730ea77efa0217#file-invaliddispose-ps1-L80-L81
PSEventJob のDispose() に成功する例
先ほどの例をわずかに変えるだけです。
変更点は、.Dispose()
前に、StopJob()
メソッドを呼び出しただけです。
https://gist.github.com/guitarrapc/dd05e671eea67059acb1#file-invoke-process-ps1-L80-L81
これだけで、例外が発生しなくなります。
どういう状況で発生しえるのか
MSDN で、.StopJob()
メソッドをみてみましょう。
PSEventJob.StopJob Method (System.Management.Automation) | Microsoft Learn
Stops the action that is performed by the job. This method is introduced in Windows PowerShell 2.0.
そして、 .Dispose()
は、通常の .NET での Disposeパターンにのっとって実装しているとあります。
Job.Dispose Method (System.Management.Automation) | Microsoft Learn
Releases the resources that are used by the Job object. These methods implement the Dispose pattern used to release managed and unmanaged resources. This method is introduced in Windows PowerShell 2.0. For an explanation of the Dispose pattern, see Implementing a Dispose Method.
実は例外が発生する場合は、Finished
プロパティ や JobStateInfo
が完了となっていないことがわかります。まだ、ジョブがうごいています。
Job.Finished Property (System.Management.Automation) | Microsoft Learn
今回の例では、プロセスが吐いた出力イベントを非同期に取得していました。が、出力が多すぎる場合にプロセス完了時にまとめてイベントを破棄しようとしても、イベント処理が完了していませんでした。
この例では、プロセス完了で出力を切りたかったので、StopJob()
を Dispose()
前に呼び出すことで、リソースを破棄できるようにしています。
まとめ
PowerShell でイベント難しいです..。
この症状は、GitContinuousPull
モジュールを動かしていて稀に PowerShell.exe が死ぬため気づきました。
ver.1.6.4 で修正済みです。