MFC添加自定义消息及重写消息过程
由于MFC中無法通過類向導來自定義消息,所以需要手動添加,主要過程如下:
本文基于vs2008下通過線程實現數據實時更新的對話框運用程序
1. 定義消息(Resource.h文件中):
由于很多新控件也會用到WM_USER消息,所以定義WM_USER+100或更高,避免沖突
#define WM_UPDATEDATA (WM_USER + 100)?
2015-3-18 更新
1. 定義消息( stdafx.h文件中):
由于很多新控件也會用到WM_USER消息,所以定義WM_USER+100或更高,避免沖突,消息定義到stdafx.h文件中,Resource.h文件由系統維護,經常出現自定義的消息丟失。
#define WM_UPDATEDATA (WM_USER + 100)2. 聲明消息響應函數:
放在消息映射部分,即DECLARE_MESSAGE_MAP()之前
// 實現 protected:HICON m_hIcon;// 生成的消息映射函數virtual BOOL OnInitDialog();afx_msg void OnPaint();afx_msg HCURSOR OnQueryDragIcon();afx_msg LRESULT OnUpdateData(WPARAM wParam, LPARAM lParam);DECLARE_MESSAGE_MAP()3. 實現消息響應函數:
由于在vs2008中未定義ON_MESSAGE_VOID,所以定義的消息響應函數必須有返回值,即LRESULT;而消息參數是WPARAM wParam和LPARAM lParam
LRESULT CEditTestDlg::OnUpdateData(WPARAM wParam, LPARAM lParam) {UpdateData(wParam);return 0; }4. 將消息映射到消息處理函數:
放在AFX_MSG_MAP之前,把WM_UPDATEDATA消息映射到消息響應函數OnUpdateData
BEGIN_MESSAGE_MAP(CEditTestDlg, CDialog)ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_MESSAGE(WM_UPDATEDATA, OnUpdateData)//}}AFX_MSG_MAPON_BN_CLICKED(IDC_BUTTON1, &CEditTestDlg::OnBnClickedButton1) END_MESSAGE_MAP()5. 發送自定義消息:
以下函數是在一個新的線程中調用的,PostMessage,SendMessage,SendMessageTimeout均可用,推薦使用SendMessageTimeout函數
static UINT SendMsgThread(LPVOID lpParam) {CEditTestDlg *dlg = (CEditTestDlg*) lpParam;int i = 0;while (i < 100){Sleep(20);i += 1;dlg->m_value2.Format(_T("%d"), i);//PostMessage(dlg->m_hWnd,WM_UPDATEDATA,FALSE,NULL);//SendMessage(dlg->m_hWnd,WM_UPDATEDATA,FALSE,NULL);SendMessageTimeout(dlg->m_hWnd, WM_UPDATEDATA, FALSE,NULL, SMTO_BLOCK, 1000, NULL);}return 0; }?
?
如果用戶需要一個定義整個系統唯一的消息,可以調用SDK函數RegisterWindowMessage定義消息:
 在Resource.h中將代碼
修改為
static UINT WM_UPDATEDATA=RegisterWindowMessage(_T("User"));并使用ON_REGISTERED_MESSAGE宏指令取代ON_MESSAGE宏指令,其余步驟同上。
 注:如果仍然使用ON_MESSAGE宏指令,compile可以通過,但是無法響應消息。
//*************重寫消息
c++消息機制是遇到兩個參數不是很明白 查了下資料 記錄下(以下都是我個人理解,錯誤請幫忙指正):
?
LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,?LPARAM lParam);
這是一個向指定的窗口發送指定消息的函數
HWND hWnd?這個參數是窗口的句柄,相當于id的玩意 不難理解
UINT Msg?這個是要發送的消息?如:WM_CLOSE,WM_SETTEXT。。。。。。。好多
WPARAM wParam和LPARAM lParam這兩個參數不是很陌生 下面也是我看其他的資料得到的理解
?
Windows的消息必須參考幫助文件才能知道具體的含義。如果是你定義的消息,愿意怎么使這兩個參數都行。但是習慣上,我們愿意使用LPARAM傳 遞地址,而WPARAM傳遞其他參數。”
接下來談談什么是消息機制:系統將會維護一個或多個消息隊列,所有產生的消息都回被放入或是插入隊列中。系統會在隊列中取出每一條消息,根據消息的接收句柄而將該消息發送給擁有該窗口的程序的消息循環。每一個運行的程序都有自己的消息循環,在循環中得到屬于自己的消息并根據接收窗口的句柄調用相應的窗口過程。而在沒有消息時消息循環就將控制權交給系統所以Windows可以同時進行多個任務。下面的偽代碼演示了消息循環的用法:
?
?
例如:主程序MyDlg.cpp
1.自定義消息:#define WM_TRAY WM_USER 100
2.函數原形:afx_msg LRESULT OnTrayNotify(WPARAM wParam,LPARAM lParam);
3.消息映射:ON_MESSAGE(WM_TRAY,OnTrayNotify)
4.原函數:
LRESULT CMyDlg::OnTrayNotify(WPARAM wParam,LPARAM lParam)
{
return m_tray.OnTrayNotify(wParam,lParam);
}
?
上面程序呢?主要過程是這樣?自定義了一個消息WM_TRAY?
再創建一個函數OnTrayNotify?
然后將消息和函數綁定在一起ON_MESSAGE(WM_TRAY,OnTrayNotify)
每當接受這個消息時就運行函數
而WPARAM wParam,LPARAM lParam這兩個參數就是消息附帶的參數跟著消息一起傳遞過來
?
在Win32 SDK中消息本身是作為一個結構體記錄傳遞給應用程序的,這個記錄中包含了消息的類型以及其他信息。這個記錄類型叫做MSG,它在window中是這樣聲明的:
 typedef struct tagMSG { // msg
 HWND hwnd; //窗口句柄
 UINT message; //消息常量標識符
 WPARAM wParam; //32位消息的特定附加信息,具體表示什么處決于message
 LPARAM lParam; //32位消息的特定附加信息,具體表示什么處決于message
 DWORD time; //消息創建時的時間
 POINT pt; //消息創建時的鼠標位置
 } MSG;
hwnd 接收消息的32位窗口句柄。窗口可以是任何類型的屏幕對象,因為Win32能夠維護大多數可視對象的句柄(窗口、對話框、按鈕、編輯框等)。
 message 用于區別其他消息的常量值,這些常量可以是Windows單元中預定義的常量,也可以是自定義的常量。
 wParam 通常是一個與消息有關的常量值,也可能是窗口或控件的句柄。 lParam 通常是一個指向內存中數據的指針。由于wParam,lParam和指針都是32位的,需要時可以強制類型轉換。具體表示什么,與message相關,他們是事先定義好的。
 如果自定義消息:#define WM_MYMESSAGE WM_USER+100,需確定wParam,lParam的意義 (假設wParam=0時發送數據,wParam=1時接收數據,lParam為CMyClass* 指針,指向一個CMyClass對象,準備要發送的數據或接收數據 發送WM_MYMESSAGE時 SendMessage(hwnd,WM_MYMESSAGE,0,pMyClassObject) 接收消息的窗口,接收WM_MYMESSAGE中(CMyClass*)lParam參數即pMyClassObject傳過來的數據
?
//**********
1. 怎樣使用MFC發送一個消息用MFC發送一個消息的方法是,
首先,應獲取接收消息的CWnd類對象的指針;
然后,調用CWnd的成員函數SendMessage( )。
LRESULT Res=pWnd->SendMessage(UINT Msg, WPARAM wParam, LPARAM lParam);
pWnd指針指向目標CWnd類對象。變量Msg是消息,wParam和lParam變量包含消息的參數,如鼠標單擊哪里或選擇了什么菜單項。目標窗口返回的消息結果放在變量Res中。
發送消息到一個沒有CWnd類對象的窗口,可以用下列目標窗口的句柄直接調用Windows API:
LRESULT Res=::SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
這里的hWnd是目標窗口的句柄。
2. 怎樣用MFC寄送一個消息
用MFC寄送一個消息與發送一個消息幾乎相同,但寄送時用PostMessage( ) ,而不是用SendMessage( );返回值Res也不一樣,Res不是一個由目標窗口返回的值,而是一個布爾值,用來表示消息是否成功地放到消息隊列中。
3. 檢索一個寄送消息
正常情況下,一旦消息被寄送后,應用程序在后臺發送它。但是在特殊情況下,需要你自己去刪除一個消息,例如想在應用程序接收到某種消息之前停止應用程序。有兩種方法可以從應用程序消息隊列中刪除一個消息,但這兩種方法都沒有涉及MFC。
■ 第一種方法:在不干擾任何事情之下窺視消息隊列,看看一個消息是否在那里。
BOOL res=::PeekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg ) ;
■ 第二種方法:實際上是等待,一直等到一個新的消息到達隊列為止,然后刪除并返回該消息。
BOOL res=::GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);
在這兩種方法中,變量hWnd指定要截獲消息的窗口,如果該變量設為NULL,所有窗口消息將被截獲。wMsgFilterMin和wMsgFilterMax變量與SendMessage( )中的變量Msg相對應,指定查看消息的范圍。如果用"0,0",則所有的消息都將被截獲。如果用WM_KEYFIRST,WM_KEYLAST或WM_MOUSEFIRST,WM_MOUSELAST,則所有鍵盤或鼠標的消息將被截獲。wRemoveMsg變量指定PeekMessage( )是否應該真正地從隊列中刪除該消息。(GetMessage( )總是刪除消息)。該變量可以取兩個值:
■ PM_REMOVE,PeekMessage( )將刪除消息。
■ PM_NOREMOVE,PeekMessage( )將把消息留在隊列里,并返回它的一個拷貝。
當然,如果把消息留在消息隊列中,然后再次調用PeekMessage( )查看相同類型的消息,則將返回完全相同的消息。
lpMsg變量是一個指向MSG結構的指針,MSG包含檢索到的消息。
typedef struct tagMSG {
HWND hwnd; // window handle message is intended for
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time; // the time the message was put in the queue
POINT pt; // the location of the mouse cursor when the
// message was put in the queue
} MSG;
4. MFC怎樣接收一個寄送的消息
MFC處理一個寄送和發送消息的唯一明顯不同是寄送的消息要在應用程序的消息隊列中花費一些時間。在消息泵(message pump)彈出它之前,它要一直在隊列中。
消息泵
MFC應用程序中的消息泵在CWinApp的成員函數Run()中。應用程序開始運行時,Run()就被調用,Run()把時間分割成兩部分。一部分用來執行后臺處理,如取消臨時CWnd對象;另一部分用來檢查消息隊列。當一個新的消息進來時,Run()抽取它—即用GetMessage( )從隊列中取出該消息,運行兩個消息翻譯函數,然后用DispatchMessage( )函數調用該消息預期的目標窗口進程。
消息泵調用的兩個翻譯函數是PreTranslateMessage( )和::TranslateMessage( )。目標窗口的MFC類可調用reTranslateMessage在發送消息給它之前進行消息翻譯,例如,CFrameWnd用PreTranslateMessage( )將加速鍵(如,Ctrl+S存儲文件)轉換為命令消息。翻譯前的消息通常被處理掉,而翻譯后的消息(如果有的話)將被重新寄送到隊列里。::TranslateMessage是一個窗口函數,將原始鍵碼轉換為鍵字符。消息一旦被DispatchMessage()發送,MFC處理它就像處理SendMessage()發送的消息一樣。
5. MFC怎樣處理一個接收到的消息
處理接收到的消息的目的非常簡單:將消息指向一個函數,該函數通過消息中的消息標識符處理它。非MFC窗口用簡單的case語句來實現該目標,每個case語句執行一些函數,或調用其他一些函數。
MainWndProc(HWND hWnd, UINT message, W PARAM wParam,LPARAM lParam)
{
switch(message)
{
case WM_CREATE:
: : :
break;
case WM_PAINT:
: : :
break;
default:
return(DefWindowProc(hWnd,message,wParam,lParam));
}
return(NULL);
}
任何遺漏的消息將被傳輸到一個默認的消息處理函數,但是,case語句不能很好地適應C++和封裝技術。在C++環境中,要求消息被一個專門處理該類型消息的類的成員函數處理。因此,MFC不采用case語句,而采用更加復雜和回旋的方法。但它允許用私有類處理消息,而只需做下面三件事情:
■ 從將要接收消息的CWnd類對象派生類(對于命令消息是CCmdTarget)。
■ 在派生類中寫一個處理消息的成員函數。
■ 在類中定義一個查找表(叫做消息映像),該表具有成員函數的條目和它要處理的消息的標識符。
然后,MFC依次調用下面的函數,指引輸入消息到處理函數。
1) AfxWndProc( )接收消息,尋找消息所屬的CWnd對象,然后調用AfxCallWndProc( )。
2) AfxCallWndProc( )存儲消息(消息標識符和參數)供未來參考,然后調用WindowProc( )。
3) WindowProc( ) 發送消息給OnWndMsg( ) ,然后,如果消息未被處理,則發送給DefWindowproc( )。
4) OnWndMsg( )要么為WM_COMMAND消息調用OnCommand( ),要么為WM_NOTIFY消息調用OnNotify( )。任何被遺漏的消息都將是一個窗口消息。OnWndMsg( )搜索類的消息映像,以找到一個能處理任何窗口消息的處理函數。如果OnWndMsg( )不能找到這樣的處理函數,則把消息返回到WindowProc( ),由它將消息發送給DefWindowProc( )。
5) OnCommand()查看這是不是一個控件通知(lParam不是NULL);如果它是,OnCommand( )就試圖將消息映射到制造通知的控件;如果它不是一個控件通知,或者控件拒絕映射的消息,OnCommand( )就調用OnCmdMsg( )。
6) OnNotify( )也試圖將消息映射到制造通知的控件;如果映射不成功, OnNotify( )就調用相同的OnCmdMsg( )函數。
7) 根據接收消息的類,OnCmdMsg( )將在一個稱為命令傳遞(Command Routing)的過程中潛在地傳遞命令消息和控件通知。例如,如果擁有該窗口的類是一個框架類,則命令和通知消息也被傳遞到視圖和文檔類,并為該類尋找一個消息處理函數。
為什么要消息映像?
這畢竟是C++語言;為什么OnWndMsg( )不為每個窗口消息調用一個預定義的虛擬函數?因為它太占CPU。若是那樣,當掃描一個消息映像以加速該過程時,OnWndMsg( )可能會做出意想不到的事情,并陷入匯編器。注意通過重載WindowProc( )、OnWndMsg( )、OnCommand( )、OnNotify( ) 或OnCmdMsg( )可以修改這一過程。重載OnWndMsg( )可以在窗口消息被排序之前插入該過程。重載OnCommand( )或OnNotify( )可以在消息被反射之前插入該過程。
?
//***********
windows消息機制(MFC)
消息分類與消息隊列
Windows中,消息使用統一的結構體(MSG)來存放信息,其中message表明消息的具體的類型,
而wParam,lParam是其最靈活的兩個變量,為不同的消息類型時,存放數據的含義也不一樣。
time表示產生消息的時間,pt表示產生消息時鼠標的位置。
按照類型,Windows將消息分為:
(0) 消息ID范圍
系統定義消息ID范圍:[0x0000, 0x03ff]
 用戶自定義的消息ID范圍:?
 WM_USER:?0x0400-0x7FFF?(例:WM_USER+10)?
 WM_APP(winver> 4.0):0x8000-0xBFFF?(例:WM_APP+4)?
 RegisterWindowMessage:0xC000-0xFFFF【用來和其他應用程序通信,為了ID的唯一性,使用::RegisterWindowMessage來得到該范圍的消息ID?】
