從win32到MFC(一)前言

開始閱讀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處理後,消息從隊列裏除掉。

相關文章
相關標籤/搜索