PreTranslateMessage

在MFC中,PreTranslateMessage是虛函式,是用來截獲訊息的。我們可以通過重載它來處理鍵盤和滑鼠訊息。在sdk中,這有所不同,我們必須在回調函式LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)中處理訊息。

它和PreTranslateMessage起的作用是類似的,只是MFC封裝的更好而已。

PreTranslateMessage是訊息在送給TranslateMessage函式之前被調用的,絕大多數本視窗的訊息都要通過這裡,比較常用,當你需要在MFC之前處理某些訊息時,常常要在這裡添加代碼.。

基本介紹

  • 外文名:PreTranslateMessage
  • 類別:函式
  • 平台:windows
  • cpu占用率:高
簡介,特徵,原理,

簡介

MFC訊息控制流最具特色的地方是CWnd類的虛擬函式PreTranslateMessage(),通過重載這個函式,我們可以改變MFC的訊息控制流程,甚至可以作一個全新的控制流出來。只有穿過訊息佇列的訊息才受PreTranslateMessage()影響,採用SendMessage()或其他類似的方式向視窗直接傳送的而不經過訊息佇列的訊息根本不會理睬PreTranslateMessage()的存在。
該函式表示在訊息處理(TranslateMessge()和DispatchMessage()等)前所作的操作,如果函式返回值為TRUE,那么訊息處理即終止,不會調用TranslateMessge()和DispatchMessage()來翻譯和分發訊息給相應的視窗;若返回值為FALSE,才會調用翻譯和分發訊息函式。
在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訊息。
四、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);
}
}

原理


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);
}

相關詞條

熱門詞條

聯絡我們