《深入淺出MFC》學習筆記之二 (轉)

worldblog發表於2007-12-10
《深入淺出MFC》學習筆記之二 (轉)[@more@]


    
  MFC的來龍去脈

  -《深入淺出MFC》學習筆記之二 
  作者:XGM 2001-8-14

   
 
 
  MFC程式也是程式,所以它也有一個WinMain,但是我們在程式中看不到它的蹤影。實際上,在程式進入點之前,有一個(而且只有一個)全域性(在Hello程式中名為theApp),這是所謂的application
  ,當操作將程式載入並啟用時,這個全域性物件獲得,其構造會先,比WinMain更早。
  書中所舉Hello程式,是一個簡單的MFC程式,其主體在於WinMain和WndProc,而這兩個部分其實都有相當程度的不變性。MFC就是把有著相當固定行為的WinMain內部操作封裝在CWinApp中;把有著相當固定行為的WndProc內部操作封裝在CFrameWnd中。也就是說,CWinApp代表程式本體,CFrameWnd代表一個主框視窗

  雖然WinMain和WndProc內部操作有相當程度的不變性,但面對不同應用程式也需有變化,所以必須以這兩個類為基礎,派生自己的類,並改寫其中一部分成員函式。

 
  CWinApp----取代WinMain的地位
  傳統上SDK程式的WinMain所完成的工作現由CWinApp的三個函式完成:
  virtual BOOL InitApplication( );
  virtual BOOL InitInstance( );
  virtual BOOL Run( );
  CFrameWnd-----取代WndProc的地位
  CFrameWnd主要用來掌握一個視窗
  引爆器------Application object
  當執行Hello程式時,這個全域性物件產生,於是建構函式(見APPCORE.CPP)執行起來,CWinApp之中的成員變數將因為這個全域性物件的誕生而獲得配置與初值,配置完成後,WinMain(MFC早已準備好,並由連結器直接加到應用程式中去的)登場。

  AfxWinInit:是繼CWinApp建構函式之後的第一個操作;
  此後的操作是pApp->InitApplication(其中的pApp指向CMyWinApp物件,即本例中的theApp),因為CMyWinApp繼承自CWinApp,而InitApplication又是CWinApp的一個虛擬函式,我們沒有改寫它(大部分情況下也不需要改它),所以上述操作相當於CWinApp::InitApplication。此程式的程式碼出現在APPCORE.CPP中;

  繼InitApplication之後,AfxWinMain呼叫pApp->InitInstance,InitInstance是CWinApp的一個虛擬函式(應用程式一定要改寫這個函式,因為它在CWinApp中是個空函式,沒有任何默任操作),我們改寫了它,所以上述操作就是調有我們自己的這個InitInstance函式,我們將在該處展開我們的主視窗生命。

  CMyWinApp::InitInstance一開始new了一個CMyFrameWnd物件,new會引發建構函式CmyFrameWnd::CMyFrameWnd,其中呼叫了CFrameWnd的成員函式Create,它將產生一個視窗。

 
  Create函式共八個引數,第一個,指定WNDCLASS視窗類,如果放置NULL,表示要以MFC內建的視窗類產生一個標準的外框視窗(Create函式在產生視窗之前會引發視窗類的註冊操作,下一段講述這一內容);第二個,指定視窗標題;第三個,指定視窗風格,預設是WS-OVERLAPPEDWINDOW,如果你不想要視窗右上角的極大極小鈕,可以改成WS-OVERLAPPED|WS-CAPTION|WS-SYSMENU|WS-THICKFRAME|WS-MINIMIZEBOX|WS-MAXIMIZEBOX,如果希望有垂直捲軸,再加上WS-VSCROLL;第四個引數,指定視窗的位置與大小,預設值rectDefault
  ;第五個,指定父視窗,第六個指定選單;第七個,為擴充風格,唯有以:CreateWindowEx(而非:CreateWindow)函式才能完成,事實上,CFrameWnd:Create最終呼叫的正是:CreateWindowEx;第八個,是一個指向CCreateContext結構的指標,利用它,在具備Document/View結構的程式中初始化外框視窗,預設值NULL

  CFrameWnd:Create在函式中呼叫CreateEx(CWnd有這個成員函式,但其派生類CFrameWnd並沒有,所以這裡呼叫的實際上是CWnd:CreateEx);後者又呼叫PreCreateWindow虛擬函式(它在CWnd及其派生類CFrameWnd都有定義,所以實際上呼叫的是CFrameWnd::PreCreateWindow),這個函式呼叫了AfxDeferRegisterClass宏,它表示如果變數afxRegisteredClass的值顯示系統已經註冊了fClass
  這種視窗類,MFC啥也不做,否則就呼叫AfxEndDeferRegisterClass(fClass){它呼叫兩個函式完成實際的視窗類註冊操作,一個是RegisterWithIcon,一個是AfxRegisterClass},準備註冊之。

  視窗顯示與
  CMyFrameWnd::CMyFrameWnd結束後,視窗已經誕生出來;程式又回到CMyWinApp::InitInstance,於是呼叫ShowWindow函式令視窗顯示出來,並呼叫UpdateWindow函式令Hello程式送出WM-PAINT

  CWinApp::Run----程式生命的活水源頭
  Run又是CWinApp的一個虛擬函式,我們沒有改寫它(大部分情況下也不需要改它),所以上述操作相當於呼叫CWinApp::Run
  WinMain已由MFC提供,視窗類已由MFC註冊完成,連視窗函式也都由MFC提供
  把訊息與處理函式連線在一起:Message Map機制
  MFC提供給應用程式使用的“很方便的介面”是兩組宏,以Hello為例,第一個操作是在Hello.h的CMyFrameWnd加上DECLARE-MESSAGE-MAP;第二個操作是在Hello.cpp的任何位置(當然不能在函式內)使用宏

  BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
  ON_COMMAND(IDM_ABOUT, OnAbout)
  ON_WM_PAINT()
  END_MESSAGE_MAP()
 
  來龍去脈總整理
  程式的誕生
  Application object 產生, 於是獲得配置,初值亦設立了。
  AfxWinMain執行AfxWinInit,後者又調有AfxInitThread,把訊息佇列儘量加
  大到96
  AfxWinMain執行InitApplication。這是CWinApp的虛擬函式,我們通常不改寫它
  AfxWinMain執行InitInstance。這是CWinApp的虛擬函式,我們必須改寫它
  CMyWinApp::InitInstance “new”了一個CMyFrameWnd物件
 
  CmyFrameWnd建構函式呼叫Create,產生主視窗。我們在Create引數中指定的視窗類是NULL,於是MFC根據視窗種類,自行為我們註冊一個名為“AfxFrameOrView42d”的視窗類。

  回到InitInstance中繼續執行ShowWindow,顯示視窗
  執行UpdateWindow,於是發出WM-PAINT
  回到AfxWinMain,執行Run,進入訊息迴圈。
 
  程式開始執行:
  程式獲得WM-PAINT訊息(由CWinApp::Run中的::GetMessage迴圈)
  WM-PAINT經由::DispatchMessage送到視窗函式CWnd::DefWindowProc中。
  CWnd::DefWindowProc將訊息傳遞到訊息對映表格
 
  傳遞過程中發現有相符專案,於是呼叫專案中對應的函式。此函式是利用BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之間的宏設立起來的。

  標準訊息的處理程式亦有標準命名,例如WM-PAINT必由OnPaint處理
 
  程式的死亡:
  使用者單擊File/Close,於是發出WM-CLOSE
  CMyFrameWnd並沒有設定WM-CLOSE處理程式,於是交給預設的處理程式
  預設函式對於WM-CLOSE的處理方式是呼叫::DestroyWindow,並因而發出WM-DESTROY
  預設的WM-DESTROY處理方式是呼叫::PostQuitMessage,因此發出WM-QUIT
 
  CWinApp::Run收到WM-QUIT後會結束內部之訊息迴圈,然後呼叫ExitInstance,這是CWinApp的一個虛擬函式;如果CMyWinApp改寫了ExitInstance,那麼CWinApp::Run所呼叫的就是CMyWinApp::ExitInstance,否則就是CWinApp::ExitInstance

  最後回到AfxWinMain,執行AfxWinTerm,結束程式
 
  附Hello程式部分程式碼:
  Hello.cpp
 
  #include "Stdafx.h"
  #include "Hello.h"
  #include "Re.h"
 
  CMyWinApp theApp;  // application object
 
  //--------------------------------------------------------------------

  // CMyWinApp's member
  //--------------------------------------------------------------------

  BOOL CMyWinApp::InitInstance()
  {
  m_pMainWnd = new CMyFrameWnd();
  m_pMainWnd->ShowWindow(m_nCmdShow);
  m_pMainWnd->UpdateWindow();
  return TRUE;
  }
  //--------------------------------------------------------------------

  // CMyWinApp's member
  //--------------------------------------------------------------------

  BOOL CMyWinApp::OnIdle(LONG lCount)
  {
  CMyFrameWnd* pWnd = (CMyFrameWnd*)m_pMainWnd;
  pWnd->IdleTimeHandler(lCount);
 
  return TRUE;
  }
  //--------------------------------------------------------------------

  // CMyFrameWnd's member
  //--------------------------------------------------------------------

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

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

  void CMyFrameWnd::OnPaint()
  {
  CPaintDC dc(this);
  CRect rect;
 
  GetClientRect(rect);
 
  dc.SetTextAlign(TA_BOTTOM | TA_CENTER);
 
  ::LineDDA(rect.right/2, 0, rect.right/2, rect.bottom/2,
  (LINEDDAPROC) LineDDACallback, (LPARAM) (LPVOID) &dc);
  }
  //--------------------------------------------------------------------

  VOID CALLBACK CMyFrameWnd::LineDDACallback(int x, int y, LPARAM
  lpdc)
  {
  static char szText[] = "Hello, MFC";
 
  ((CDC*)lpdc)->TextOut(x, y, szText, sizeof(szText)-1);
  for(int i=1; i<50000; i++); 
  }
  //--------------------------------------------------------------------

  void CMyFrameWnd::OnAbout()
  {
  CDialog about("AboutBox", this);  // "AboutBox"
  about.odal();
  }
  //--------------------------------------------------------------------

  void CMyFrameWnd::IdleTimeHandler(LONG lCount)
  {
  CString str;
  CRect rect(10,10,200,30);
  CDC* pDC = new CClientDC(this);
 
  str.Format("%010d", lCount);
  pDC->DrawText(str, &rect, DT_LEFT | DT_TOP);
  }
 
  Hello.h
 
  class CMyWinApp : public CWinApp
  {
  public:
  virtual BOOL InitInstance();  //
  virtual BOOL OnIdle(LONG lCount);  // OnIdle e?
  };
 
  //--------------------------------------------------------------------

  class CMyFrameWnd : public CFrameWnd
  {
  public:
  CMyFrameWnd();  // constructor
  afx_msg void OnPaint();  // for WM_PAINT
  afx_msg void OnAbout();  // for WM_COMMAND (IDM_ABOUT)
  void IdleTimeHandler(LONG lCount);  // we want it call by
  CMyWinApp::OnIdle
 
  private:
  DECLARE_MESSAGE_MAP()  // Declare Message Map
  static VOID CALLBACK LineDDACallback(int,int,LPARAM);
  //?
  };

 

 

 

 


   
   
   
 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-990808/,如需轉載,請註明出處,否則將追究法律責任。

相關文章