windows訊息
摘自: http://www.360doc.com/content/18/0731/13/58275976_774656445.shtml
一個例子:
MSG msg;
while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
TranslateMessage()函式的作用是把虛擬鍵訊息轉換到字元訊息,以滿足鍵盤輸入的需要。
DispatchMessage()函式所完成的工作是把當前的訊息傳送到對應的視窗過程中去。
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( )可以在訊息被反射之前插入該過程。