複製鏈接
請複製以下鏈接發送給好友

PreTranslateMessage

鎖定
在MFC中,PreTranslateMessage是虛函數,是用來截獲消息的。我們可以通過重載它來處理鍵盤和鼠標消息。在sdk中,這有所不同,我們必須在回調函數LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)中處理消息。
它和PreTranslateMessage起的作用是類似的,只是MFC封裝的更好而已。
PreTranslateMessage是消息在送給TranslateMessage函數之前被調用的,絕大多數本窗口的消息都要通過這裏,比較常用,當你需要在MFC之前處理某些消息時,常常要在這裏添加代碼.。
外文名
PreTranslateMessage
類    別
函數
平    台
windows
cpu佔用率

目錄

PreTranslateMessage簡介

MFC消息控制流最具特色的地方是CWnd類的虛擬函數PreTranslateMessage(),通過重載這個函數,我們可以改變MFC的消息控制流程,甚至可以作一個全新的控制流出來。只有穿過消息隊列的消息才受PreTranslateMessage()影響,採用SendMessage()或其他類似的方式向窗口直接發送的而不經過消息隊列的消息根本不會理睬PreTranslateMessage()的存在。
該函數表示在消息處理(TranslateMessge()和DispatchMessage()等)前所作的操作,如果函數返回值為TRUE,那麼消息處理即終止,不會調用TranslateMessge()和DispatchMessage()來翻譯和分發消息給相應的窗口;若返回值為FALSE,才會調用翻譯和分發消息函數。
在win32程序中,關於消息有兩種傳遞方式:
a.MFC消息,MFC會把所有的消息一條條放到一個AFX_MSGMAP_ENTRY結構中,形成一個數組,該數組存放了所有的消息和與它們相關的參數。也可以説消息是放到消息隊列裏去了。
b.採用SendMessage()或其他類似的方式向窗口直接發送的而不經過消息隊列的消息。這兩種方式中只有第一種(穿過消息隊列的消息)才受PreTranslateMessage()影響,第二種消息並不會理睬PreTranslateMessage()的存在。

PreTranslateMessage特徵

一、是否調用TranslateMessage()和DispatchMessage()是由一個名稱為PreTranslateMessage()函數的返回值決定的,如果該函數返回TRUE,則不會把該消息分發給窗口函數處理。
二、傳給PreTranslateMessage()的消息是未經翻譯過的消息,它沒有經過TranslateMessage()處理。例如:可以在該函數中使用(pMsg->wParam==VK_RETURN)來攔截回車鍵,wParam中存放的是鍵盤上字符的虛擬碼。
三、在WindowProc裏不能處理WM_Char消息。
四、SetWindowText會發送WM_Char給窗口。
五、PeekMessage和GetMessage的區別:
GetMessage在沒有消息的時候等待消息,cpu佔用率當然低。
PeekMessage沒有消息的時候立刻返回,可以在沒有消息的時候可以做其他處理,但cpu佔用率一般較高。
大多遊戲都用PeekMessage();
PeekMessage和GetMessage的區別:
GetMessage在沒有消息的時候等待消息,cpu當然低
PeekMessage沒有消息的時候立刻返回.
因為遊戲不能靠windows消息驅動,所以要用PeekMessage();
附:關於PreTranslateMessage()函數的小程序示例:
BOOLCSearchuserDlg::PreTranslateMessage(MSG*pMsg)
{
if(pMsg->message==WM_KEYDOWN)//判斷是否有按鍵按下
{
switch(pMsg->wParam)
{
caseVK_DOWN://表示是方向鍵中的向下的鍵//addhandlecodehere
break;
caseVK_UP://表示是方向鍵中的向上的鍵//addhandlecodehere
break;
default:
break;
}
}
}
****************************************************************************************************************************************
PeekMessage一般是用來重載消息循環,避免程序停止響應。舉例:
MSGmsg;
if(::PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if(msg.message==WM_QUIT)
{
::PostQuitMessage(-1);
}
if(!AfxGetApp()->PreTranslateMessage(&msg))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
[1] 

PreTranslateMessage原理

PretranslateMessage的實現,不得不談到MFC消息循環的實現。MFC通過CWinApp類中的Pumpmessage函數實現消息循環,但是實際的消息循環代碼位於CWinThread中,CWinApp只是從CWinThread繼承過來。其簡化後的代碼大概如下:
BOOLCWinThread::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(雖然也會調用其他函數,但是這個最為關鍵),其代碼如下:
BOOLPASCALCWnd::WalkPreTranslateTree(HWNDhWndStop,MSG*pMsg)
{
ASSERT(hWndStop==NULL||::IsWindow(hWndStop));
ASSERT(pMsg!=NULL);
//walk from the target window up to the hWnd Stop window checking
//if any window wants to translate this message
for(HWNDhWnd=pMsg->hwnd;hWnd!=NULL;hWnd=::GetParent(hWnd))
{
CWnd*pWnd=CWnd::FromHandlePermanent(hWnd);
if(pWnd!=NULL)
{
//target window is a Cwindow
if(pWnd->PreTranslateMessage(pMsg))
return TRUE;//trappedbytargetwindow(eg:accelerators)
}
//gotto hWnd Stop window without interest
if(hWnd==hWndStop)
break;
}
return FALSE;//no specialprocessing
}
到這裏我們可以看到,代碼還是很直接的。從接受到消息的窗口層層往上遍歷,並調用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()把virtuekey消息轉化成character消息,如VK_F1會轉化成WM_HELP,而DispatchMessage則把取出的消息發送到目的窗口.如果收到WM_CLOSE消息則結束循環,發送postqiutmessage(0),處理WM_DESTROY銷燬窗口!
while (GetMessage(&msg, NULL, 0, 0)) //C++ code
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
參考資料