読者です 読者をやめる 読者になる 読者になる

Shell_NotifyIcon のタイムアウトと UI の安定性

Vista

SP1の導入でより安定性が増すWindows Vista

(中略)

例えば、Windows UIの安定性は格段に上がった。具体的に言えば、Windowsではタスクバーの右側に“通知領域”と呼ばれる動作中のツールなどの状態を表示する領域があるが、Vista SP1導入前はアイコンが出たり出なかったりという問題がよく発生していた。これはHDD上に何らかのエラーがある時によく発生し、スキャンディスクをかけると解決することが多いのだが、SP1をインストールした後にはそうした問題がほとんど発生しなくなった。また、全体としての性能は明らかに向上しており、以前よりも快適に利用できるようになった。

(訂正)Shell_NotifyIcon タイムアウト時の挙動は,FALSE (失敗) が返ってきたときに GetLastError で原因を知るというのが正しいです.本記事の最初のバージョンでは Shell_NotifyIcon の戻り値で判断するように書かれていましたので,修正してあります.
この「出たり出なかったり」という挙動ですが,多くの場合はアプリケーションが Shell_NotifyIcon API のエラーを正しく取り扱わないことで発生します *1
Shell_NotifyIcon は,SendMessageTimeout を利用してシェルウィンドウにメッセージを送信します.SendMessageTimeout は同期呼び出し型の API なので,受け手のシェルウィンドウのメッセージポンプが応答を返すまで待機します.SendMessage API と同じく,この間は新着送信メッセージへの応答以外の動作は行いません.もし,Shell_NotifyIcon がタイムアウトを設定していなければどうなるでしょうか?
もしタイムアウトがなければ,シェルがハングしていたり,高負荷でシェルが十分な CPU 時間を得られている状況での Shell_NotifyIcon API の使用は死を意味します.シェルが応答を返さないため,通知領域にアイコンを設定しようとしたアプリケーションのスレッドまで道連れになってハングアップすることになります.
実際には,シェルが忙しいときに Shell_NotifyIcon を呼び出すと,数秒程度で失敗を示す FALSE が返り,アプリケーションは制御を取り戻すことができます.ここで GetLastError を呼び出して,エラーコード 1460 (timeout) をチェックすることで,タイムアウトによって Shell_NotifyIcon が失敗したことが分かります.
ここでアプリケーションがタイムアウトエラー発生時に再度アイコンの登録を行わないと,「アイコンが出なかったり」ということが起こりえます.Windows Vista の起動直後にログインしたときは,ディスクアクセスの競合によってタイムアウトが発生しやすい状況に陥ります.スタートアップアプリケーションが影響を受けやすいのはこのためです.
もっとも,Vista で Shell_NotifyIcon タイムアウト後に再度設定しようとしたら今度はエラー 87 (parameters incorrect) が返ってきた という人もいるので,実際に Vista での取り扱いに何か問題があったのかもしれません.
SendMessageTimeout は,相手にメッセージが届いていないのか,それとも相手には届いたものの返信を受け取り損ねたかを区別できないので,Shell_NotifyIcon によるアイコン設定手続きの途中まで成功している可能性を考慮する必要があります.推奨されるリトライ方法については,KB418138 通知領域にアイコンが登録されないことがある を参照してください.



余談ですが,記事中“タスクトレイ”ではなく正式名称の“通知領域”*2 を使っているあたりはさすが.

*1:[http://support.microsoft.com/kb/835874/ja:title=KB835874 通知領域のアイコンが消えてしまうことがある], [http://support.microsoft.com/kb/418138/JA/:title=KB418138 通知領域にアイコンが登録されないことがある]

*2:[http://blogs.msdn.com/oldnewthing/archive/2003/09/10/54831.aspx:title=Why do some people call the taskbar the "tray"?]