从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 的实现;

cmdtarg.cpp:类CCmdTarget的实现;

wincore.cpp:类CWnd;doccore.cpp:类CDocument;thrdcore.cpp:类CWinThread;appcore.cpp:类CWinApp; ===>继承自 CCmdTarget

winfrm.cpp:类CFrameWnd;viewcore.cpp:类CView;dlgcore.cpp:类CDialog;===>继承自 CWnd

MFC类组织结构图可参考链接:https://blog.csdn.net/bflong/article/details/47316241

Win32应用程序的函数入口一般是WinMain/wWinMain,分别对应ANSI/UNICODE字符集版本。(控制台程序的函数入口main/wmain)

贴一下核心代码(去掉部分Debug代码):

    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处理后,消息从队列里除掉。

原文地址:https://www.cnblogs.com/hanawasakuraki/p/9444417.html