トップ回答者
メッセージループ中のDispatchMessageに時間がかかる

質問
-
ダイアログ入力後に処理を行うPGを作成したのですが、ダイアログ入力可になるまでに20秒ほど時間がかかる場合があります。
ログ出力を行う等して調査したところ、メッセージループにてTranslateMessageした後のDispatchMessageに約20秒ほど時間がかかっていました。関係する部分のソースは以下の通りです。
static TCHAR szClassName1[] = _T("SampleClass1"); HWND hDlg; //メインダイアログハンドル HINSTANCE hInst; //インスタンスハンドル LRESULT CALLBACK mainDlgProc(HWND,UINT, WPARAM, LPARAM); //============================================================================= int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { MSG msg; WNDCLASS wndclass; WNDPROC lpfnWndProc; int nDlgidd; hInst = hInstance; //ウインドウクラスの登録 wndclass.style = 0; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = DLGWINDOWEXTRA; wndclass.hInstance = hInst; wndclass.hIcon = LoadIcon(NULL,(LPCTSTR)IDI_ICON1); wndclass.hCursor = LoadCursor(NULL,IDC_ARROW); wndclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); wndclass.lpszMenuName = NULL; wndclass.lpfnWndProc = mainDlgProc; wndclass.lpszClassName = szClassName1; lpfnWndProc = mainDlgProc; nDlgidd = IDD_DIALOG1; if((hDlg=CreateDialog(hInst, MAKEINTRESOURCE(nDlgidd), 0, (DLGPROC)lpfnWndProc)) == NULL) { // エラー処理(省略) } //メインウインドウの表示 ShowWindow(hDlg, nCmdShow); UpdateWindow(hDlg); //メッセージループ while(GetMessage(&msg, NULL, 0, 0)) { if(IsDialogMessage(hDlg,&msg) == FALSE) { TranslateMessage(&msg); DispatchMessage(&msg); } } return 0; } LRESULT CALLBACK mainDlgProc(HWND hWnd,UINT uMessage,WPARAM wParam,LPARAM lParam) { static HWND hIDEwnd; switch(uMessage){ //ウインドウ作成時の処理 case WM_INITDIALOG: CenterWindow(hWnd); SendMessage(GetDlgItem(hWnd, IDC_IDEDIT1), EM_SETLIMITTEXT, 260, 0); hIDEwnd = GetDlgItem(hWnd, IDC_IDEDIT1); SetFocus(hIDEwnd); break; //ユーザーメッセージ処理 case WM_COMMAND: // 省略 break; //プログラム終了時のウインドウ破棄 case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd,uMessage,wParam,lParam); } return 0; }
標準的なメッセージループ処理で、DispatchMessageに時間がかかる理由が思い当りません。
調査の糸口だけでも分かればと思い、質問致しました。よろしくお願い致します。
回答
-
デフォルトプロシージャが、DefWindowProc になっていますが、DefDlgProc の必要があると思います。
これが原因とは思いませんが、影響はあるかもしれません。
ちなみに、メッセージの 0xC042 は、RegisterWindowMessage した値です。環境に依存する値なので、具体的なメッセージについてはわかりません。
0x7FFF は、WM_APP-1 だと思います。こちらは、WM_USER範囲の最後の値なので、そのウィンドウの内部メッセージとして定義しているのだと思います。
追記:
モードレスダイアログにしている意味は何かあるのでしょうか?
差支えないなら、モーダルダイアログ(DialogBox API で呼び出し)で試してみてはいかがでしょう?
とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx
-
こんにちは。
ご質問のコードを私の環境(Windows 10 x64、 Visual Studio 2015)で試したところ、再現しませんでした。20秒程止まることはありませんでした。(環境が異なるか、試行回数が少ない?
ちなみに再現するOSとVisual Studioのバージョンは何でしょうか?
少し気になった部分は、CreateDialogの引数として渡されるDialogBoxプロシージャの形式がWindowプロシージャのようになっていることです。
https://msdn.microsoft.com/ja-jp/library/cc410762.aspx
上記のページによりますと、「ダイアログボックスプロシージャは不要なメッセージを処理させるために DefWindowProc 関数を呼び出してはなりません。」と記載されております。一度 DialogBoxプロシージャ を下記のようなコードに置き換えて再現するか確認できますでしょうか?
int CALLBACK mainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { static HWND hIDEwnd; switch (msg) { //ウインドウ作成時の処理 case WM_INITDIALOG: SendMessage(GetDlgItem(hWnd, IDC_IDEDIT1), EM_SETLIMITTEXT, 260, 0); hIDEwnd = GetDlgItem(hWnd, IDC_IDEDIT1); SetFocus(hIDEwnd); return FALSE; //フォーカスをセットした場合はFALSEを返す //ユーザーメッセージ処理 case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { DestroyWindow(hWnd); return TRUE; } break; //プログラム終了時のウインドウ破棄 case WM_DESTROY: PostQuitMessage(0); return TRUE; } return FALSE; }
- 編集済み kenjinoteMVP 2016年6月22日 5:51
- 回答の候補に設定 星 睦美 2016年7月28日 6:50
- 回答としてマーク 星 睦美 2016年9月15日 6:50
すべての返信
-
DispatchMessageはメッセージに対応するWndProcを呼び出しているわけで、要するにWndProc(とかそのたぐい。サブクラス化されていたり、フックされていたりするとそいつらの処理含む)が遅いのです。
デバッガがあるなら、時間がかかっているときにBreakして、本当に時間を食っている関数を突き止める必要があります
jzkey
デバッグできればよいのですが、発生する環境が遠地にあるためできません。。。
ログ出力することで時間がかかっている箇所をやっと特定できた、といった状況です。
(※正確にはDispatchMessageの先が何なのかを特定できていませんが)あと出力したログから分かっているのは、時間がかかっていた時のMSG構造体のmessage値が0x7fffで、
その前後が0x7fff, 0xc042であった、といったところです。後出し情報で申し訳ありません。
他に調査すべきものや調査方法等はないでしょうか? -
デフォルトプロシージャが、DefWindowProc になっていますが、DefDlgProc の必要があると思います。
これが原因とは思いませんが、影響はあるかもしれません。
ちなみに、メッセージの 0xC042 は、RegisterWindowMessage した値です。環境に依存する値なので、具体的なメッセージについてはわかりません。
0x7FFF は、WM_APP-1 だと思います。こちらは、WM_USER範囲の最後の値なので、そのウィンドウの内部メッセージとして定義しているのだと思います。
追記:
モードレスダイアログにしている意味は何かあるのでしょうか?
差支えないなら、モーダルダイアログ(DialogBox API で呼び出し)で試してみてはいかがでしょう?
とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx
-
こんにちは。
ご質問のコードを私の環境(Windows 10 x64、 Visual Studio 2015)で試したところ、再現しませんでした。20秒程止まることはありませんでした。(環境が異なるか、試行回数が少ない?
ちなみに再現するOSとVisual Studioのバージョンは何でしょうか?
少し気になった部分は、CreateDialogの引数として渡されるDialogBoxプロシージャの形式がWindowプロシージャのようになっていることです。
https://msdn.microsoft.com/ja-jp/library/cc410762.aspx
上記のページによりますと、「ダイアログボックスプロシージャは不要なメッセージを処理させるために DefWindowProc 関数を呼び出してはなりません。」と記載されております。一度 DialogBoxプロシージャ を下記のようなコードに置き換えて再現するか確認できますでしょうか?
int CALLBACK mainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { static HWND hIDEwnd; switch (msg) { //ウインドウ作成時の処理 case WM_INITDIALOG: SendMessage(GetDlgItem(hWnd, IDC_IDEDIT1), EM_SETLIMITTEXT, 260, 0); hIDEwnd = GetDlgItem(hWnd, IDC_IDEDIT1); SetFocus(hIDEwnd); return FALSE; //フォーカスをセットした場合はFALSEを返す //ユーザーメッセージ処理 case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { DestroyWindow(hWnd); return TRUE; } break; //プログラム終了時のウインドウ破棄 case WM_DESTROY: PostQuitMessage(0); return TRUE; } return FALSE; }
- 編集済み kenjinoteMVP 2016年6月22日 5:51
- 回答の候補に設定 星 睦美 2016年7月28日 6:50
- 回答としてマーク 星 睦美 2016年9月15日 6:50