The startup process of MFC program and the execution sequence of related functions

The original text comes from: http://blog.csdn.net/yuvmen/article/details/5877271

 

Understanding the startup process of MFC programs is very helpful for beginners to learn MFC; for those who do not use VC, they will forget it after a while. Just write it down so you can refer to it later.

1. Create the Application object object theApp

     At the beginning of the program, one (and only one) Application object object theApp is produced, that is, a CWinApp object. As soon as this global object is produced, its constructor is executed. Because the CMyWinApp constructor is not defined, the CWinApp class constructor is executed. This function is defined in line 75 of APPCORE.CPP, you can find it out and gnaw on it yourself. Therefore, the member variables in CWinApp will get configuration and initial value due to the birth of the global object theApp.

2, WinMain debut

     When programming with the SDK, the entry point of the program is the WinMain function, but in the MFC program we do not see the WinMain function, oh! ~ It turns out that she was hidden in the MFC code. When theApp configuration is complete, WinMain comes on stage, slow! Take a closer look at the program, and it is not connected to the code of the WinMain function! I don't know about this. MFC has already been prepared and added directly to the application code by the linker. It turns out that she is in APPMODUL.CPP. Well, we think that when theApp is configured, the program will go to APPMODUL.CPP. . What does it do? Take a look at the code below extracted from APPMODUL.CPP:

     extern "C" int WINAPI

     _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
     {
         // call shared/exported WinMain
         return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
     }

     The "_t" of the _tWinMain function is a macro prepared to support Unicode.

     The return value of the _tWinMain function is the return value of the AfxWinMain function. The AfxWinMain function is defined in the 21st line of WINMAIN.CPP. After a little sorting, you can see what this "program entry point" mainly does:

     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()函数是取得CMyWinApp对象指针,故上面函数第6至8行相当于调用:

     CMyWinApp::InitApplication();
     CMyWinApp::InitInstance()
     CMyWinApp::Run();

     因而导致调用:
     CWinApp::InitApplication();   //因为 CMyWinApp 并没有改写 InitApplication
     CMyWinApp::InitInstance()     //因为 CMyWinApp 改写了 InitInstance
     CWinApp::Run();               //因为 CMyWinApp 并没有改写 Run

     用过SDK写程序的朋友,现在可能会发出会心的微笑。

3、AfxWinInit——AFX内部初始化操作

     AfxWinInit是继CWinApp构造函数之后的第一个操作,主要做的是AFX内部初始化操作,该函数定义于APPINIT.CPP第24行,这里就不掏出来了,你自己搜出来啃吧!

4、执行CWinApp::InitApplication

     AfxWinInit之后的操作是pApp->InitApplication,我们已知道pApp指向CMyWinApp对象,当调用:

         pApp->InitApplication();

     相当于调用:

         CMyWinApp::InitApplication();

     但是你要知道,CMyWinApp继承自CWinApp,而InitApplication又是CWinApp的一个虚拟函数,我们并没有改写它(大部分情况下不需改写它),所以上述操作相当于调用:

         CWinApp::InitApplication();

     此函数定义于APPCORE.CPP第125行,你自己搜出来看吧!我就不搬出来了,里面的操作都是MFC为了内部管理而做的(其实我也看不懂,知道有这回事就好了)。

5、执行CWinApp::InitInstance

     继InitApplication函数之后,AfxWinMain调用pApp->InitInstance。当程序调用:

         pApp->InitInstance();

     相当于调用:

         CMyWinApp::InitInstance();

     但是你要知道,CMyWinApp继承自CWinApp,而InitInstance又是CWinApp的一个虚拟函数。由于我们改写了它,所以上述操作就是调用我们自己(CMyWinApp)的这个InitInstance函数。

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

     现在已经来到CWinApp::InitInstance了,该函数先new一个CMyFrameWnd对象,从而产生主窗口。在创建CMyFrameWnd对之前,要先执行构造函数CMyFrameWnd::CMyFrameWnd(),该函数用Create函数产生窗口:

         CMyFrameWnd::CMyFrameWnd()
         {
             Create(NULL, "Hello MFC", WS_OVERLAPPEDWINDOW, rectDefault, NULL, "MainMenu");
         }

     其中Create是CFrameWnd的成员函数,它将产生一个窗口,用过SDK编程序的朋友都知道,要创建主窗口时要先注册一个窗口类,规定窗口的属性等,但,这里使用哪一个窗口类呢?Create函数第一个参数(其它参数请参考MSDN或《深出浅出MFC》详解)指定窗口类设为NULL又是什么意思啊?意思是要以MFC内建的空中类产生一个标准的外框窗口,但,我们的程序一般都没有注册任何窗口类呀!噢,Create函数在产生窗口之前会引发窗口类的注册操作。

     让我们先挖出Create函数都做了些什么操作,Create函数定义于WINFRM.CPP的第538行(在此我就不把代码Copy过来了,你自己打开出来看吧),函数在562行调用CreateEx函数,由于CreateEx是CWnd的成员函数,而CFrameWnd是从CWnd继而来,故将调用CWnd::CreateEx。此函数定义于WINCORE.CPP第665行,下面是部分代码:

     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;

         if(PreCreateWindow(cs))
         {
             PostNcDestroy();
             return FALSE;
         }

         AfxHookWindowCreate(this);
         HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
         cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
         cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
         ...
     }

     用过SDK编程序的朋友,看到上面代码应该有一点感觉了吧,函数中调用的PreCreateWindows是虚拟函数,在CWnd和CFrameWnd之中都有定义。由于this指针所指对象的缘故,这里应该调用的是CFrameWnd::PreCreateWindow。该函数定义于WINFRM.CPP第521行,以下是部分代码:

     BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
     {
         if (cs.lpszClass == NULL)
         {
             VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
             cs.lpszClass = _afxWndFrameOrView;   // COLOR_WINDOW background
         }
         ...
     }

     其中AfxDeferRegisterClass是一个定义于AFXIMPL.H中的宏。该宏如下:

         #define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)

     注:这里有宏和《深入浅出MFC》的不一样,以上代码是从Visual C++ 6.0摘取。

     AfxEndDeferRegisterClass定义于WINCORE.CPP第3619行,该函数很复杂,主要是注册五个窗口类(哇!终于看到窗口类了,怎么用5个呢?我还不清楚),不同类的PreCreateWindow成员函数都是在窗口产生之前一刻被调用,准备用来注册窗口类。如果我们指定的窗口类是NULL,那么就使用系统默认类。从CWnd及其各个派生类的PreCreateWindow成员函数可以看出,整个Framework针对不同功能的窗口使用了哪些窗口类。

