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で置き換え、ワーカースレッドでもメッセージを処理することで問題は解決した。