none
メッセージループ中のDispatchMessageに時間がかかる RRS feed

  • 質問

  • ダイアログ入力後に処理を行う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に時間がかかる理由が思い当りません。
    調査の糸口だけでも分かればと思い、質問致しました。

    よろしくお願い致します。

    2016年6月22日 3:03

回答

  • デフォルトプロシージャが、DefWindowProc になっていますが、DefDlgProc の必要があると思います。

    これが原因とは思いませんが、影響はあるかもしれません。

    ちなみに、メッセージの 0xC042 は、RegisterWindowMessage した値です。環境に依存する値なので、具体的なメッセージについてはわかりません。

    0x7FFF は、WM_APP-1 だと思います。こちらは、WM_USER範囲の最後の値なので、そのウィンドウの内部メッセージとして定義しているのだと思います。

    追記:

    モードレスダイアログにしている意味は何かあるのでしょうか?
    差支えないなら、モーダルダイアログ(DialogBox API で呼び出し)で試してみてはいかがでしょう?


    とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx


    2016年6月22日 4:47
  • こんにちは。

    ご質問のコードを私の環境(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
    2016年6月22日 4:55

すべての返信

  • DispatchMessageはメッセージに対応するWndProcを呼び出しているわけで、要するにWndProc(とかそのたぐい。サブクラス化されていたり、フックされていたりするとそいつらの処理含む)が遅いのです。

    デバッガがあるなら、時間がかかっているときにBreakして、本当に時間を食っている関数を突き止める必要があります


    jzkey

    2016年6月22日 3:10
  • DispatchMessageはメッセージに対応するWndProcを呼び出しているわけで、要するにWndProc(とかそのたぐい。サブクラス化されていたり、フックされていたりするとそいつらの処理含む)が遅いのです。

    デバッガがあるなら、時間がかかっているときにBreakして、本当に時間を食っている関数を突き止める必要があります


    jzkey


    デバッグできればよいのですが、発生する環境が遠地にあるためできません。。。
    ログ出力することで時間がかかっている箇所をやっと特定できた、といった状況です。
    (※正確にはDispatchMessageの先が何なのかを特定できていませんが)

    あと出力したログから分かっているのは、時間がかかっていた時のMSG構造体のmessage値が0x7fffで、
    その前後が0x7fff, 0xc042であった、といったところです。

    後出し情報で申し訳ありません。
    他に調査すべきものや調査方法等はないでしょうか?

    2016年6月22日 4:31
  • デフォルトプロシージャが、DefWindowProc になっていますが、DefDlgProc の必要があると思います。

    これが原因とは思いませんが、影響はあるかもしれません。

    ちなみに、メッセージの 0xC042 は、RegisterWindowMessage した値です。環境に依存する値なので、具体的なメッセージについてはわかりません。

    0x7FFF は、WM_APP-1 だと思います。こちらは、WM_USER範囲の最後の値なので、そのウィンドウの内部メッセージとして定義しているのだと思います。

    追記:

    モードレスダイアログにしている意味は何かあるのでしょうか?
    差支えないなら、モーダルダイアログ(DialogBox API で呼び出し)で試してみてはいかがでしょう?


    とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx


    2016年6月22日 4:47
  • こんにちは。

    ご質問のコードを私の環境(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
    2016年6月22日 4:55
  • とっちゃん様、kenjinote様、返信ありがとうございます。

    DefWindowProcの件、ご指摘の通りですね。
    kenjinote様の提示いただいたように修正して確認してみます。
    修正版の確認結果は、また明日ご報告致します。

    ちなみに環境はWindows7、VisualC++6.0です。


    よろしくお願い致します。

    2016年6月22日 7:47
  • ダイアログコールバックに関してはkenjinoteさんのアドバイスどおりなので、ちょっと捕捉。

    ダイアログを出すだけなら、wndclassに関するコードは一切いらないはずですね。
    実際に RegisterClass()してませんし、たぶんダイアログリソースにCLASS名称登録もしていないのではないでしょうか。
    カスタムなクラスを登録してダイアログを出すのは結構めんどくさく、
    Windows上級者でもさらっと実装できる人は、あんまり見ないですね。

    2016年6月22日 8:56
  • DispatchMessage() API は最終的に、カーネル モード側の win32k!NtUserDispatchMessage ルーチンを呼び出しますが、アンチウィルスなどの一部のセキュリティ ソフトは win32k.sys 内の様々な関数をフックする製品があります。
    なのでもしかしたらセキュリティ ソフトの影響。。。なんてことも考えられるかも。
    2016年6月22日 10:24