MFC程式的啟動與死亡順序)

來源:互聯網
上載者:User
MFC程式的啟動與死亡順序(轉)

MFC程式的啟動與死亡順序

 

      1、建立Application object對象theApp
          程式一開始生產一個(且只有一個)Application
      object對象theApp,也即一個CWinApp對象,這個全域對象一產生,便執行其建構函式,因為並沒有定義CMyWinApp建構函式,所以即執行CWinApp類的建構函式。該函數定義於APPCORE.CPP第75行,你可以自己搜出來啃一啃,因此,CWinApp之中的成員變數將因為 theApp這個全域對象的誕生而獲得配置與初值。
      2、WinMain登場
          用SDK編程式時,程式的進入點是WinMain函數,而在MFC程式裡我們並沒有看到WinMain函數,哦!~
      原來她是被隱藏在MFC代碼裡面了。當theApp配置完成後,WinMain登場,慢!細看程式,並沒連到WinMain函數的代碼啊!這個我也不知道,MFC早已準備好並由連結器直接加到應用程式代碼中了,原來她在APPMODUL.CPP裡面,好,我們就認為當theApp配置完成後,程式就轉到 APPMODUL.CPP來了。那執行什麼呢?看看下面從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);
          }
          _tWinMain函數的“_t”是為了支援Unicode而準備的一個宏。
         
      _tWinMain函數傳回值是AfxWinMain函數的傳回值,AfxWinMain函數定義於WINMAIN.CPP第21行,稍加整理,去蕪存菁,就可以看到這個“程式進入點”主要做些什麼事:
         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;          
      用過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();
          }

              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);
              ...
      }

 

      函數調用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()         

為什麼經過這樣的宏之後,訊息就會自動流往指定的函數去呢?謎底在於Message Map的結構設計。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.