当前位置: 代码迷 >> VC/MFC >> MFC的运作过程分析
  详细解决方案

MFC的运作过程分析

热度:599   发布时间:2016-05-02 03:57:29.0
MFC的运行过程分析

MFC程序的运行细节剖析

       MFC程序也是Windows程序,所以它应该也有一个WinMain,但是在程序中看不到它的踪影。其实在程序进入点之前,还有一个(而且仅有一个)全局对象(theApp),这就是所谓的Application object,当操作系统将程序加载并激活时,这个全局对象获得配置,其构造函数会先执行,比WinMain更早。

一 CWinApp取代WinMain

       CWinApp的派生对象被称为application object,可以想见,CWinApp本身就代表一个程序本体。所谓程序本体是与程序本身有关而不与窗口有关的数据或动作。CWinApp类定义如下:

class CWinApp : public CWinThread

{

    //startup args

    HINSTANCE m_hInstance;

    HINSTANCE m_hPrevInstance;

    LPTSTR m_lpCmdLine;

    int m_nCmdShow;

 

//Runing args

CWnd* m_pMainWnd;

CWnd* m_pActiveWnd;

    LPCTSTR m_pszAppName;

    LPCTSTR m_pszRegistryKey;

   

public:

    LPCTSTR m_pszExeName;       //executable name

    LPCTSTR m_pszHelpFilePath;  //default based on module path

    LPCTSTR m_pszProfileName;   //default based on app name

 

public:

    virtual BOOL InitApplication();

 

    //overrides for implementation

    virtual BOOL InitInstance();

    virtual int ExitInstance();

    virtual int Run();

    virtual BOOL OnIdle(LONG lCount);

};

       传统意义上SDK程序的WinMain所完成的工作现在由CWinApp的三个函数完成:

virtual BOOL InitApplication();

       virtual BOOL InitInstance();

       virtual int Run();

那么WndProc函数到哪里去了呢?CFrameWnd主要用来掌握一个窗口,你几乎可以说它是用来取代SDK程序中的窗口函数的地位。MFC通过内建了一个所谓的Message Map机制,会把消息自动送到“与消息对应的特定函数”中去。有关Message Map实现机制请看另外一篇博客。

二 程序运行过程分析

       如图所示,左半边MFC程序代码,右半部是我们自己写的程序代码。

       当程序开始执行时,theApp这个全局对象产生,于是构造函数执行起来。CWinApp的构造函数如下

CWinApp::CWinApp(LPCSTR lpszAppName)

{

    m_pszAppName = lpszAppName;

 

    //initialize CWinThread state

    AFX_MODULE_THREAD_STATE* pThreadState = AfxGetModuleThreadState();

    pThreadState->m_pCurrentWinThread = this;

    m_hThread = ::GetCurrentThread();

    m_nThreadID = ::GetCurrentThreadId();

 

    //initialize CWinApp state

    AFX_MODULE_STATE* pModuleState = AfxGetModuleState();

    pModuleState->m_pCurrentWinApp = this;

 

    //in non-running state until WinMain

    m_hInstance = NULL;

    m_pszHelpFilePath = NULL;

    ........

}

       CWinApp之中的成员变量将因为theApp这个全局对象的诞生而获得配置与初值。theApp配置完成后,WinMain登场。我们并未编写WinMain程序代码,这是MFC早就已经准备好并由链接器直接加到应用程序代码中:

extern “c” int WINAPI

_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)

{

    return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);

}

 

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)

{

    int nReturnCode = -1;

    CWinApp* pApp = AfxGetApp();

 

    AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow);

   

    pApp->InitApplication();

    pApp->InitInstance();

    nReturnCode = pApp->Run;

 

    AfxWinTerm();

    return nReturnCode;

}

其中,AfxGetApp是一个全局函数,定义域AFXWIN1.INL中:

_AFXWIN_INLINE  CWinApp* AFXAPI AfxGetApp()

            { return afxCurrentWinApp; }

       #define afxCurrentWinApp  AfxGetModuleState()->m_pCurrentWinApp

       根据之前描述的CWinApp::CWinApp()中的操作,可以知道AfxGetApp其实就是取得CMyWinApp对象指针。所以,AfxWinMain中这样的操作:

       CWinApp* pApp = AfxGetApp();

       pApp->InitApplication();

       pApp->InitInstance();

       nReturnCode = pApp->Run;

 

       其实就相当于调用:

       CMyWinApp::InitApplication();

       CMyWinApp::InitInstance();

       CMyWinApp::Run();

      

       因而导致调用:

       CWinApp::InitApplication();

       CMyWinApp::InitInstance();//改写了父类的该方法,调用子类的

       CWinApp::Run();

 

AfxWinInit——AFX内部初始化操作

       AfxWinInit是继CWinApp构造函数之后的第一个操作。以下是它的操作摘要

BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)

