作者:刘树伟
MFC文档视图中的菜单项、工具栏按钮的状态,都可以通过空闲消息来更新。CWinApp提供了空闲消息函数的虚函数:CWinApp::OnIdle,它由CWinApp::Run调用,而CWinApp::Run又由AfxWinMain调用,AfxWinMain的代码如下:
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
// AFX internal initialization
if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
goto InitFailure;
// App global initializations (rare)
if (pApp != NULL && !pApp->InitApplication())
goto InitFailure;
// Perform specific initializations
if (!pThread->InitInstance()) // 这句最终会调用到CWinApp::InitInstance
{
if (pThread->m_pMainWnd != NULL)
{
TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");
pThread->m_pMainWnd->DestroyWindow();
}
nReturnCode = pThread->ExitInstance();
goto InitFailure;
}
nReturnCode = pThread->Run(); // 这句最终会调用到CWinApp::OnIdle
InitFailure:
#ifdef _DEBUG
// Check for missing AfxLockTempMap calls
if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
{
TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld).\n",
AfxGetModuleThreadState()->m_nTempMapLock);
}
AfxLockTempMaps();
AfxUnlockTempMaps(-1);
#endif
AfxWinTerm();
return nReturnCode;
}
所以,我们可以看到CWinApp::OnIdle是在pThread->InitInstance()之后调用的。对于模式对话框程序,直到对话框关闭后,才会执行到pThread->Run(),所以,对话框程序,重写CWinApp::OnIdle并不会被执行到。幸运的是,对话框程序提供了替代方案:WM_KICKIDLE消息。CWnd::RunModalLoop在处理循环时,会在空闲时,向对话框发送SendMessage(WM_KICKIDLE, MSGF_DIALOGBOX, lIdleCount++)消息,我们只需要模仿CWinApp::Run,在对话框处理WM_KICKIDLE消息时,向它的所有孩子发送WM_IDLEUPDATECMDUI消息即可:
case WM_KICKIDLE:
{
LONG lIdleCount = lParam;
if (pUIWnd != NULL && pUIWnd->m_hWnd != NULL && pUIWnd->IsWindowVisible())
{
pUIWnd->SendMessageToDescendants(WM_IDLEUPDATECMDUI, (WPARAM)TRUE, 0, TRUE, TRUE);
}
}
break;
然后在需要响应空闲消息的子窗口内,响应WM_IDLEUPDATECMDUI消息,做一些事情。