(1) 窗口消息:即與窗口的內部運作有關的消息,如創建窗口,繪制窗口,銷毀窗口等。
? ? ?可以是一般的窗口,也可以是MainFrame,Dialog,控件等。?
? ???如:WM_CREATE, WM_PAINT, WM_MOUSEMOVE, WM_CTLCOLOR, WM_HSCROLL等
(2) 當用戶從菜單選中一個命令項目、按下一個快捷鍵或者點擊工具欄上的一個按鈕,都將發送WM_COMMAND命令消息。
? ? ?LOWORD(wParam)表示菜單項,工具欄按鈕或控件的ID;如果是控件, HIWORD(wParam)表示控件消息類型。
? ? ?#define?LOWORD(l) ((WORD)(l))
? ? ?#define?HIWORD(l) ((WORD)(((DWORD)(l) >> 16) & 0xFFFF))
(3) 隨著控件的種類越來越多,越來越復雜(如列表控件、樹控件等),僅僅將wParam,lParam將視為一個32位無符號整數,已經裝不下太多信息了。
? ? 為了給父窗口發送更多的信息,微軟定義了一個新的WM_NOTIFY消息來擴展WM_COMMAND消息。
? ? WM_NOTIFY消息仍然使用MSG消息結構,只是此時wParam為控件ID,lParam為一個NMHDR指針,
? ? 不同的控件可以按照規則對NMHDR進行擴充,因此WM_NOTIFY消息傳送的信息量可以相當的大。
注:Window 9x 版及以后的新控件通告消息不再通過WM_COMMAND 傳送,而是通過WM_NOTIFY 傳送,
 ? ? ? 但是老控件的通告消息, 比如CBN_SELCHANGE 還是通過WM_COMMAND 消息發送。
