簡述MFC程式生與死 (轉)
經常從網上好,但開啟VC看了不到幾行,滑鼠就移到最到最右上角,對準那個“X”,咔嚓...(心還暗念:嚓死你!),二話不說在“程式”選單裡尋找qq的存在,開始進入休閒時光!!這可是我經常做的事情,唉!苦於基礎不紮實,經常被美好的程式碼踢出門外。但幸好我還對她有一斯感覺,近來有幸買到侯先生的《深入淺出MFC》,看到第六章:MFC程式的生死因果,覺得是學MFC,喔不,應該是看MFC程式程式碼的好起點,該章對MFC程式(沒有支援Document/View)的生死因果做也詳細的講解,為了加深記憶,我總結了以下流程,供大家參考。
MFC程式的啟動與死亡順序:
1、建立Application 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 WIN
_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(D 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程式設計序的朋友,看到上面程式碼應該有一點感覺了吧,函式中呼叫的PreCreate是虛擬函式,在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成員函式可以看出,整個針對不同功能的視窗使用了哪些視窗類。
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()
為什麼經過這樣的宏之後,訊息就會自動流往指定的函式去呢?謎底在於Message Map的結構設計,自己找《深入淺出MFC》第3章的Message Map模擬程式去啃一啃吧!
好了,就寫到這了,如果你是剛接觸MFC,我想看了之後你可能也有點糊塗,SORRY啦!我是從來沒有寫過總結的,沒事!把侯先生的《深入淺出MFC》拿出來啃幾遍就不會了。
2001-11-26 16:15 "> 阿青
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-990484/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- C#Windows程式的生與死 (轉)C#Windows
- mfc程式流程 (轉)C程式
- No MFC 程式設計01 - 最精簡的 win32 程式 (轉)程式設計Win32
- MFC,QT與WinForm,WPF簡介QTORM
- 簡述top命令與結束程式kill命令
- 簡述Linux 中程式與執行緒Linux執行緒
- Visual C++/MFC 指南(4):MFC 簡要指點 (轉)C++
- MFC技術內幕簡結 (轉)
- NO MFC - 使用 .log 除錯程式 (轉)除錯
- 遊戲創業者的“生”與“死”遊戲創業
- Windows管道技術簡述 (轉)Windows
- RNN與LSTM網路簡述RNN
- 【Oracle】死鎖的產生與處理Oracle
- 簡單而完整:MFC骨幹程式(深入淺出MFC之讀書筆記)筆記
- MFC下CSocket程式設計詳解(轉)程式設計
- MFC中OnDraw與OnPaint的區別 (轉載)AI
- 老文章——TWebBrowser程式設計簡述Web程式設計
- 開源BI系統簡述(轉載)
- No MFC,Only API (轉)API
- MFC檔案複製的函式程式程式碼 (轉)函式
- 使用MFC編寫internet查詢程式 (轉)
- 基於MFC文件/視/框架程式之利劍 (轉)框架
- CNN 簡述CNN
- 簡述HTTPHTTP
- 簡述JavaScript模組化程式設計(二)JavaScript程式設計
- MFC簡單計算器
- MFC控制元件精簡控制元件
- MFC開發(一)簡單同步時間應用程式
- Web 開發中 Blob 與 FileAPI 使用簡述WebAPI
- 簡述iOS開發framework製作與使用iOSFramework
- Windows 2000程式細述. (轉)Windows
- 初學MFC(2) (轉)
- 初學MFC(1) (轉)
- 簡述瀏覽器輸入 URL 地址後發生的事情瀏覽器
- 【程式碼簡述設計模式】----- 觀察者模式設計模式
- Java代理簡述Java
- Angular框架簡述Angular框架
- Nginx 配置簡述Nginx