Windows消息机制-PreTranslateMessage
PreTranslateMessage作用和使用方法
Windows消息機制的流程:
A. 操作系統接收應用程序的窗口消息,將消息投遞到該應用程序的消息隊列中
B. 應用程序在消息循環中調用GetMessage函數從消息隊列中取出一條一條的消息,取出消息后,應用程序可以對消息進行一些預處理。
C. 應用程序調用DispatchMessage,將消息回傳給操作系統。
D. 系統利用WNDCLASS結構體的lpfnWndProc成員保存的窗口過程函數的指針調用窗口過程,對消息進行處理。
??PreTranslateMessage是消息在送給TranslateMessage函數之前被調用的,絕大多數本窗口的消息都要通過這里,比較常用,當需要在MFC之前處理某些消息時,常常要在這里添加代碼.??
????????MFC消息控制流最具特色的地方是CWnd類的虛擬函數PreTranslateMessage(),通過重載這個函數,可以改變MFC的消息控制流程,甚至可以作一個全新的控制流出來。?只有穿過消息隊列的消息才受PreTranslateMessage()影響,采用SendMessage()或其他類似的方式向窗口直接發送的而不經過消息隊列的消息根本不會理睬PreTranslateMessage()的存在。?
?
???????是否調用TranslateMessage()和DispatchMessage()是由一個名稱為PreTranslateMessage()函數的返回值決定的,如果該函數返回TRUE,則不會把該消息分發給窗口函數處理。?
?????傳給PreTranslateMessage()的消息是未經翻譯過的消息,它沒有經過TranslateMessage()處理。可以在該函數中使用(pMsg->wParam==VK_RETURN)來攔截回車鍵。?wParam中存放的是鍵盤上字符的虛擬碼。?
PeekMessage和GetMessage的區別:?
GetMessage在沒有消息的時候等待消息,cpu當然低?
PeekMessage沒有消息的時候立刻返回,所以cpu占用率高。?
因為游戲不能靠windows消息驅動,所以要用PeekMessage();?
?????PretranslateMessage的實現,不得不談到MFC消息循環的實現。MFC通過CWinApp類中的Pumpmessage函數實現消息循環,但是實際的消息循環代碼位于CWinThread中,?CWinApp只是從CWinThread繼承過來。其簡化后的代碼大概如下:?
BOOL?CWinThread::PumpMessage()?
{?
_AFX_THREAD_STATE?*pState?=?AfxGetThreadState();?
?
::GetMessage(&(pState->m_msgCur),?NULL,?NULL,?NULL))?
?
if?(!AfxPreTranslateMessage(&(pState->m_msgCur)))?
{?
::TranslateMessage(&(pState->m_msgCur));?
::DispatchMessage(&(pState->m_msgCur));?
}?
return?TRUE;?
}?
可以看到,PumpMessage在實際的TranslateMessage和DispatchMessage發生之前會調用AfxPreTranslateMessage,AfxPreTranslateMessage又會調用CWnd::WalkPreTranslateTree(雖然也會調用其他函數,但是這個最為關鍵),其代碼如下:?
BOOL?PASCAL?CWnd::WalkPreTranslateTree(HWND?hWndStop,?MSG*?pMsg)?
{?
ASSERT(hWndStop?==?NULL?||?::IsWindow(hWndStop));?
ASSERT(pMsg?!=?NULL);?
?
//?walk?from?the?target?window?up?to?the?hWndStop?window?checking?
//?if?any?window?wants?to?translate?this?message?
?
for?(HWND?hWnd?=?pMsg->hwnd;?hWnd?!=?NULL;?hWnd?=?::GetParent(hWnd))?
{?
CWnd*?pWnd?=?CWnd::FromHandlePermanent(hWnd);?
if?(pWnd?!=?NULL)?
{?
//?target?window?is?a?C?window?
if?(pWnd->PreTranslateMessage(pMsg))?
return?TRUE;?//?trapped?by?target?window?(eg:?accelerators)?
}?
?
//?got?to?hWndStop?window?without?interest?
if?(hWnd?==?hWndStop)?
break;?
}?
return?FALSE;?//?no?special?processing?
}?
?
可以看到,代碼還是很直接的。從接受到消息的窗口層層往上遍歷,并調用PretranslateMessage看是否返回TRUE,是則結束,否則繼續。?
這里有一個地方非常關鍵:CWnd?*pWnd?=?CWnd::FromHandlePermanent(hWnd)?這一句代碼從當前AfxModuleThreadState拿到Permanent句柄表,從而找到hWnd對應的CWnd?
MFC中PreTranslateMessage是GetMessage(...)函數的下一級操作,即GetMessage(...)從消息隊列中獲取消息后,交由PreTranslateMessage()處理,若其返回FALSE則再交給TranslateMessage和DispatchMessage處理(進入WindowProc);??
如果用SendMessage,???則消息直接交到WindowProc處理,所以GetMessage不會取得SendMessage的消息,當然PreTranslateMessage也就不會被調用。?? [Page]
如果用PostMessage,則消息進入消息隊列,由GetMessage取得,PreTranslateMessage就有機會進行處理。
windows?消息處理機制是這樣的?: ??
??????首先系統(也就是windows)把來自硬件(鼠標,鍵盤等消息)和來自應用程序的消息 放到一個系統消息隊列中去?.?而應用程序需要有自己的消息隊列,也就是線程消息隊列,每一個線程有自己的消息隊列,對于多線程的應用程序就有和線程數目相等的線程消息隊列?. ??
? windows消息隊列把得到的消息發送到線程消息隊列,?線程消息隊列每次取出一條消息發送到指定窗口,不斷循環直到程序退出.這個循環就是靠消息環(while(GetMessage()) TranslateMessage();DispatchMessage();實現的.GetMessage()只是從線程消息中取出一條消息,TranslateMessage()把virtue?key消息轉化成character消息,如VK_F1會轉化成WM_HELP,而DispatchMessage??則把取出的消息發送到目的窗口.如果收到WM_CLOSE消息則結束循環,發送postqiutmessage(0),處理WM_DESTROY銷毀窗口!
?while (GetMessage(&msg, NULL, 0, 0))????????? //C++ code
?{??
??????? TranslateMessage(&msg);
?????? ?DispatchMessage(&msg);
?}
/*************************二*******************/
函數原型:virtual?BOOL?PreTranslateMessage(?MSG?*pMsg)?
1、在MFC中,PreTranslateMessage是虛函數,是用來截獲消息的。我們可以通過重載它來處理鍵盤和鼠標消息。在sdk中,這有所不同,我們必須在回調函
數 LRESULT???CALLBACK???WndProc(HWND???hWnd,???UINT???message,???WPARAM???wParam,???LPARAM???lParam)中處理消息。它和PreTranslateMessage起的作用是類似的,只是MFC封裝的更好而已。
2、重載該函數可以實現窗口消息在派發給窗口函數TranslateMessage()和DispatchMessae()之前的過濾,缺省的實現是完成加速鍵的翻譯。?
3、?該函數表示在消息處理(TranslateMessge()和DispatchMessage()等)前所作的操作,如果函數返回值為TRUE,那么消息?處理即終止,不會調用TranslateMessge()和DispatchMessage()來翻譯和分發消息給相應的窗口;若返回值為FALSE,才?會調用翻譯和分發消息函數。
4、在win32程序中,關于消息有兩種傳遞方式:
a.?MFC消息,MFC會把所有的消息一條條放到一個AFX_MSGMAP_ENTRY結構中,形成一個數組,該數組存放了所有的消息和與它們相關的參數。也可以說消息是放到消息隊列里去了。?b.?采用SendMessage()或其他類似的方式向窗口直接發送的而不經過消息隊列的消息。?這兩種方式中只有第一種(穿過消息隊列的消息)才受PreTranslateMessage()影響,第二種消息并不會理睬PreTranslateMessage()的存在。
?
一、是否調用TranslateMessage()和DispatchMessage()是由PreTranslateMessage()函數的返回值決定的,如果該函數返回TRUE,則不會把該消息分發給窗口函數處理。
二、傳給PreTranslateMessage()的消息是未經翻譯過的消息,它沒有經過
TranslateMessage()處理。例如:可以在該函數中使用(pMsg->wParam?==?VK_RETURN)來攔截回車鍵,其中wParam中存放的是鍵盤上字符的虛擬碼。
三、在WindowProc里不能處理WM_Char消息。(WindowProc函數見MFC消息響應機制一文)
四、SetWindowText會發送WM_Char給窗口。
、PeekMessage和GetMessage的區別:
????GetMessage在沒有消息的時候等待消息,cpu當然低
????PeekMessage沒有消息的時候立刻返回,所以cpu占用率高。因為游戲不能靠windows消息驅動,所以要用PeekMessage();?
另一篇文章:
????在一個WIN32程序中,WINDOWS會將消息傳遞給相應的窗口。但是消息不是立即就被傳遞給相應的窗口,而是會從整個程序最頂層的窗口傳遞到下一級?窗口,再傳遞到下一級窗口,直到傳遞給目標窗口。在整個過程中,有些消息,在某些特定的情況下,無法默認傳遞到目標窗口的。比如用戶在EDIT控件中按下?回車鍵,CANCEL鍵等,如果EDIT窗口之前有對話框窗口,對話框會默認處理回車消息(即響應ONOK函數,然后關閉對話框),然后退出消息傳遞。所以EDIT會收不到。要解決這個問題,可以在EDIT窗口之前所有的對話框中重載PreTranslateMessage函數,然后在函數內加上:
if?(pMsg->message==WM_KEYDOWN?&&?pMsg->wParam==VK_RETURN)?//如果消息類型為
?????????? WM_KEYDOWN??????????????????????????????????????????????????????????????????????? //并且用戶按下的是回車
???return?FALSE;????//不翻譯消息,直接將消息傳遞下去。具體可查MSDN。?注意,這里返回值不能為TRUE,TRUE的意思是翻譯消息后退出消息傳遞,如此一來雖然也能避開對話框默認處理,但是會退出消息傳遞,這樣EDIT控件照樣得不到消息。?如此就可避開對話框默認處理,將消息傳遞下去。注意:只有對話框才會默認處理按下回車,CANCEL消息,其他控件窗口則不會,所以在其他窗口中不必重載PreTranslateMessage函數,當然如果重載了也不會錯。?附:關于PreTranslateMessage()函數的小程序示例:
BOOL?CSearchuserDlg::PreTranslateMessage(MSG*?pMsg)?
{
?????????? if(pMsg->message==WM_KEYDOWN)??//判斷是否有按鍵按下?????
?????????? {
????????????????? switch(pMsg->wParam)????????
?????????? {
???????????????????? case?VK_DOWN:?????//表示是方向鍵中的向下的鍵???????????????//add?handle?code?here???????????
??????? ? ? ? ????????? break;
??????????????????? case?VK_UP:??????//表示是方向鍵中的向上的鍵????????????????//add?handle?code?here???????????
??????????????????????? break;???????????
???????????????? default:???????????
????????????????????? break;????????
?????????? }????
?????? }?
}
**********************************************************************
PeekMessage?一般是用來重載消息循環,避免程序停止響應。???????????舉例:????????
??MSG???msg;???
??if(::PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{??????????
??????? if(msg.message==WM_QUIT)??????????
????????? {???
????????????::PostQuitMessage(-1);????????????
???????? }???
????????if(!AfxGetApp()->PreTranslateMessage(&msg))????????????
?????? {???
????????????::TranslateMessage(&msg);????????????????
??????????? ::DispatchMessage(&msg);????????????
??????? }??????
}
******************************************************************************
界面上有很多個控件,如edit,如何知道當前的焦點是在哪個控件上面?
1.?GetFocus()可以得到當前控件的句柄,?你再枚舉子窗口句柄判斷,?跟得到的句柄一致的即為哪個被點中。
2.?CWnd*?pCtrl?=?CWnd::GetFocus();??
int?iCtrlID?=?pCtrl->GetDlgCtrlID();??
if(iCtrlID?==?IDC_EDIT1)??{??}?
else?if(iCtrlID?==?IDC_EDIT2)??{??}??...?
3.?GetFocus()?功能:確定當前焦點位于哪個控件上。函數執行成功時返回當前得到焦點控件的窗口句柄,發生錯誤時返回無效引用。
用法:?應用程序利用IsValid()函數可以檢測GetFocus()是否返回有效的控件引用。同時,使用TypeOf()函數可以確定控件的類型。
?
總結
以上是生活随笔為你收集整理的Windows消息机制-PreTranslateMessage的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决:Java HotSpot(TM)
- 下一篇: input 框 去掉下面的提示文字、提示