(4) windwos也允許程序員定義自己的消息,使用SendMessage或PostMessage來發送消息。
windows消息還可以分為:
(1) 隊列消息(Queued Messages)?
 消息會先保存在消息隊列中,消息循環會從此隊列中取出消息并分發到各窗口處理?
 如:WM_PAINT,WM_TIMER,WM_CREATE,WM_QUIT,以及鼠標,鍵盤消息等。
 其中,WM_PAINT,WM_TIMER只有在隊列中沒有其他消息的時候才會被處理,
 WM_PAINT消息還會被合并以提高效率。其他所有消息以先進先出(FIFO)的方式被處理。
(2) 非隊列消息(NonQueued Messages)?
 消息會繞過系統消息隊列和線程消息隊列,直接發送到窗口過程進行處理?
 如:WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSOR,WM_WINDOWPOSCHANGED
Windows系統的整個消息系統分為3個層級:
? ? ① Windows內核的系統消息隊列
? ? ② App的UI線程消息隊列
? ? ③ 處理消息的窗體對象
Windows內核維護著一個全局的系統消息隊列;按照線程的不同,系統消息隊列中的消息會分發到應用程序的UI線程的消息隊列中;
應用程序的每一個UI線程都有自己的消息循環,會不停地從自己的消息隊列取出消息,并發送給Windows窗體對象;
每一個窗體對象都使用窗體過程函數(WindowProc)來處理接收到的各種消息。
1 LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)2 {3 PAINTSTRUCT ps;4 HDC hdc;5 6 switch (message)7 {8 case WM_COMMAND:9 break; 10 case WM_PAINT: 11 hdc = BeginPaint(hWnd, &ps); 12 // TODO: 在此添加任意繪圖代碼... 13 EndPaint(hWnd, &ps); 14 break; 15 case WM_DESTROY: 16 PostQuitMessage(0); 17 break; 18 default: 19 return DefWindowProc(hWnd, message, wParam, lParam); 20 } 21 return 0; 22 }需要的話,在WindowProc中,可以用::GetMessageTime獲取當前消息產生的時間,
 用::GetMessagePos獲取當前消息產生時鼠標光標所在的位置。
(1) 各個窗口消息由各個窗體(或控件)自身的WindowProc(虛函數)接收并處理。
(2)?WM_COMMAND命令消息統一由當前活動主窗口的WindowProc接收,經過繞行后,可被其他的CCmdTarget對象處理。
(3)?WM_COMMAND控件通知統一由子窗口(控件)的父窗口的WindowProc接收并處理,也可以進行繞行被其他的CCmdTarget對象處理。
? ? ?(例如:CFormView具備接受WM_COMMAND控件通知的條件,又具備把WM_COMMAND消息派發給關聯文檔對象處理的能力,
? ? ? ? ?所以給CFormView的WM_COMMAND控件通知是可以讓文檔對象處理的。)
? ? ?另外,WM_COMMAND控制通知會先調用ReflectLastMsg反射通知子窗口(控件),如果子窗口(控件)處理了該消息并返回TRUE,則消息會停止分發;
? ? ?否則,會繼續調用OnCmdMsg進行命令發送(如同WM_COMMAND命令消息一樣)。
注:WM_COMMAND命令消息與WM_COMMAND控件通知的相似之處:
 WM_COMMAND命令消息和WM_COMMAND控制通知都是由WindowProc給OnCommand處理,
 OnCommand通過wParam和lParam參數區分是命令消息或通知消息,然后送給OnCmdMsg處理。
 事實上,BN_CLICKED控件通知消息的處理和WM_COMMAND命令消息的處理完全一樣。
 因為該消息的通知代碼是0,ON_BN_CLICKED(id,memberfunction)和ON_COMMAND(id,memberfunction)是等同的。
(4)WM_NOTIFY消息只是對WM_COMMAND控件通知進行了擴展,與WM_COMMAND控件通知具有相同的特點。
SendMessage與PostMessage
PostMessage 把消息投遞到消息隊列后,立即返回;?
 SendMessage把消息直接送到窗口過程處理,處理完才返回。
GetMessage與PeekMessage
GetMessage 有消息且該消息不為WM_QUIT,返回TRUE。
    ? ? ? ? ? ?有消息且該消息為WM_QUIT,返回FALSE。
 ? ? ? ? ? ? ? ? ? 沒有消息時,掛起該UI線程,控制權交還給系統。
 PeekMessage 有消息返回TRUE,如果沒有消息返回FALSE;不會阻塞。
 ? ? ? ? ? ? ? ? ? ?是否從消息隊列中刪除此消息(PM_REMOVE),由函數參數來指定。
要想在沒有消息時做一些工作,就必須使用PeekMessage來抓取消息,以便在沒有消息時,能在OnIdle中執行空閑操作(如下):
1 while (TRUE) 2 {3 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) 4 {5 if (msg.message == WM_QUIT)6 break;7 TranslateMessage(&msg);8 DispatchMessage(&msg);9 } 10 else 11 { 12 OnIdle(); 13 } 14 }例如:MFC使用OnIdle函數來清理一些臨時對象及未使用的動態鏈接庫。
只有在OnIdle返回之后程序才能繼續處理用戶的輸入,因此不應在OnIdle進行較長的任務。
MFC消息處理
在CWnd中,MFC使用OnWndMsg來分別處理各類消息:
如果是WM_COMMAND消息,交給OnCommand處理;然后返回。
如果是WM_NOTIFY消息,交給OnNotify處理;然后返回。
如果是WM_ACTIVATE消息,先交給_AfxHandleActivate處理,再繼續下面的處理。
如果是WM_SETCURSOR消息,先交給_AfxHandleSetCursor處理,然后返回。
如果是其他的窗口消息(包括WM_ACTIVATE消息),則
? 首先在消息緩沖池(一個hash表,用于加快消息處理函數的查找)進行消息匹配,
 ? ? 若匹配成功,則調用相應的消息處理函數;
 ? ? 若不成功,則在消息目標的消息映射數組中進行查找匹配,看它是否能處理當前消息。
 ? 如果消息目標處理了該消息,則會匹配到消息處理函數,調用它進行處理;
否則,該消息沒有被應用程序處理,OnWndMsg返回FALSE。
MFC消息映射
消息映射實際是MFC內建的一個消息分派機制。
把MFC中的宏進行展開(如下),可以得到消息映射表整個全貌。
注:GetMessageMap為虛函數。
 ? ? ?{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0}:對象消息映射表的結束標識
窗口消息只能由CWnd對象來處理,采用向基類直線上朔的方式,來查找對應的消息響應函數進行處理。
一旦找到消息響應函數(若有返回值且為TRUE),就停止上朔。因此,我們經常會看到這樣的代碼:
增加一個消息處理函數來寫我們的邏輯時,MFC ClassWizard會在該函數之前或之后顯示調用其基類對應的函數,保證基類中邏輯被執行。
命令消息可由CCmdTarget對象接收并處理(OnCmdMsg為虛函數),除了向基類直線上朔方式外,還有命令繞行機制(要防止形成圈,死循環)。
在某種程度上,控制通知消息由窗口對象處理是一種習慣和約定。然而,控件通知消息也是可以有CCmdTarget對象接收并處理,并進行命令繞行的。
下圖為MFC經典單文檔視圖框架的命令消息繞行路線:
函數調用過程如下(如果沒有任何對象處理該條WM_COMMAND消息,最后會被::DefWindowProc處理)。
非模態對話框的消息處理
1 static CAboutDlg aboutDlg; 2 aboutDlg.Create(IDD_ABOUTBOX, this); 3 aboutDlg.ShowWindow(SW_SHOW);應用程序只有一個消息循環。
對于窗口消息,非模態對話框(及其子控件)與父窗口(及其子控件)都是用自身的WindowProc函數接收并處理,互不干擾。
對于命令消息,由當前活動主窗口的WindowProc接收(例如:當前活動主窗口為非模態對話框,則命令消息會被非模態對話框接收)。
可以在當前活動主窗口的OnCmdMsg中做命令繞行,使得其他的CCmdTarget對象也可以處理命令消息。
對于控件通知,由其父窗口的WindowProc接收并處理,一般不進行命令繞行被其他的CCmdTarget對象處理。
模態對話框的消息處理
1 CAboutDlg aboutDlg; 2 aboutDlg.DoModal();(1) 模態對話框彈出來后,首先會讓父窗口失效,使其不能接受用戶的輸入(鍵盤鼠標消息)。
1 EnableWindow(hwndParent, FALSE) ;(2) 父窗口消息循環被阻塞(會卡在DoModal處,等待返回),由模態對話框的消息循環來接管(因此整個程序不會卡住)。
? ? 接管后,模態對話框的消息循環仍然會將屬于父窗口及其子控件的窗口消息(不包括鍵盤鼠標相關的窗口消息)發送給它們各自的WindowProc窗口函數,進行響應處理。
(3) 模態對話框銷毀時(點擊IDOK或IDCANCEL),父窗口消息循環重新激活,繼續DoModal后的邏輯。
? ? 激活后,父窗口有可以重新接受用戶的輸入(鍵盤鼠標消息)。
1 EnableWindow(hwndParent, TRUE) ;從上面的過程中,我們可以得到如下結論:
對于窗口消息,模態對話框主窗口(及其子控件)與父窗口(及其子控件)都是用自身的WindowProc函數接收并處理,互不干擾。
只是父窗口(及其子控件)無法接受到鍵盤鼠標消息相關的窗口消息。
對于命令消息,由模態對話框主窗口的WindowProc接收。可以在模態對話框主窗口的OnCmdMsg中做命令繞行,使得其他的CCmdTarget對象也可以處理命令消息。
對于控件通知,由其父窗口的WindowProc接收并處理,一般不進行命令繞行被其他的CCmdTarget對象處理。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的MFC添加自定义消息及重写消息过程的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: C#使用DataContractJson
- 下一篇: 注解 @PostConstruct 与
