本文講解SendMessage、PostMessage兩個函數(shù)的實現(xiàn)原理,分為三個步驟進行講解,分別適合初級、中級、高級程序員進行理解,三個步驟分別為:
1、SendMessage、PostMessage的運行機制。
2、SendMessage、PostMessage的運行內(nèi)幕。
3、SendMessage、PostMessage的內(nèi)部實現(xiàn)。
注:理解這篇文章之前,必須先了解Windows的消息循環(huán)機制。
1、SendMessage、PostMessage的運行機制
我們先來看最簡單的。
SendMessage可以理解為,SendMessage函數(shù)發(fā)送消息,等待消息處理完成后,SendMessage才返回。稍微深入一點,是等待窗口處理函數(shù)返回后,SendMessage就返回了。
PostMessage可以理解為,PostMessage函數(shù)發(fā)送消息,不等待消息處理完成,立刻返回。稍微深入一點,PostMessage只管發(fā)送消息,消息有沒有被送到則并不關(guān)心,只要發(fā)送了消息,便立刻返回。
對于寫一般Windows程序的程序員來說,能夠這樣理解也就足夠了。但SendMessage、PostMessage真的是一個發(fā)送消息等待、一個發(fā)送消息不等待嗎?具體細節(jié),下面第2點將會講到。
2、SendMessage、PostMessage的運行內(nèi)幕
在寫一般Windows程序時,如上第1點講到的足以應付,其實我們可以看看MSDN來確定SendMessage、PostMessage的運行內(nèi)幕。
在MSDN中,SendMessage解釋如為:The SendMessage function sends the specified message to a window or windows. It calls the window procedure for the specified window and does not return until the window procedure has processed the message.
翻譯成中文為:SendMessage函數(shù)將指定的消息發(fā)到窗口。它調(diào)用特定窗口的窗口處理函數(shù),并且不會立即返回,直到窗口處理函數(shù)處理了這個消息。
再看看PostMessage的解釋:The PostMessage function places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message.
翻譯成中文為:PostMessage函數(shù)將一個消息放入與創(chuàng)建這個窗口的消息隊列相關(guān)的線程中,并立刻返回不等待線程處理消息。
仔細看完MSDN解釋,我們了解到,SendMessage的確是發(fā)送消息,然后等待處理完成返回,但發(fā)送消息的方法為直接調(diào)用消息處理函數(shù)(即WndProc函數(shù)),按照函數(shù)調(diào)用規(guī)則,肯定會等消息處理函數(shù)返回之后,SendMessage才返回。而PostMessage卻沒有發(fā)送消息,PostMessage是將消息放入消息隊列中,然后立刻返回,至于消息何時被處理,PostMessage完全不知道,此時只有消息循環(huán)知道被PostMessage的消息何時被處理了。
至此我們撥開了一層疑云,原來SendMessage只是調(diào)用我們的消息處理函數(shù),PostMessage只是將消息放到消息隊列中。下一節(jié)將會更深入這兩個函數(shù),看看Microsoft究竟是如何實現(xiàn)這兩個函數(shù)的。
3、SendMessage、PostMessage的內(nèi)部實現(xiàn)
Windows內(nèi)部運行原理、機制往往是我們感興趣的東西,而這些東西又沒有被文檔化,所以我們只能使用Microsoft提供的工具自己研究了。
首先,在基本W(wǎng)in32工程代碼中,我們可以直接看到消息處理函數(shù)、消息循環(huán),所以建立一個基本W(wǎng)in32工程(本篇文章使用VS2005),為了看到更多信息,我們需要進行設置,讓VS2005載入Microsoft的Symbol(pdb)文件[1]。為了方便,去除了一些多余的代碼,加入了兩個菜單,ID分別為:IDM_SENDMESSAGE、IDM_POSTMESSAGE。如下列出經(jīng)過簡化后的必要的代碼。
消息循環(huán):
Ln000:while (GetMessage(&msg, NULL, 0, 0))
Ln001:{
Ln002: TranslateMessage(&msg);
Ln003: DispatchMessage(&msg);
Ln004:}
消息處理函數(shù):
Ln100:LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
Ln101:{
Ln102: int wmId, wmEvent;
Ln103: switch (message)
Ln104: {
Ln105: case WM_COMMAND:
Ln106: wmId = LOWORD(wParam);
Ln107: wmEvent = HIWORD(wParam);
Ln108: switch (wmId)
Ln109: {
Ln110: case IDM_EXIT:
Ln111: DestroyWindow(hWnd);
Ln112: break;
Ln113: case IDM_SENDMESSAGE:
Ln114: SendMessage(hWnd, WM_SENDMESSAGE, 0, 0);
Ln115: break;
Ln116: case IDM_POSTMESSAGE:
Ln117: PostMessage(hWnd, WM_POSTMESSAGE, 0, 0);
Ln118: break;
Ln119: default:
Ln120: return DefWindowProc(hWnd, message, wParam, lParam);
Ln121: }
Ln122: break;
Ln123:
Ln124: case WM_SENDMESSAGE:
Ln125: MessageBox(hWnd, L"SendMessage", L"Prompt", MB_OK);
Ln126: break;
Ln127:
Ln128: case WM_POSTMESSAGE:
Ln129: MessageBox(hWnd, L"PostMessage", L"Prompt", MB_OK);
Ln130: break;
Ln131:
Ln132: case WM_DESTROY:
Ln133: PostQuitMessage(0);
Ln134:
Ln135: default:
Ln136: return DefWindowProc(hWnd, message, wParam, lParam);
Ln137: }
Ln138: return 0;
Ln139:}