CCmdTargetは、メッセージマップにおける基本となるクラス。
CCmdTarget クラス | Microsoft Docs
CCmdTarget クラス (MFC)
MFC覚え書き(CCmdTarget〜) - かせいさんとこ
ウィンドウやボタンなどのコントロールだけではなく、ビューやドキュメント、アプリケーションに至るまで、ウィンドウメッセージを受け取りイベント処理ができるのは、このCCmdTargetを継承しているからである。
コマンドが発生すると、CCmdTarget::OnCmdMsgが呼ばれる。
CCmdTarget クラス | Microsoft Docs
そこで、AfxMessageBoxなど挟んでみる。
もちろん末端のイベントハンドラであるOnBnClickedButton1にもAfxMessageBoxを入れる。
また、戻り値は基底クラスのOnCmdMsgに任せる。
起動して、真ん中のボタンを押すと、
まず、OnCmdMsgが実行されて、
その後、OnBnClickedButton1。
右のOKボタンでダイアログ閉じる場合も、
が呼ばれる。OnBnClickedButton1は真ん中のボタンのハンドラなので当然呼ばれない。
コマンドに対する処理がそのハンドラでされた場合は、処理済みとしてTRUEを返す。
まだ処理されていない場合はFALSEを返し、別のハンドラに廻していく。
こういうのをコマンドルーティングといい、各クラスのOnCmdMsgにもそういう処理が書かれている。
たとえば、CDialog::OnCmdMsgには、
BOOL CDialog::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; if ((nCode != CN_COMMAND && nCode != CN_UPDATE_COMMAND_UI) || !IS_COMMAND_ID(nID) || nID >= 0xf000) { // control notification or non-command button or system command return FALSE; // not routed any further } // if we have an owner window, give it second crack CWnd* pOwner = GetParent(); if (pOwner != NULL) { TRACE(traceCmdRouting, 1, "Routing command id 0x%04X to owner window.\n", nID); ASSERT(pOwner != this); if (pOwner->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; } // last crack goes to the current CWinThread object CWinThread* pThread = AfxGetThread(); if (pThread != NULL) { TRACE(traceCmdRouting, 1, "Routing command id 0x%04X to app.\n", nID); if (pThread->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; } TRACE(traceCmdRouting, 1, "IGNORING command id 0x%04X sent to %hs dialog.\n", nID, GetRuntimeClass()->m_lpszClassName); return FALSE; }
どの順番でどういう風に廻すかが書かれている。
この辺にも標準のコマンドルーティングなどが書かれている。
Sample/mfc/CCmdTarget/CCmdTarget/src/CCmdTarget at master · bg1bgst333/Sample · GitHub