処理してほしくないメッセージまで処理されてしまう
ビジーループV2動作中に、いろいろなウィンドウ操作を試してみたでしょうか? では、ビジーループV2の動作中にウィンドウを閉じてみてください。閉じた後どうなるかを目視できるよう、タスクマネージャでWaitSample.exeの実行状況も監視してください。時間が経つとCPUの利用率が0になり、処理が終了します。しかし、プログラムは終了しません。これはどういうことなのでしょうか。
プログラムが終了しないということは、WinMainにあるメッセージループから抜け出せない状態であることを意味します。メッセージループから抜け出す条件はなんでしょうか。メッセージループは、次のようになっています。
// メイン メッセージ ループ: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
GetMessageがFALSEを返すまでひたすらループするということがここから分かります。リファレンスによると、GetMessageは、WM_QUITメッセージを取得すると0を返すとあります。要するにWM_QUITがメインメッセージループで取得されないされない限り、プログラムが終了しないということになります。
サンプルアプリは、ウィンドウが破棄されるタイミング(WM_DESTORYを受け取ったタイミング)で、WM_QUITメッセージを発行します。ビジーループV2動作中にウィンドウを閉じてしまうため、WM_QUITがメインメッセージループではない場所で処理されてしまい、結果的にWM_QUITメッセージを検出することができず、プログラムが終了できなくなってしまったようです。
ウィンドウを閉じさせないためには?
ビジーループ中にウィンドウの操作ができるのは嬉しい副作用ですが、ウィンドウが閉じられてしまうのは不本意です。対応策はいくつか考えられますが、実務の場合、時間のかかる処理中だからと言って、アプリを終わるタイミングで、ということはあまり考えられません。むしろウィンドウを閉じられる必要がないほうが多いのではないでしょうか。
では、ウィンドウを閉じることができないようにするにはどうすればいいのでしょうか。ウィンドウを閉じることができないようにするということは、ウィンドウを操作できないようにするということと同じ意味を持ちます。最初のBusyLoopは意図していないとはいえ、一切操作ができていませんでした。それと同じような状況を擬似的にでも実現できれば、問題は解決できそうです。ウィンドウを操作できないようにするには、EnableWindow APIを利用してウィンドウの有効・無効を切り替えることで実現できます。プログラム的にはいたって単純で、ループの最初でEnableWindow( hwnd, FALSE );を呼び、ループから抜けたところでEnableWindow( hwnd, TRUE );を呼び出すだけです。さっそくコードを見て試してみましょう(体験メニュービジーループV3)。
void BusyLoop3( HWND hwnd, DWORD dwTime ) { if( hwnd != NULL ){ EnableWindow( hwnd, FALSE ); } while( dwTime > 0 ){ PumpMessage(); // メッセージを処理してから待機カーソルにする DWORD dwWait = ( dwTime > 1000 ) ? 1000 : dwTime ; CSimpleWaitCursor waitCursor; DWORD dwStart = GetTickCount(); while( (GetTickCount()-dwStart) < dwWait ); dwTime -= dwWait; } if( hwnd != NULL ){ EnableWindow( hwnd, TRUE ); } }
いかがでしょうか。一見するとあまり問題は感じないかもしれません。ユーザーからみても、何やら作業を行っているということが認識できる状況になっています。しかし、まずメイン画面が無効状態であるにもかかわらず、ダイアログも何も出ていないのは何だか妙な感じです。まるでXPまでの頃の、応答なしになり始めた状態のような感じで、得体のしれない不安感がぬぐえません。数秒程度ならこれでもいいかもしれませんが、あまりお勧めできる状況とは言えないようです。