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後にUnregister-ObjectEventをしているここにあります。
https://gist.github.com/guitarrapc/0ea30c730ea77efa0217#file-invaliddispose-ps1-L80-L81

PSEventJob のDispose() に成功する例
先ほどの例をわずかに変えるだけです。
変更点は、.Dispose()前に、.Dispose()メソッドを呼び出しただけです。
https://gist.github.com/guitarrapc/dd05e671eea67059acb1#file-invoke-process-ps1-L80-L81

これだけで、例外が発生しなくなります。
どのような状況で発生しえるのか
MSDNで、.StopJob()メソッドをみてみましょう。
Stops the action that is performed by the job. This method is introduced in Windows PowerShell 2.0.
そして、 .Dispose()は、通常の .NETでのDisposeパターンにのっとって実装しているとあります。
https://msdn.microsoft.com/en-us/library/system.management.automation.job.dispose(v=vs.85).aspx
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プロパティやFinishedが完了となっていないことがわかります。まだ、ジョブがうごいています。
https://msdn.microsoft.com/en-us/library/system.management.automation.job.finished(v=vs.85).aspx
今回の例では、プロセスが吐いた出力イベントを非同期に取得していました。が、出力が多すぎる場合にプロセス完了時、まとめてイベントを破棄しようとしても、イベント処理が完了していませんでした。
この例では、プロセス完了で出力を切りたかったので、StopJob()をStopJob()前に呼び出すことで、リソースを破棄できるようにしています。
まとめ
PowerShellでイベント難しいです..。
この症状は、GitContinuousPullモジュールを動かしていて稀にPowerShell.exeが死ぬため気づきました。
ver.1.6.4で修正済みです。
https://github.com/guitarrapc/GitContinuousPull/releases/tag/ver.1.6.4