ShellExecuteExに30秒かかる
O2Handler 0.3にて、ShellExecuteExの実行に30秒もかかるという現象が発生した。
詳しい状況は、
- たまに発生する。
- 30秒かかっているが、その間のスレッド時間は0秒。
- 発生すると別アプリケーションのShellExecuteExも遅くなり、最初のShellExecuteEx完了から30秒たつと実行が完了する。
- 遅くなった別アプリケーションのShellExecuteExはO2Handlerを強制終了した瞬間に完了する。
これらの状況から、ShellExecuteが送信するブロードキャストメッセージをO2Handlerが処理していないのではないかと予想した。
O2Handler3ではワーカースレッドからイベントの通知を受けるために、メッセージループにはGetMessageではなく、PeekMessageとMsgWaitForMultipleObjectsを使用していたので、これが原因かもしれない。
そこで、MsgWaitForMultipleObjectsをGetMessageに、SetEventをPostThreadMessage(hTreahd,WM_NULL,0,0)に置き換えたみたところ、この問題は発生しなくなった。
が、いろいろ試してみたところ、わけのわからない状況となってきた。
- エクスプローラ以外のアプリケーションの実行は遅くならない。
- 他のアプリケーションでShellExecuteExを使用すると、最初に遅くなったアプリケーションを含め、全てのShellExecuteExが即完了することがある。
- 他のアプリケーションでShellExecuteExが遅くならないことがある。
- 上記の修正を行ったO2Handlerと修正前のO2Handlerを同時に使用すると、修正後のO2Handlerでも問題が起こることがある。
結論
答えはMSDNに書いてあった。
ワーカースレッドからShellExecuteExを呼び出しているにも関わらず、WaitForSingleObjectを呼び出していたのが原因だった。
- まず、ワーカースレッドAからShellExecuteExを呼び出す。
- ShellExecuteEx正常終了。ただし、ShellExecuteExが内部で使用したウィンドウはそのまま残っている。(多分、再利用の為だろう)
- ワーカースレッドAはWaitForSingleObjectで休眠。
- ワーカースレッドBからShellExecuteExを呼び出し。
- ワーカースレッドAのShellExecuteEx作業用ウィンドウにメッセージをブロードキャスト送信。
- だが、WaitForSingleObjectで寝ているので反応が無い。
- 30秒経過したらあきらめる。
こんな感じだ。
MSDNに書いてあるように、WaitForSingleObjectをMsgWaitForMultipleObjectsExで置き換え、ワーカースレッドでもメッセージを処理することで問題は解決した。