7、窗口显示与更新

     CMyFrameWnd::CMyFrameWnd结束后,窗口已经诞生出来;程序流程又回到CMyWinApp::InitInstance,于是调用ShowWindow函数令窗口显示出来,并调用UpdateWindow函数令程序送出WM_PAINT消息。在SDK程序中,消息是通过窗口函数来处理,而现在窗口函数在哪里、又如何送到窗口函数手中呢?那要从CWinApp::Run说起了。

8、执行CWinApp::Run——程序生命的活水源头

     在执行完CMyWinApp::InitInstance函数后,程序的脚步到了AfxWinMain函数的pApp->Run了,现在我们已知道这将执行CWinApp::Run函数,该函数定义于APPCORE.CPP第391行,下面是程序代码:

     int CWinApp::Run()
     {
         if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
         {
             // Not launched /Embedding or /Automation, but has no main window!
             TRACE0("Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application./n");
             AfxPostQuitMessage(0);
         }
         return CWinThread::Run();
     }

     函数调用CWinThread::Run函数,该函数定义于THRDCORE.CPP第456行,在这里我就不Copy出来了。函数在第480行调用了PumpMessage函数,该函数定义于THRDCORE.CPP第810行,整理后的部分代码如下:

     BOOL CWinThread::PumpMessage()
     {
         if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
         {
             return FALSE;
         }

         // process this message
         if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
         {
             ::TranslateMessage(&m_msgCur);
             ::DispatchMessage(&m_msgCur);
         }
         return TRUE;
     }

     该函数主要操作是将消息由::DispatchMessage送到窗口函数(CWnd::DefWindowProc)中,但程序一般没有提供任何窗口函数,但在AfxEndDeferRegisterClass中,在注册五种窗口类之前已经指定窗口函数为:

         wndcls.lpfnWndProc = DefWindowProc;

     虽然窗口函数被指定为DefWindowProc成员函数(CWnd::DefWindowProc),但事实上消息并不是被唧往该处,而是一个名为AfxWndProc的全局函数去。

9、把消息与处理函数连接在一起——Message Map机制

     到此,主窗口已经产生,等待的就是各种消息了,然后调用相应的处理函数,然而消息和处理函数怎样连接在一起呢?MFC采用了Message Map机制(消息映射机制),提供给应用程序使用的“很方便的接口”的两组宏,其原理我还不大清楚,在这里也无法讲解,主要用法是:先在类声明中结合DECLARE_MESSAGE_MAP()给出处理函数,如:

     class CMyFrameWnd : public CFrameWnd
     {
     public:
         CMyFrameWnd();
         afx_msg void OnPaint();    // for WM_PAINT
         afx_msg void OnAbout();    // for WM_COMMAND (IDM_ABOUT)
         DECLARE_MESSAGE_MAP()
     }

     再在相应的.CPP文件的任何位置(当然不能在函数之内)使用BEBIN_MESSAGE_MAP()和END_MESSAGE_MAP()宏把相应的消息加入去,如:

     BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
         ON_COMMAND(IDM_ABOUT, OnAbout)
         ON_WM_PAINT()
     END_MESSAGE_MAP()

 

 

总结一下

(1) 调用CXXApp构造函数构造全局对象theApp: CXApp theApp;

(2) 调用Winmain函数完成初始化工作: 通过宏_tWinMain

(3) 初始化工作包括: 窗口类注册、窗口产生、显示和更新、消息循环等等

     ① 注册窗口类:AfxEndDeferRegisterClass()  //相当于SDK里面的RegisterClass()函数

     ② 创建窗口:CMainFrame::PreCreateWindow()  //允许我们修改窗口属性的地方

                        CFrameWnd::Create()

     ③ 消息循环:PumpMessage()

在程序运行时CWinApp将创建一个CFrameWnd框架窗口实例,而框架窗口将创建文档模板,然后有文档模板创建文档实例和视实例,并将两者关联。

 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326825506&siteId=291194637