Windows訊息機制的流程:
A. 作業系統接收應用程式的視窗訊息,將訊息投遞到該應用程式的訊息佇列中
B. 應用程式在訊息迴圈中調用GetMessage函數從訊息佇列中取出一條一條的訊息,取出訊息後,應用程式可以對訊息進行一些預先處理。
C. 應用程式調用DispatchMessage,將訊息回傳給作業系統。
D. 系統利用WNDCLASS結構體的lpfnWndProc成員儲存的視窗過程函數的指標調用視窗過程,對訊息進行處理。
PreTranslateMessage作用和使用方法
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);
}