{

    ASSERT(hPrevInstance == NULL);

    //set resource handles

    AFX_MODULE_STATE* pState = AfxGetModuleState();

    pState->m_hCurrentInstanceHandle = hInstance;

    pState->m_hCurrentResourceHandle = hInstance;

    //fill in the initial state for the application

    CWinApp* pApp = AfxGetApp();

    if(pApp != NULL)

    {

        //windows spcific initialization

        pApp->m_hInstance = hInstance;

        pApp->m_hPrevInstance = hPrevInstance;

        pApp->m_lpCmdLine = lpCmdLine;

        pApp->m_nCmdShow = nCmdShow;

        pApp->SetCurrentHandles();

    }

    //initialize thread specific data

    if(!afxContexIsDLL)

    {

        AfxInitThread();

    }

    return TURE;

}

 

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;

    }

    return TRUE;

}

这些都是MFC为了内部管理而做的。

CMyWinApp::InitInstance

       继InitApplication之后,调用InitInstance即调用CMyWinApp::InitInstance();

也就是调用我们重写后的InitInstance函数。

CFrameWnd::Create产生主窗口(并先注册窗口类)

 

       CMyWinApp::InitInstance一开始new了一个CMyFrameWnd对象,准备用作主框窗口的c++对象。会调用CMyFrameWnd类的构造函数,从而调用Create函数它将产生一个窗口。在调用Create函数的过程中Create会调用CreateEx,CreateEx函数实现如下:

BOOL CWnd::CreateEx(DWORD dwExstyle, LPCTSTR lpszClassName,

                    LPCTSTR lpszWindowName, DWORD dwStyle,

                    int x, int y, int nWidth, int nHeight,

                    HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)

{

    //allow modification of several         common create parameters

    CREATESTRUCT cs;

    cs.dwExstyle = dwExStyle;

    cs.lpszClass = lpszClassName;

    cs.lpszName = lpszWindowName;

    cs.style = dwStyle;

    cs.x = x;

    cs.y = y;

    cs.cx = nWidth;

    cs.cy = nHeight;

    cs.hwndParent = hWndParent;

    cs.hMenu = nIDorHMenu;

    cs.hInstance = AfxGetInstanceHandle();

    cs.lpCreateParams = lpParam;

 

    PreCreateWindow(cs);

    AfxHookWindowCreate(this);

    HWND hWnd = ::CreateWindow(.....);

    ........

 

}

到这里可能熟悉SDK编程的都会问那么窗口类呢,其实窗口类在Create函数调用时窗口类参数传的NULL代表将以MFC内建的窗口类产生一个标准的外框窗口。

 

窗口显示与更新

 

       CMyFrameWnd::CMyFrameWnd结束后,窗口已经诞生出来;程序流程又回到了CmyWinApp::InitInstance,于是调用ShowWindow函数令窗口显示出来,并调用UpdateWindow函数令hello程序发送出WM_PAINT消息。

       我们很关心这个WM_PAINT消息是如何送到窗口函数的手中。而且,窗口函数又在哪里?

       MFC程序是不是也像SDK程序一样,有一个GetMessage/DispatchMesage循环?

 

CWinApp::Run

       程序运行到这,接下来将执行pApp->Run()即相当于调用CMyWinApp::Run();

Run函数是CWinApp的一个虚函数,而且我们没有改写它,所以上述操作相当于调用CWinApp::Run();通过研读Run函数实现代码它会调用PumpMessage()函数,在这个函数中就是真正处理消息循环函数。

 

三 整理

1.程序的诞生:

Application object 产生,内存于是获得配置,初值亦设立了。AfxWinMain

执行AfxWinInit,后者有调用AfxInitThread,把消息队列尽量加大到96。

AfxWinMain执行InitApplication。这是CWinApp的虚函数,我们通常不改写它。

AfxWinMain执行InitInstance。这是CWinApp的虚函数,我们必须改写它。

CMyWinApp::InitInstance new 了一个CMyFrameWnd对象。

       CMyFrameWnd构造函数调用Create,产生主窗口。

       回到InitInstance中继续执行ShowWindow,显示窗口。

       执行UpdateWindow,于是发出WM_PAINT。

       回到AfxWinMain,执行Run,进入消息循环。

2.程序开始运行:

程序获得WM_PAINT消息(藉由CWinApp::Run中的::GetMessage循环)。

WM_PAINT经由::DispatchMessage送到窗口函数CWnd::DefWindowProc

中。

       CWnd::DefWindowProc将消息传递过消息映射表格(Message Map)。

       传递过程中发现有相符项目,于是调用项目中对应的函数。此函数是应用程序利用BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间的宏设立起来的。

       标准消息的处理程序亦有标准命名,例如WM_PAINT必然由OnPaint处理。

3.程序的死亡

使用者单击【File/Close】,于是发出WM_CLOS。

CMyFrameWnd并没有设置WM_CLOSE处理程序,于是交给默认的处理程序。

默认的处理函数对于WM_CLOSE的处理方式是调用::DestroyWindow,并因

而发出WM_DESTROY。

CWinApp::Run收到WM_QUIT后会结束其内部消息循环,然后调用

ExitInstance,这是CWinApp的一个虚拟函数。

       最后回到AfxWinMain,执行AfxWinTerm,结束程序。

版权声明:本文为博主原创文章,未经博主允许不得转载。

  相关解决方案