VS2010/MFC編程入門之四(MFC應用程序框架分析)


可能你們對工程的運行原理仍是很模糊,理不出頭緒,畢竟跟C++編程入門系列中的例程差異太大。這一節就爲你們分析下MFC應用程序框架的運行流程。 shell

       一.SDK應用程序與MFC應用程序運行過程的對比 編程

       程序運行都要有入口函數,在以前的C++教程中都是main函數,而Windows應用程序的入口函數是WinMain函數,MFC程序也是從WinMain函數開始的。下面雞啄米就給出用Windows SDK寫的「HelloWorld」程序,與應用程序框架進行對比,這樣能更好的瞭解框架是怎樣運行的。Windows SDK開發程序就是不使用MFC類庫,直接用Windows API函數進行軟件開發。雞啄米不是要講解SDK開發,只是爲了對比而簡單介紹,至於SDK開發能夠在你們學完MFC之後選擇是否要研究,通常來講有簡單瞭解就能夠了。 windows

       SDK應用程序 app

       首先,給出Windows SDK應用程序「HelloWorld」的源碼:  框架


C++代碼
#include <windows.h>   
 
LRESULT CALLBACK myWndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam);  
    
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)     
{     
  const static TCHAR appName[] = TEXT("Hello world");     
  WNDCLASSEX myWin;     
  myWin.cbSize = sizeof(myWin);     
  myWin.style = CS_HREDRAW | CS_VREDRAW;     
  myWin.lpfnWndProc = myWndProc;     
  myWin.cbClsExtra = 0;     
  myWin.cbWndExtra = 0;     
  myWin.hInstance = hInstance;     
  myWin.hIcon = 0;     
  myWin.hIconSm  = 0;     
  myWin.hCursor = 0;     
  myWin.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);     
  myWin.lpszMenuName = 0;     
  myWin.lpszClassName = appName;     
  //Register     
  if (!RegisterClassEx(&myWin)) return 0;     
  const HWND hWindow = CreateWindow(     
    appName,     
    appName,     
    WS_OVERLAPPEDWINDOW,     
    CW_USEDEFAULT,     
    CW_USEDEFAULT,     
    CW_USEDEFAULT,     
    CW_USEDEFAULT,     
    0,     
    0,     
    hInstance,     
    0);     
  ShowWindow(hWindow,iCmdShow);     
  UpdateWindow(hWindow);     
  {     
    MSG msg;     
    while(GetMessage(&msg,0,0,0))     
    {     
      TranslateMessage(&msg);     
      DispatchMessage(&msg);     
    }     
    return (int)msg.wParam;     
  }     
}     
    
LRESULT CALLBACK myWndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)     
{     
  if (msg==WM_PAINT)     
  {     
    PAINTSTRUCT ps;     
    const HDC hDC = BeginPaint(hWindow,&ps);     
    RECT rect;     
    GetClientRect(hWindow,&rect);     
    DrawText(hDC,TEXT("HELLO WORLD"),-1,&rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);     
    EndPaint(hWindow,&ps);     
    return 0;     
  }     
  else if (msg==WM_DESTROY)     
  {     
    PostQuitMessage(0);     
    return 0;     
  }     
  return DefWindowProc(hWindow,msg,wParam,lParam);     
函數

       上面的程序運行的流程是:進入WinMain函數->初始化WNDCLASSEX,調用RegisterClassEx函數註冊窗口類->調用ShowWindow和UpdateWindow函數顯示並更新窗口->進入消息循環。關於消息循環再簡單說下,Windows應用程序是消息驅動的,系統或用戶讓應用程序進行某項操做或完成某個任務時會發送消息,進入程序的消息隊列,而後消息循環會將消息隊列中的消息取出,交予相應的窗口過程處理,此程序的窗口過程函數就是myWndProc函數,窗口過程函數處理完消息就完成了某項操做或任務。本例是要顯示「HELLO WORLD」字符串,UpdateWindow函數會發送WM_PAINT消息,可是此消息不通過消息隊列而是直接送到窗口過程處理,在窗口過程函數中最終繪製了「HELLO WORLD」字符串。 ui

 

       MFC應用程序 spa

       下面是MFC應用程序的運行流程,經過MFC庫中代碼進行分析: orm

       首先在HelloWorld.cpp中定義全局對象theApp:CHelloWorldApp theApp;。調用CWinApp和CHelloWorldApp的構造函數後,進入WinMain函數(位於appmodul.cpp中)。 對象


C++代碼
extern "C" int WINAPI  
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,  
    _In_ LPTSTR lpCmdLine, int nCmdShow)  
#pragma warning(suppress: 4985)  
{  
    // call shared/exported WinMain  
    return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);  

       在TCHAR.h中,有此定義:#define _tWinMain   WinMain,因此這裏的_tWinMain就是WinMain函數。它調用了AfxWinMain函數(位於WinMain.cpp中)。


