モードレスダイアログを使って終了を待つ
サンプルを動かしてみて分かるように、スレッドに切り出しても、待機してしまったのでは「(応答なし)」を解決できません。まずは、前編最後の手法と同じモードレスダイアログを利用した方法を使って待機します。前編とは異なり処理ループが無くなっているので、全体の構造が少し変わっています。
#include <process.h> void ThreadBusyLoopModeless( HWND hwnd, DWORD dwTime ) { // ダイアログ作成 HWND hwndDlg = NULL; if( hwnd != NULL ){ EnableWindow( hwnd, FALSE ); hwndDlg = CreateDialog( hInst, MAKEINTRESOURCE(IDD_WAIT), hwnd, DlgProc_Simple); if( hwndDlg != NULL ){ ShowWindow( hwndDlg, SW_SHOWNORMAL ); UpdateWindow( hwndDlg ); } } // 別スレッド化した時間のかかる処理 { unsigned thrdaddr; HANDLE hThread = reinterpret_cast<HANDLE>( _beginthreadex( NULL, 0, BusyLoopThProcEx, &dwTime, 0, &thrdaddr ) ); if( hThread != NULL ){ bool bLoop = true; do{ DWORD dwResult = MsgWaitForMultipleObjects( 1, &hThread, FALSE, INFINITE, QS_ALLINPUT ); switch( dwResult ){ case WAIT_OBJECT_0: // スレッド終了を検出 bLoop = false; break; case WAIT_OBJECT_0+1: // メッセージを処理(PeekMessage が FALSEになるまでメッセージを処理する必要がある) PumpMessage(); break; } }while( bLoop ); CloseHandle( hThread ); } } // ダイアログ破棄 if( hwndDlg != NULL ){ ShowWindow( hwndDlg, SW_HIDE ); DestroyWindow( hwndDlg ); } if( hwnd != NULL ){ EnableWindow( hwnd, TRUE ); } }
サンプルを動かしてみると分かりますが、前編とは異なり反応の悪さはなくなったと思います。改めて見てみるとメインスレッド側の処理は、特別なことをしてるわけではありません。単にモードレスダイアログをモーダルダイアログのように見せかけて、そこで終了を待つという形に落ち着いています。これであれば、何も自分で煩雑な処理を行わずとも、最初からモーダルダイアログにしてしまえばもっとすっきりしたコードになるのではないでしょうか。
モーダルダイアログを利用して待機する
モーダルダイアログはモードレスダイアログとは異なり、オーナーウィンドウを自動的に無効にしてくれるという特典があります。モードレスダイアログでは自前のコードで同様のことを行いましたが、OSによっては、処理終了時にアクティブにならないなどいくつかの不具合に見える現象があります。モーダルダイアログを利用している場合はこのようなことは発生しませんので、モーダルダイアログに切り替えることができれば、余計な作業を減らせます。
また、モーダルダイアログはモードレスダイアログとは異なり逐次処理ができません。そのため、WM_INITDIALOGメッセージで、スレッドを作成するように切り替えます。モーダルダイアログは呼び出し元から情報を与えることができないと思っている人が多いようですが、DialogBoxParam APIというWM_INITDIALOGのLPARAMに情報を渡せるAPIがあるため、、これを利用してスレッド情報も渡します。サンプルでは、スレッド関数までは外に切り出していませんが、より汎用的なものにするのであれば、スレッド関数も外部から渡すようにすることで、汎用的な待機ダイアログにすることも可能です。この辺りは、実務の状況に合わせて適切に作り変えてもらえればと思います。
class CThreadParamDlg { public: CThreadParamDlg( DWORD dwTime ) : m_hwndDlg( NULL ) , m_workTime( dwTime ) { } public: HWND m_hwndDlg; DWORD m_workTime; }; void _cdecl BusyLoopThProcDlg( void* param ) { CThreadParamDlg* pParam = static_cast( param ); DWORD dwStart = GetTickCount(); while( (GetTickCount()-dwStart) < pParam->m_workTime ); PostMessage( pParam->m_hwndDlg, WM_COMMAND, MAKEWPARAM( IDOK, 0 ), 0 ); // 処理終了はOKメニュー風に通知 } INT_PTR CALLBACK DlgProc_Modal(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch( message ){ case WM_INITDIALOG: { CSimpleWaitCursor wait; CThreadParamDlg* pParam = reinterpret_cast( lParam ); pParam->m_hwndDlg = hDlg; if( _beginthread( BusyLoopThProcDlg, 0, pParam ) == -1 ){ EndDialog( hDlg, IDABORT ); } } return TRUE; case WM_COMMAND: if( LOWORD(wParam) == IDOK ){ EndDialog( hDlg, LOWORD(wParam) ); return TRUE; } break; } return FALSE; } void ThreadBusyLoopModal( HWND hwnd, DWORD dwTime ) { CThreadParamDlg param( dwTime ); DialogBoxParam( hInst, MAKEINTRESOURCE(IDD_WAIT), hwnd, DlgProc_Modal, reinterpret_cast<LPARAM>( ¶m ) ); }
動作そのものは、モードレスダイアログ版と変わりありませんが、モーダルダイアログにすることで、全体的にすっきりしたと思います。