if (afxTraceFlags & traceCmdRouting) TRACE1(\window.\\n\#endif
ASSERT(pOwner != this);
if (pOwner->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; }
// 最后,给当前线程对象处理
CWinThread* pThread = AfxGetThread(); if (pThread != NULL) {
#ifdef _DEBUG
if (afxTraceFlags & traceCmdRouting)
TRACE1(\nID); #endif
if (pThread->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; }
#ifdef _DEBUG
if (afxTraceFlags & traceCmdRouting) {
TRACE2(\dialog.\\n\
GetRuntimeClass()->m_lpszClassName); } #endif return FALSE; }
从上述实现可以看出,CDialog处理命令消息遵循如下顺序:
对话框自身→父窗口→线程对象
例如,模式对话框产生的WM_ENTERIDLE消息就发送给父窗口处理。
从实现中还看到,MFC根据TRACE过滤标识
afxTraceFlags的值,把有关命令消息的派发显示到调试窗口。
CDialog::OnCmdMsg不仅适用于模式对话框,也适用于无模式对话框。
5. 消息预处理和Dialog消息
另外,对话框窗口的消息处理还有一个特点,就是增加了对Dialog消息的处理,如同在介
绍::IsDialogMessage函数时所述。如果是Dialog消息,MFC框架将不会让它进入下一步的消息循环。为此,MFC覆盖了CDialog的虚拟函数
PreTranslateMessage,该函数的实现如下: BOOL CDialog::PreTranslateMessage(MSG* pMsg) {
// 用于无模式或者模式对话框的处理 ASSERT(m_hWnd != NULL); //过滤tooltip messages
if (CWnd::PreTranslateMessage(pMsg)) return TRUE;
//在Shift+F1帮助模式下,不转换Dialog messages CFrameWnd* pFrameWnd = GetTopLevelFrame(); if (pFrameWnd != NULL && pFrameWnd->m_bHelpMode) return FALSE;
//处理Escape键按下的消息
if (pMsg->message == WM_KEYDOWN &&
(pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_CANCEL) &&
(::GetWindowLong(pMsg->hwnd, GWL_STYLE) & ES_MULTILINE) &&
_AfxCompareClassName(pMsg->hwnd, _T(\{
HWND hItem = ::GetDlgItem(m_hWnd, IDCANCEL); if (hItem == NULL || ::IsWindowEnabled(hItem)) {
SendMessage(WM_COMMAND, IDCANCEL, 0); return TRUE; } }
// 过滤来自控制该对话框子窗口的送给该对话框的Dialog消息
return PreTranslateInput(pMsg);
}
从其实现可以看出,如果是Tooltip消息或者Dialog消息,这些消息将在PreTranslateMessage中被处理,不会进入消息发送的处理。
PreTranslateInput是CWnd的成员函数,它调用::IsDialogMessage函数来处理Dialog消息。 PreTranslateMessage的实现不仅用于模式对话框,而且用于无模式对话框。 6. 模式对话框的消息循环
从DoModal的实现可以看出,DoModal调用
CreateDlgIndirect创建的是无模式对话框,MFC如何来接管和控制应用程序的消息队列,实现一个模式对话框的功能呢?
CDialog调用了RunModalLoop来实现模式窗口的消息循环。RunModalLoop是CWnd的成员函数,它和相关函数的实现如下:
int CWnd::RunModalLoop(DWORD dwFlags) {
ASSERT(::IsWindow(m_hWnd)); //窗口必须已经创建且不在模式状态 ASSERT(!(m_nFlags & WF_MODALLOOP)); // 以下变量用于Idle处理 BOOL bIdle = TRUE; LONG lIdleCount = 0;
BOOL bShowIdle = (dwFlags & MLF_SHOWONIDLE) && !(GetStyle() & WS_VISIBLE);
HWND hWndParent = ::GetParent(m_hWnd); m_nFlags |= (WF_MODALLOOP|WF_CONTINUEMODAL); MSG* pMsg = &AfxGetThread()->m_msgCur;
//获取和派发消息直到模式状态结束 for (;;) {
ASSERT(ContinueModal());
//第一阶段,判断是否可以进行Idle处理
while (bIdle &&!::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE)) {
ASSERT(ContinueModal());
//必要的话,当Idle时显示对话框窗口 if (bShowIdle) {
ShowWindow(SW_SHOWNORMAL); UpdateWindow(); bShowIdle = FALSE; }
// 进行Idle处理
//必要的话发送WM_ENTERIDLE消息给父窗口
if (!(dwFlags & MLF_NOIDLEMSG) &&hWndParent != NULL && lIdleCount == 0) {
::SendMessage(hWndParent, WM_ENTERIDLE, MSGF_DIALOGBOX, (LPARAM)m_hWnd); }