MFC框架机制详解
MFC框架機(jī)制詳解
?
1.1 ? ?Windows消息機(jī)制要點(diǎn)
?
1.1.1 ? ?窗口過(guò)程
? ? 每個(gè)窗口會(huì)有一個(gè)稱為窗口過(guò)程的回調(diào)函數(shù)(WndProc),它帶有四個(gè)參數(shù),分別為:窗口句柄(Window Handle), 消息ID(Message ID), 和兩個(gè)消息參數(shù)(wParam, lParam), 當(dāng)窗口收到消息時(shí)系統(tǒng)就會(huì)調(diào)用此窗口過(guò)程來(lái)處理消息。(所以叫回調(diào)函數(shù))
?
1.1.2 ? ?消息類型
1. ?系統(tǒng)定義消息(System-Defined Messages)
在SDK中事先定義好的消息,非用戶定義的,其范圍在[0x0000, 0x03ff]之間 (1024),
可以分為以下三類:
窗口消息(Windows Message)
? 與窗口的內(nèi)部運(yùn)作有關(guān),如創(chuàng)建窗口,繪制窗口,銷毀窗口等。可以是一般的窗口,也可以是Dialog,控件等。所有派生自CWnd 的類才有資格接收標(biāo)準(zhǔn)消息。
??
? 第一個(gè)消息是:WM_NULL = $0000;
??
? 最后一個(gè)消息是:WM_DDE_LAST = WM_DDE_FIRST (03E0)+8=03E0)+8=03E8=1000;
??
? 中間有幾個(gè)地方并不連續(xù);
??
? 如:WM_CREATE, WM_PAINT, WM_MOUSEMOVE, WM_CTLCOLOR, WM_HSCROLL…
命令消息(Command Message)
? 與處理用戶請(qǐng)求有關(guān), 如單擊菜單項(xiàng)或工具欄或控件時(shí), 就會(huì)產(chǎn)生命令消息。WM_COMMAND=$0111, LOWORD(wParam)表示菜單項(xiàng),工具欄按鈕或控件的ID。如果是控件, HIWORD(wParam)表示控件消息類型。
??
? 所有派生自CCmdTarget 的類都有能力接收WM_COMMAND消息。
控件通知(Notify Message)
? 控件通知消息, 這是最靈活的消息格式, 其Message, wParam, lParam分別為:WM_NOTIFY= $004E, 控件ID,指向NMHDR的指針。NMHDR包含控件通知的內(nèi)容, 可以任意擴(kuò)展。
2. ?用戶程序定義消息(Application-Defined Messages)
用戶自定義的消息, 對(duì)于其范圍有如下規(guī)定:
WM_USER: 0x0400-0x7FFF (ex. WM_USER+10) ?
WM_APP(winver> 4.0): 0x8000-0xBFFF (ex.WM_APP+4) ?
RegisterWindowMessage: 0xC000-0xFFFF?
3. ?消息隊(duì)列(Message Queues)
Windows中有兩種類型的消息隊(duì)列?
系統(tǒng)消息隊(duì)列(System Message Queue)
? 這是一個(gè)系統(tǒng)唯一的Queue,設(shè)備驅(qū)動(dòng)(mouse, keyboard)會(huì)把操作輸入轉(zhuǎn)化成消息存在系統(tǒng)隊(duì)列中,然后系統(tǒng)會(huì)把此消息放到目標(biāo)窗口所在的線程的消息隊(duì)列(thread-specific message queue)中等待處理?
線程消息隊(duì)列(Thread-specific Message Queue)
? 每一個(gè)GUI線程都會(huì)維護(hù)這樣一個(gè)線程消息隊(duì)列。(這個(gè)隊(duì)列只有在線程調(diào)用GDI函數(shù)時(shí)才會(huì)創(chuàng)建,默認(rèn)不創(chuàng)建)。然后線程消息隊(duì)列中的消息會(huì)被送到相應(yīng)的窗口過(guò)程(WndProc)處理。
??
? 注意: 線程消息隊(duì)列中WM_PAINT,WM_TIMER只有在Queue中沒(méi)有其他消息的時(shí)候才會(huì)被處理,WM_PAINT消息還會(huì)被合并以提高效率。其他所有消息以先進(jìn)先出(FIFO)的方式被處理。
4. ?隊(duì)列消息(Queued Messages)和非隊(duì)列消息(Non-Queued Messages)?
隊(duì)列消息(Queued Messages)
? 消息會(huì)先保存在消息隊(duì)列中,消息循環(huán)會(huì)從此隊(duì)列中取消息并分發(fā)到各窗口處理 ?
? 如鼠標(biāo),鍵盤消息。
非隊(duì)列消息(NonQueued Messages)
? 消息會(huì)繞過(guò)系統(tǒng)消息隊(duì)列和線程消息隊(duì)列直接發(fā)送到窗口過(guò)程被處理 ?
? 如:WM_ACTIVATE, WM_SETFOCUS, ?
? WM_SETCURSOR, WM_WINDOWPOSCHANGED?
??
? 注意: postMessage發(fā)送的消息是隊(duì)列消息,它會(huì)把消息Post到消息隊(duì)列中; SendMessage發(fā)送的消息是非隊(duì)列消息, 被直接送到窗口過(guò)程處理
5. ?PostMessage(PostThreadMessage), SendMessage
PostMessage:把消息放到指定窗口所在的線程消息隊(duì)列中后立即返回。 PostThreadMessage:把消息放到指定線程的消息隊(duì)列中后立即返回。 ?
SendMessage:直接把消息送到窗口過(guò)程處理, 處理完了才返回。
6. ?GetMessage, PeekMessage
PeekMessage會(huì)立即返回 可以保留消息?
GetMessage在有消息時(shí)返回 會(huì)刪除消息?
7. ?TranslateMessage, TranslateAccelerator
TranslateMessage: 把一個(gè)virtual-key消息轉(zhuǎn)化成字符消息(character message),并放到當(dāng)前線程的消息隊(duì)列中,消息循環(huán)下一次取出處理。
TranslateAccelerator: 將快捷鍵對(duì)應(yīng)到相應(yīng)的菜單命令。它會(huì)把WM_KEYDOWN 或 WM_SYSKEYDOWN轉(zhuǎn)化成快捷鍵表中相應(yīng)的WM_COMMAND 或WM_SYSCOMMAND消息, 然后把轉(zhuǎn)化后的 WM_COMMAND或WM_SYSCOMMAND直接發(fā)送到窗口過(guò)程處理, 處理完后才會(huì)返回。?
8. ?消息死鎖( Message Deadlocks)
假設(shè)有線程A和B, 現(xiàn)在有以下下步驟?
1) 線程A SendMessage給線程B, A等待消息在線程B中處理后返回 ?
2) 線程B收到了線程A發(fā)來(lái)的消息,并進(jìn)行處理,在處理過(guò)程中,B也向線程A SendMessgae,然后等待從A返回。 ?
因?yàn)榇藭r(shí), 線程A正等待從線程B返回, 無(wú)法處理B發(fā)來(lái)的消息, 從而導(dǎo)致了線程A,B相互等待, 形成死鎖。多個(gè)線程也可以形成環(huán)形死鎖。 ?
可以使用 SendNotifyMessage或SendMessageTimeout來(lái)避免出現(xiàn)死鎖。?
9. ?BroadcastSystemMessage
我們一般所接觸到的消息都是發(fā)送給窗口的, 其實(shí), 消息的接收者可以是多種多樣的,它可以是應(yīng)用程序(applications), 可安裝驅(qū)動(dòng)(installable drivers), 網(wǎng)絡(luò)設(shè)備(network drivers), 系統(tǒng)級(jí)設(shè)備驅(qū)動(dòng)(system-level device drivers)等, ?
BroadcastSystemMessage這個(gè)API可以對(duì)以上系統(tǒng)組件發(fā)送消息。?
?
1.2 ? MessageMAP的形成
? ? MFC也定義了豐富的宏來(lái)簡(jiǎn)化消息響應(yīng)的代碼,要想真正了解MFC的消息機(jī)制,必需弄清楚這些宏。
第一個(gè)宏:DECLARE_MESSAGE_MAP()
作用:為一個(gè)消息響應(yīng)類聲明必需的成員變量和成員函數(shù)。?
我們?cè)诖翱陬悺?yīng)用程序類、文檔類、視圖類、以及這些類的子類的定義中,都能看到DECLARE_MESSAGE_MAP()宏,通常被自動(dòng)化工具聲明在類的最后部分,如:
// 生成的消息映射函數(shù)
protected:
DECLARE_MESSAGE_MAP()
};1234
DECLARE_MESSAGE_MAP()宏定義如下(在DLL類型和WINDOWS程序類型下,定義會(huì)有不同,本文只分析非DLL類型,下同):
#define DECLARE_MESSAGE_MAP()
private:
static const AFX_MSGMAP_ENTRY _messageEntries[];
protected:
static const AFX_MSGMAP messageMap;
virtual const AFX_MSGMAP* GetMessageMap() const;12345678
可以看到,宏DECLARE_MESSAGE_MAP()定義了兩個(gè)靜態(tài)成員變量,并重載了一個(gè)虛函數(shù)。下面分析一下這三個(gè)成員:_messageEntries被定義為一個(gè)AFX_MSGMAP_ENTRY類型的數(shù)組。
結(jié)構(gòu)體AFX_MSGMAP_ENTRY的定義如下:
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's
UINT_PTR nSig; // signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
}; 123456789
通過(guò)查看源代碼中的注釋,可以看出AFX_MSGMAP_ENTRY定義了一個(gè)消息入口,或者說(shuō)定義了一個(gè)消息到函數(shù)的映射關(guān)系。 nMessage和nCode確定一條消息的內(nèi)容,nID和nLastID確定了一條消息的來(lái)源,而nSig和pfn確定了消息的響應(yīng)函數(shù)和調(diào)用方式。
通過(guò)對(duì)消息響應(yīng)過(guò)程的源碼分析可知,nSig事實(shí)上是一系列編碼,每一種編碼代表一種響應(yīng)函數(shù)的類型,包括返回值、參數(shù)信息等。在響應(yīng)消息的時(shí)候,將把pfn指向的函數(shù)指針強(qiáng)制類型轉(zhuǎn)換為nSig代表的函數(shù)類型,然后再調(diào)用。
pfn的類型AFX_PMSG定義如下,意為CCmdTarget的成員函數(shù):
typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);1
由此我們可以得出:靜態(tài)成員_messageEntries是一個(gè)消息到函數(shù)的映射表,或叫消息入口表。通過(guò)查找此表,可以找到消息的響應(yīng)函數(shù)。
?
DECLARE_MESSAGE_MAP()宏聲明的另一個(gè)靜態(tài)成員變量messageMap被定義為AFX_MSGMAP類型。AFX_MSGMAP定義如下:
struct AFX_MSGMAP
{
#ifdef _AFXDLL
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
#else
const AFX_MSGMAP* pBaseMap;
#endif
const AFX_MSGMAP_ENTRY* lpEntries;
}; 123456789101112131415
過(guò)濾掉_AFXDLL(當(dāng)MFC工程是以動(dòng)態(tài)鏈接庫(kù)為目標(biāo)代碼編譯時(shí),使用_AFXDLL宏)的影響,可以簡(jiǎn)化為如下:
struct AFX_MSGMAP
{
const AFX_MSGMAP* pBaseMap;
const AFX_MSGMAP_ENTRY* lpEntries;
}; 12345
可見(jiàn)結(jié)構(gòu)體AFX_MSGMAP中定義了兩個(gè)指針,pBaseMap指向另一個(gè)AFX_MSGMAP,lpEntries指向一個(gè)消息入口表。可以推想,在響應(yīng)消息時(shí),一定是在lpEntries指向的消息入口表中尋找響應(yīng)函數(shù),也可能會(huì)在pBaseMap指向的結(jié)構(gòu)體中做同樣的響應(yīng)函數(shù)尋找操作。其圖示如下:
至于DECLARE_MESSAGE_MAP()宏重載的虛函數(shù)GetMessageMap,可以猜測(cè)只是用來(lái)返回成員messageMap 的地址而已。因?yàn)镚etMessageMap是虛函數(shù),所以系統(tǒng)只要通過(guò)調(diào)用消息響應(yīng)類的基類CCmdTarget類的GetMessageMap函數(shù),便可以找到最后一級(jí)子類的消息映射信息。我們將在接下來(lái)對(duì)其它幾個(gè)宏的分析中得到相同的結(jié)論。
第二個(gè)重要的宏:BEGIN_MESSAGE_MAP
作用:定義DECLARE_MESSAGE_MAP宏聲明的靜態(tài)變量。
BEGIN_MESSAGE_MAP定義的源代碼如下:
//以下這一段是重點(diǎn),理解了這一段,就全部理解了MFC框架中的消息映射機(jī)制。
#define BEGIN_MESSAGE_MAP(theClass, baseClass)
const AFX_MSGMAP* theClass::GetMessageMap() const
{ return &theClass::messageMap; }
///
AFX_COMDAT const AFX_MSGMAP theClass::messageMap =//定義Message
{ &baseClass::messageMap, &theClass::_messageEntries[0] };//傳入消息入口地址
///
AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] =//消息條目?jī)?nèi)容
{1234567891011
BEGIN_MESSAGE_MAP宏有兩個(gè)參數(shù),theClass表示為當(dāng)前類,bassClass為當(dāng)前類的父類。
BEGIN_MESSAGE_MAP宏首先定義了函數(shù)GetMessageMap的函數(shù)體,如前文所述,直接返回當(dāng)前類的成員變量messageMap的地址。
const AFX_MSGMAP* theClass::GetMessageMap() const
{ return &theClass::messageMap; } 12
然后初始化了當(dāng)前類的成員變量messageMap。messageMap的pBaseMap指針指向其父類(基類)的messageMap成員,lpEntries指針指向當(dāng)前類的_messageEntries數(shù)組的首地址。
AFX_COMDAT const AFX_MSGMAP theClass::messageMap =
{ &baseClass::messageMap, &theClass::_messageEntries[0] }; 12
最后,定義了_messageEntries數(shù)組初始化代碼的開(kāi)始部分。
AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] =
{ 12
第二個(gè)重要的宏END_MESSAGE_MAP() ?
作用:定義_messageEntries數(shù)組初始化代碼的結(jié)束部分。
#define END_MESSAGE_MAP()
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
}; 12345
在DECLARE_MESSAGE_MAP和END_MESSAGE_MAP之間還有一些宏,如ON_COMMAND、ON_WM_CREATE 等,這些宏最終都會(huì)被生成一條AFX_MSGMAP_ENTRY結(jié)構(gòu)體數(shù)據(jù),并成為_(kāi)messageEntries消息映射表數(shù)據(jù)的一個(gè)元素。我們以常見(jiàn)的ON_COMMAND宏為例。ON_COMMAND宏的源代碼為:
#define ON_COMMAND(id, memberFxn)
{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v,
static_cast (memberFxn) },12345
通過(guò)以上分析,我們可以得到一個(gè)鏈表式的數(shù)據(jù)結(jié)構(gòu),子類的messageMap成員為鏈表的頭節(jié)點(diǎn)。鏈表的每個(gè)節(jié)點(diǎn)都包含一個(gè)消息入口表。MFC的消息處理函數(shù)CCmdTarget::OnCmdMsg正是通過(guò)這樣一個(gè)鏈表查找到消息的響應(yīng)函數(shù),并調(diào)用該函數(shù)來(lái)響應(yīng)消息。
?
?
1.3 ? 整體過(guò)程分析
鼠標(biāo)點(diǎn)擊,產(chǎn)生單擊事件,鼠標(biāo)設(shè)備驅(qū)動(dòng)程序根據(jù)用戶事件,轉(zhuǎn)換成消息,并放置于WINDOWS的系統(tǒng)隊(duì)列中
WINDOWS將系統(tǒng)隊(duì)列中的消息取出,并投擲于消息對(duì)應(yīng)的應(yīng)用程序所屬的線程隊(duì)列。
每個(gè)應(yīng)用程序在創(chuàng)建時(shí),系統(tǒng)都會(huì)為其創(chuàng)建一個(gè)消息隊(duì)列,發(fā)送給應(yīng)用程序的消息都存放在該消息隊(duì)列中,等待被處理。 而應(yīng)用程序的消息引擎
MSG msg;
while (GetMessage(msg, NULL, NULL, NULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}123456
會(huì)不停的從自己的專屬消息隊(duì)列中獲取消息,并進(jìn)行消息的翻譯和轉(zhuǎn)發(fā)(TranslateMessage和DiapatchMessage)
GetMessage:從線程隊(duì)列中取消息,取出后對(duì)應(yīng)的消息會(huì)從隊(duì)列中刪除;若無(wú)消息,則阻塞
TranslateMessage:把鍵盤消息轉(zhuǎn)換成對(duì)應(yīng)的ASCII字符內(nèi)存,并重新放置于隊(duì)列中,等待取出 DispatchMessage:在注冊(cè)窗口對(duì)象時(shí),有如下代碼:及設(shè)置對(duì)口的消息處理回調(diào)函數(shù)
WNDCLASS wc;
......
wc.lpfnWndProc = (WNDPROC)WndProc;
......1234
在窗口類的定義中,有如下代碼:
DECLARE_MESSAGE_MAP()
......
BEGIN_MESSAGE_MAP(CMsgTestDlg, CDialog)
......
ON_MESSAGE(WM_USER_SEND_MSG, &CMsgTestDlg::HandleSendMsg)
ON_MESSAGE(WM_USER_POST_MSG, &CMsgTestDlg::HandlePostMsg)
......
END_MESSAGE_MAP()12345678
該段代碼,是的所有該類型的對(duì)話框?qū)ο蠊蚕硪粋€(gè)消息MAP表,DispatchMessage會(huì)根據(jù)消息所屬的窗口,調(diào)用回調(diào)函數(shù)WndProc,而WndProc則getMessageMap,根據(jù)消息類型,在map中進(jìn)行匹配查找,找到對(duì)應(yīng)的處理函數(shù),并調(diào)用,則消息處理完畢。
?
1. 4 ?系統(tǒng)消息隊(duì)列
當(dāng)操作系統(tǒng)啟動(dòng)并初始化時(shí),線程Raw Input Thread(RIT)就會(huì)啟動(dòng),并創(chuàng)系統(tǒng)硬件輸入隊(duì)列(System Hardware Input Queue)(SHIQ). 對(duì)于外部的硬件事件(鼠標(biāo)或者鍵盤),硬件驅(qū)動(dòng)會(huì)將事件轉(zhuǎn)換成消息,并存放到SHIQ中,而RIT線程就專門負(fù)責(zé)處理SHIQ中的消息,把消息分發(fā)到對(duì)應(yīng)線程的消息隊(duì)列里面。
?
1.5 ?線程消息隊(duì)列
對(duì)于每個(gè)用MFC開(kāi)發(fā)的GUI程序,他們都有一個(gè)CMy***App,該類繼承自CWinApp,而CWinApp繼承自CWinThread, CWinApp是一個(gè)GUI線程,系統(tǒng)會(huì)為其維護(hù)一個(gè)THREADINFO結(jié)構(gòu),
消息隊(duì)列包含在一個(gè)叫THREADINFO的結(jié)構(gòu)中,有四個(gè)隊(duì)列:
Sent Message Queue 發(fā)送消息隊(duì)列
Posted Message Queue 登記消息隊(duì)列
Visualized Input Queue 輸入消息隊(duì)列
Reply Message Queue 響應(yīng)消息隊(duì)列ml>
Sent Message Queue: 該隊(duì)列保存其他程序通過(guò)SendMessage給該線程發(fā)送的消息 ?
Posted Message Queue: 該隊(duì)列保存其他隊(duì)列通過(guò)PostMessage給該線程發(fā)送的消息 ?
Visualized Input Queue: 保存系統(tǒng)隊(duì)列分發(fā)過(guò)來(lái)的消息,比如鼠標(biāo)或者鍵盤的消息?
Reply Message Queue: 保存向窗體發(fā)送消息后的結(jié)果,比如sendMessage操作結(jié)束后,接收消息方會(huì)發(fā)送一個(gè)Reply消息給發(fā)送方的Reply隊(duì)列中,以喚醒發(fā)送隊(duì)列。
這些隊(duì)列如何產(chǎn)生的?線程是內(nèi)核對(duì)象,我們看看線程信息是如何定義的,包含哪些內(nèi)容,分析如下THREADINFO結(jié)構(gòu)體定義,可以得出一些信息:?
2.0 ? ?MFC技術(shù)內(nèi)幕:執(zhí)行期類型識(shí)別與動(dòng)態(tài)創(chuàng)建
?
2.1 ? ?WIN32運(yùn)行機(jī)制
Win32 SDK程序的入口點(diǎn) WinMain()
(1) 定義應(yīng)用程序要用的變量;
HWND hwnd;
MSG msg;
...123
(2) 定義窗口類(WNDCLAS)變量wndclass;
WNDCLAS wndclass;1
(3) 根據(jù)窗口類結(jié)構(gòu)填寫各條款,形成初始化的窗口類;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
...12
(4) 利用RegisterClass()函數(shù)注冊(cè)初始化好的窗口類wndclass,注冊(cè)失敗則輸出信息并返回操作系統(tǒng),成功則跳過(guò)if繼續(xù)執(zhí)行;
if(!RegisterClass(&wndclass))
{ //注冊(cè)失敗處理 }12
(5) 根據(jù)窗口類建立窗口:
hwnd = CreateWindow(...);1
(6) 在屏幕上顯示窗口;
ShowWindow(...);
UpdateWindow(...); // 發(fā)出WM_PAINT消息12
(7) 進(jìn)入消息循環(huán);
while(GetMessage(&msg, NULL, 0, 0)) // 從調(diào)用線程的消息隊(duì)列里取得一個(gè)消息并將其放于指定的結(jié)構(gòu)msg {?
TranslateMessage(&msg); // 將虛擬鍵消息轉(zhuǎn)換為字符消息
DispatchMessage(&msg); // 調(diào)度一個(gè)消息給窗口程序
}1234
(8) return (msg.wParam);
其中窗口函數(shù):
LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
....
switch (message) {
case WM_CREATE:
// 響應(yīng)WM_CREATE消息的處理過(guò)程
case WM_PAINT:
//...
default:
return (DefWindowProc(hWnd, message, wParam, lParam)); // 不愿處理的消息交給默認(rèn)處理函數(shù)解決
}12345678910111213
MFC編寫的Windows應(yīng)用程序的運(yùn)行機(jī)制:
創(chuàng)建應(yīng)用程序類(CWinApp的派生類)全局對(duì)象 theAppe?
調(diào)用應(yīng)用程序類的構(gòu)造函數(shù)初始化對(duì)象 theApp
配置內(nèi)存,并設(shè)定成員初值
調(diào)用WinMain()主函數(shù)
獲取theApp的指針;
pApp = AfxGetApp();
pThread = AfxGetThread();12
全局初始化();
AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)1
設(shè)置錯(cuò)誤處理模式:
SetErrorMode();1
設(shè)置模塊資源句柄和填寫應(yīng)用程序初始化狀態(tài)數(shù)據(jù);
初始化主線程特定數(shù)據(jù),并把消息隊(duì)列盡量加大;
AfxInitThread();1
應(yīng)用程序初始化;
pApp->InitApplication(); // 這個(gè)函數(shù)在MFC中已作廢,最好不要重載它
pThread->InitInstance(); // 注冊(cè)窗口類,產(chǎn)生窗口,顯示窗口,須改寫(注:下面是MDI程序中InitInstance()中LoadFrame()函數(shù)引發(fā)的窗口注冊(cè),創(chuàng)建等一系列過(guò)程,只寫出了主要的一些函數(shù)調(diào)用過(guò)程,具體函數(shù)作用查查MFC類手冊(cè)或MSDN,我就不羅嗦了: CFrameWnd::LoadFrame(), CFrameWnd::Create(), CWnd::CreateEx(), CFrameWnd::PreCreateWindow(), AfxEndDeferRegisterClass(), _AfxRegisterWithIcon(), AfxRegisterClass(), CreateWindowEx();)
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();1234
運(yùn)行程序, 進(jìn)入消息處理循環(huán);
pThread->Run();1
如果消息隊(duì)列中沒(méi)有消息: OnIdle();
當(dāng)消息隊(duì)列中有消息且不是WM_QUIT,分發(fā)給消息處理函數(shù):PumpMessage();?
當(dāng)收到WM_QUIT消息:ExitInstance();
調(diào)用析構(gòu)函數(shù),退出應(yīng)用程序,控制權(quán)交還操作系統(tǒng);
其中Run()中的消息映射過(guò)程如下(大致的函數(shù)調(diào)用過(guò)程):
wndcls.lpfnWndProc = DefWindowProc; // 窗口類注冊(cè)的窗口函數(shù)
CWnd::DefWindowProc();
::CallWindowProc(pfnWndProc, m_hWnd, nMsg, wParam, lParam);
AfxWndProc();
AfxCallWndProc();
CWnd::WindowProc();
CWnd::OnWndMsg();
如果是WM_COMMAND: OnCommand()
OnCmdMsg();
如果是WM_NOTIFY: OnNotify()
OnCmdMsg();1234567891011
其中重點(diǎn)在CWnd::WindowProc()及其后的消息分派過(guò)程:?
當(dāng)一個(gè)消息抵達(dá)時(shí),框架調(diào)用了CMainFrame從CWnd繼承下來(lái)的虛擬WindowProc()函數(shù);?
WindowProc函數(shù)調(diào)用OnWndMsg函數(shù);?
而OnWndMsg又調(diào)用GetMessageMap來(lái)獲取一個(gè)指向CMainFrame::messageMap的指針,并搜索CMainFrame::_messageEntries來(lái)獲取一個(gè)其消息ID與當(dāng)前正等待處理的消息ID相匹配的條目,如果找到了該條目,對(duì)應(yīng)的CMainFrame函數(shù)(其地址與該消息ID一同存儲(chǔ)在_messageEntries數(shù)組中)就被調(diào)用。否則,OnWndMsg參考CMainFrame::messageMap獲得一個(gè)指向CFrameWnd::messageMap的指針并為基類重復(fù)該過(guò)程。如果基類沒(méi)有該消息的處理程序,則框架將上升一個(gè)級(jí)別,參考基類的基類,相當(dāng)系統(tǒng)地沿著繼承鏈向上走,直到它找到一個(gè)消息處理程序或者該消息傳遞Windows進(jìn)行默認(rèn)處理為止;”
原文:https://blog.csdn.net/zt_xcyk/article/details/72743250?
?
總結(jié)
- 上一篇: 关于H264通过RTP传输的打包方式
- 下一篇: 软件开发的“三重门”