開始閱讀MFC源碼,從MFC角度學習win32窗口與消息機制,同時學習MFC一些成熟的架構和設計模式。設計模式
源碼閱讀基於vs2012,位於目錄 "安裝目錄\Microsoft Visual Studio 11.0\VC\atlmfc\src\mfc",能夠看到 afx*.cpp, app*.cpp, ctl*.cpp, db*.cpp, dlg*.cpp, doc*.cpp, file*.cpp, ole*.cpp, view*.cpp, win*.cpp,代碼十分龐大,這裏只簡單介紹核心代碼。架構
winmain.cpp:入口函數 AfxWinMain 的實現;app
cmdtarg.cpp:類CCmdTarget的實現;函數
wincore.cpp:類CWnd;doccore.cpp:類CDocument;thrdcore.cpp:類CWinThread;appcore.cpp:類CWinApp; ===>繼承自 CCmdTargetoop
winfrm.cpp:類CFrameWnd;viewcore.cpp:類CView;dlgcore.cpp:類CDialog;===>繼承自 CWnd學習
MFC類組織結構圖可參考連接:https://blog.csdn.net/bflong/article/details/47316241this
Win32應用程序的函數入口通常是WinMain/wWinMain,分別對應ANSI/UNICODE字符集版本。(控制檯程序的函數入口main/wmain)spa
貼一下核心代碼(去掉部分Debug代碼):.net
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()) { if (pThread->m_pMainWnd != NULL) { pThread->m_pMainWnd->DestroyWindow(); } nReturnCode = pThread->ExitInstance(); goto InitFailure; } nReturnCode = pThread->Run();
依次執行 AfxWinInit , CWinApp::InitApplication , CWinThread::InitInstance 初始化函數後,進入主循環 CWinThread::Run 。線程
(1)AfxWinInit (appinit.cpp)
BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, int nCmdShow)
{
......
// 保存進程實例與資源句柄
AFX_MODULE_STATE* pModuleState = AfxGetModuleState(); pModuleState->m_hCurrentInstanceHandle = hInstance;
pModuleState->m_hCurrentResourceHandle = hInstance;
// 保存自WinMain的初始化參數
CWinApp* pApp = AfxGetApp();
if (pApp != NULL)
{
// Windows specific initialization (not done if no CWinApp)
pApp->m_hInstance = hInstance;
hPrevInstance; // Obsolete.
pApp->m_lpCmdLine = lpCmdLine;
pApp->m_nCmdShow = nCmdShow;
pApp->SetCurrentHandles();
}
if (!afxContextIsDLL)
AfxInitThread();
......
}
void AFXAPI AfxInitThread()
{
if (!afxContextIsDLL)
{
//設置本線程的消息鉤子
_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
ASSERT(pThreadState->m_hHookOldMsgFilter == NULL);
pThreadState->m_hHookOldMsgFilter = ::SetWindowsHookEx(WH_MSGFILTER, _AfxMsgFilterHook, NULL, ::GetCurrentThreadId());
}
}
總結下AfxWinInit主要初始化CWinApp的一些參數:hInstance, lpCmdLine, nCmdShow, 隨後設置了當前線程的消息鉤子(回調函數_AfxMsgFilterHook)。
(2)CWinApp::InitApplication
BOOL CWinApp::InitApplication() { if (CDocManager::pStaticDocManager != NULL) { if (m_pDocManager == NULL) m_pDocManager = CDocManager::pStaticDocManager; CDocManager::pStaticDocManager = NULL; } if (m_pDocManager != NULL) m_pDocManager->AddDocTemplate(NULL); else CDocManager::bStaticInit = FALSE; LoadSysPolicies(); return TRUE; }
CWinApp::InitApplication只是簡單初始化了CDocManager的全局變量。
(3)CWinThread::InitInstance
BOOL CWinThread::InitInstance() { ASSERT_VALID(this); return FALSE; // by default don't enter run loop }
CWinThread::InitInstance幾乎什麼也沒作。
(4)CWinThread::Run
int CWinThread::Run() { ASSERT_VALID(this); _AFX_THREAD_STATE* pState = AfxGetThreadState(); // for tracking the idle time state BOOL bIdle = TRUE; LONG lIdleCount = 0; //獲取和派發窗口消息直到接收WM_QUIT消息 for (;;) { // 階段一:檢測是否能夠執行一些空閒操做 while (bIdle && !::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE)) { if (!OnIdle(lIdleCount++)) bIdle = FALSE; // assume "no idle" state } // 階段二:執行消息循環 do { // 處理消息轉發和響應 if (!PumpMessage()) return ExitInstance(); //重置 bIdle 狀態 if (IsIdleMessage(&(pState->m_msgCur))) { bIdle = TRUE; lIdleCount = 0; } } while (::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE)); } }
對比一下win32窗口消息的循環:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
for( ;; )
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
return TRUE;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
BackgroundProcessing();
}
PeekMesssage 與 GetMessage
GetMessage一直等待到有窗口消息纔會返回,會阻塞線程;
PeekMessage等待一段時間,沒有窗口消息則返回FALSE。
PeekMessage能夠用於一些後臺進程,或沒有消息須要處理時執行其餘操做(如MFC的CWinThread::OnIdle中執行內存堆塊檢測,更新控件狀態)。
PeekMessage 的 PM_REMOVE 和 PM_NOREMOVE 參數
PM_NOREMOVE:PeekMessage處理後,消息不從隊列裏除掉;
PM_REMOVE:PeekMessage處理後,消息從隊列裏除掉。