上一講雞啄米講的是VS2010應用程序工程中文件的組成結構,可能大家對工程的運行原理還是很模糊,理不出頭緒,畢竟跟C++編程入門系列中的例程差別太大。這一節雞啄米就為大家分析下MFC應用程序框架的運行流程。
???????一.SDK應用程序與MFC應用程序運行過程的對比
?????? 程序運行都要有入口函數,在之前的C++教程中都是main函數,而Windows應用程序的入口函數是WinMain函數,MFC程序也是從WinMain函數開始的。下面雞啄米就給出用Windows SDK寫的“HelloWorld”程序,與應用程序框架進行對比,這樣能更好的了解框架是怎樣運行的。Windows SDK開發程序就是不使用MFC類庫,直接用Windows API函數進行軟件開發。雞啄米不是要講解SDK開發,只是為了對比而簡單介紹,至于SDK開發可以在大家學完MFC以后選擇是否要研究,一般來說有簡單了解就可以了。
???????SDK應用程序
?????? 首先,給出Windows SDK應用程序“HelloWorld”的源碼:??
- #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”字符串。
???????MFC應用程序
?????? 下面是MFC應用程序的運行流程,通過MFC庫中代碼進行分析:
?????? 首先在HelloWorld.cpp中定義全局對象theApp:CHelloWorldApp theApp;。調用CWinApp和CHelloWorldApp的構造函數后,進入WinMain函數(位于appmodul.cpp中)。
- 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中)。
- 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函數的代碼如下:
- 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函數的代碼如下:
- 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形式如下:
- 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中的數據,并根據對視圖類的操作修改文檔類的數據。一個視圖類只能跟一個文檔類相聯系,而一個文檔類可以跟多個視圖類相聯系。關于視圖類和文檔類的關系后面會詳細講解。
?????? 本節VC++/MFC編程入門教程內容比較多,主要是讓大家對MFC應用程序的運行原理有大概的了解。對于以后的MFC開發有很多好處。如果有問題請在雞啄米博客留言交流。謝謝。
?
原文地址:http://www.jizhuomi.com/software/145.html