C++代碼
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)  
{   
       .............略  
       // App global initializations (rare)  
       if (pApp != NULL && !pApp->InitApplication())  
              goto InitFailure;  
 
       if (!pThread->InitInstance())  
       {  
              .........略  
       }  
 
       // Run函數位於THRDCORE.cpp中,由此函數進入消息循環  
       nReturnCode = pThread->Run();  
 
       ..............略  
 
       return nReturnCode;  
}  

       上面InitInstance函數的代碼以下:


C++代碼
BOOL CTestApp::InitInstance()       
{      
       .............略      
       CSingleDocTemplate* pDocTemplate;      
       pDocTemplate = new CSingleDocTemplate(      
              IDR_MAINFRAME,      
              RUNTIME_CLASS(CTestDoc),      
              RUNTIME_CLASS(CMainFrame),      // main SDI frame window      
              RUNTIME_CLASS(CTestView));    
       if (!pDocTemplate)  
             return FALSE;    
       AddDocTemplate(pDocTemplate);      
       // Parse command line for standard shell commands, DDE, file open      
     
       CCommandLineInfo cmdInfo;      
       ParseCommandLine(cmdInfo);      
     
       //ProcessShellCommand位於AppUI2.cpp中,註冊並建立窗口      
       if (!ProcessShellCommand(cmdInfo))      
             return FALSE;      
     
       m_pMainWnd->ShowWindow(SW_SHOW);      
       m_pMainWnd->UpdateWindow();      
     
       return TRUE;      
}     

       InitInstance中的ProcessShellCommand函數又調用了CMainFrame的LoadFrame函數註冊並建立了窗口,執行完ProcessShellCommand函數之後,調用了m_pMainWnd的ShowWindow和UpdateWindow函數顯示並更新框架窗口。這些是否是與上面的SDK程序十分相似?

       接下來該是消息循環了,上面的AfxWinMain函數中調用了pThread的Run函數(位於THRDCORE.cpp中),在Run中包含了消息循環。Run函數的代碼以下:


C++代碼
int CWinThread::Run()      
{      
        .............略      
        // phase2: pump messages while available      
        do     
        {      
              // pump message, but quit on WM_QUIT      
              if (!PumpMessage())      
                     return ExitInstance();      
     
              // reset "no idle" state after pumping "normal" message      
              if (IsIdleMessage(&m_msgCur))      
              {      
                     bIdle = TRUE;      
     
                     lIdleCount = 0;      
     
              }      
       } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));      
       ..............略      
}      
       
BOOL CWinThread::PumpMessage()      
{    
       return AfxInternalPumpMessage();   
}   
 
BOOL AFXAPI AfxInternalPumpMessage()  
{  
       _AFX_THREAD_STATE *pState = AfxGetThreadState();  
     
       if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))         
       {      
             .............略      
       }      
       ...............略      
       if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur)))  
       {  
             ::TranslateMessage(&(pState->m_msgCur));  
             ::DispatchMessage(&(pState->m_msgCur));  
       }    
     
       return TRUE;      
}      

       咱們看到PumpMessage中經過調用GetMessage、TranslateMessage、DispatchMessage等創建了消息循環並投遞消息。

       窗口過程函數AfxWinProc形式以下:


C++代碼
LRESULT CALLBACK AfxWndProc(HWND hWnd,UINT nMsg,WPARAM wParam, LPARAM lParam)  
{  
      ……  
      CWnd*pWnd=CWnd::FromHandlePermanent(hWnd); 
      ReturnAfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam);  

       二者運行過程對比

       到此,經過對比能夠發現,MFC應用程序的運行流程與SDK程序是相似的,都是先進行一些初始化過程,再註冊並建立窗口,而後顯示、更新窗口,最後進入消息循環,消息都由窗口過程函數處理。如今你們是否是以爲有些頭緒了?在運行流程上有基本的掌握便可。

       二.MFC應用程序框架主要類之間的關係

       在第二講中,給你們演示瞭如何利用應用程序嚮導生成單文檔應用程序框架,能夠看到程序的基本框架和必要的代碼都自動生成了,上一講又講解了文件組成結構,實際上在前面自動生成的框架中比較重要的類包括如下幾個:CHelloWorldApp、CMainFrame、CHelloWorldDoc和CHelloWorldView,至於其餘的類好比CClassView、CFileView等都是在框架窗口(CMainFrame)上建立的面板等,不是必要的。

       就四個主要類的關係簡單講下,CHelloWorldApp類處理消息,將收到的消息分發給相應的對象。CMainFrame是視圖CHelloWorldView的父窗口,視圖CHelloWorldView就顯示在CMainFrame的客戶區中。視圖類CHelloWorldView用來顯示文檔類CHelloWorldDoc中的數據,並根據對視圖類的操做修改文檔類的數據。一個視圖類只能跟一個文檔類相聯繫,而一個文檔類能夠跟多個視圖類相聯繫。關於視圖類和文檔類的關係後面會詳細講解。

相關文章
相關標籤/搜索