日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

SendMessage、PostMessage原理和源代码详解

發布時間:2023/12/18 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SendMessage、PostMessage原理和源代码详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文講解SendMessage、PostMessage兩個函數的實現原理,分為三個步驟進行講解,分別適合初級、中級、高級程序員進行理解,三個步驟分別為:

1、SendMessage、PostMessage的運行機制。

2、SendMessage、PostMessage的運行內幕。

3、SendMessage、PostMessage的內部實現。

注:理解這篇文章之前,必須先了解Windows的消息循環機制。

?

1、SendMessage、PostMessage的運行機制

我們先來看最簡單的。

SendMessage可以理解為,SendMessage函數發送消息,等待消息處理完成后,SendMessage才返回。稍微深入一點,是等待窗口處理函數返回后,SendMessage就返回了。

PostMessage可以理解為,PostMessage函數發送消息,不等待消息處理完成,立刻返回。稍微深入一點,PostMessage只管發送消息,消息有沒有被送到則并不關心,只要發送了消息,便立刻返回。

對于寫一般Windows程序的程序員來說,能夠這樣理解也就足夠了。但SendMessage、PostMessage真的是一個發送消息等待、一個發送消息不等待嗎?具體細節,下面第2點將會講到。

?

2、SendMessage、PostMessage的運行內幕

在寫一般Windows程序時,如上第1點講到的足以應付,其實我們可以看看MSDN來確定SendMessage、PostMessage的運行內幕。

在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函數將指定的消息發到窗口。它調用特定窗口的窗口處理函數,并且不會立即返回,直到窗口處理函數處理了這個消息。

再看看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函數將一個消息放入與創建這個窗口的消息隊列相關的線程中,并立刻返回不等待線程處理消息。

仔細看完MSDN解釋,我們了解到,SendMessage的確是發送消息,然后等待處理完成返回,但發送消息的方法為直接調用消息處理函數(即WndProc函數),按照函數調用規則,肯定會等消息處理函數返回之后,SendMessage才返回。而PostMessage卻沒有發送消息,PostMessage是將消息放入消息隊列中,然后立刻返回,至于消息何時被處理,PostMessage完全不知道,此時只有消息循環知道被PostMessage的消息何時被處理了。

至此我們撥開了一層疑云,原來SendMessage只是調用我們的消息處理函數,PostMessage只是將消息放到消息隊列中。下一節將會更深入這兩個函數,看看Microsoft究竟是如何實現這兩個函數的。

?

3、SendMessage、PostMessage的內部實現

Windows內部運行原理、機制往往是我們感興趣的東西,而這些東西又沒有被文檔化,所以我們只能使用Microsoft提供的工具自己研究了。

首先,在基本Win32工程代碼中,我們可以直接看到消息處理函數、消息循環,所以建立一個基本Win32工程(本篇文章使用VS2005),為了看到更多信息,我們需要進行設置,讓VS2005載入Microsoft的Symbol(pdb)文件[1]。為了方便,去除了一些多余的代碼,加入了兩個菜單,ID分別為:IDM_SENDMESSAGE、IDM_POSTMESSAGE。如下列出經過簡化后的必要的代碼。

消息循環:

Ln000:while (GetMessage(&msg, NULL, 0, 0))

Ln001:{

Ln002:??? TranslateMessage(&msg);

Ln003:??? DispatchMessage(&msg);

Ln004:}

?

消息處理函數:

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:}



下面一步步分析這兩個函數的內部情況,先討論 SendMessage。

第一步,在DispatchMessage(Ln003)函數處下個斷點,F5進行調試,當程序運行到斷點后,查看 CallStack 窗口,可得如下結果:

#003:MyProj.exe!wWinMain(HINSTANCE__ * hInstance=0x00400000, HINSTANCE__ * hPrevInstance=0x00000000, wchar_t * lpCmdLine=0x000208e0, int nCmdShow=0x00000001)? Line 49?? C++

#002:MyProj.exe!__tmainCRTStartup()? Line 589 + 0x35 bytes C

#001:MyProj.exe!wWinMainCRTStartup()? Line 414 C

#000:kernel32.dll!_BaseProcessStart@4()? + 0x23 bytes?

我們可以看到,進程先調用 kernel32.dll 中的 BaseProcessStart 函數,然后調用的 Startup Code 的函數 wWinMainCRTStartup,然后調用 _tmainCRTStartup 函數,最終調用我們的 wWinMain 函數,我們的程序就運行起來了。

?

第二步,去除第一步下的斷點,在 WndProc(Ln101) 函數入口處下個斷點,F5 繼續運行,運行到新下的斷點處,查看 CallStack 窗口,可得如下結果:

#008:MyProj.exe!WndProc(HWND__ * hWnd=0x00050860, unsigned int message=0x00000101, unsigned int wParam=0x00000074, long lParam=0xc03f0001)? Line 122??? C++

#007:user32.dll!_InternalCallWinProc@20()? + 0x28 bytes???

#006:user32.dll!_UserCallWinProcCheckWow@32()? + 0xb7 bytes???

#005:user32.dll!_DispatchMessageWorker@8()? + 0xdc bytes??

#004:user32.dll!_DispatchMessageW@4()? + 0xf bytes

#003:MyProj.exe!wWinMain(HINSTANCE__ * hInstance=0x00400000, HINSTANCE__ * hPrevInstance=0x00000000, wchar_t * lpCmdLine=0x000208e0, #000:int nCmdShow=0x00000001)? Line 49 + 0xc bytes C++

#002:MyProj.exe!__tmainCRTStartup()? Line 589 + 0x35 bytes C

#001:MyProj.exe!wWinMainCRTStartup()? Line 414 C

#000:kernel32.dll!_BaseProcessStart@4()? + 0x23 bytes?

??? #000~#003 跟第一步相同,不再解釋。在 #004、#005,可以看到,函數運行到 DispatchMessage 的內部了,DispatchMessageW、DispatchMessageWorker 是 user32.dll 中到處的函數,而且函數前部字符串相等,在此猜想應該是 DispatchMessage 的內部處理。#008 為我們消息處理函數,所以推想而知,#006、#007 是為了調用我們的消息處理函數而準備的代碼。

?

第三步,去除第二步下的斷點,在Ln003、Ln114、Ln115、Ln125 處分別下一個斷點,在菜單中選擇對應項,使程序運行至 Ln114,F10下一步,可以看到并沒有運行到 break(Ln115),直接跳到了 Ln125 處,由此可知目前 SendMessage 已經在等待了,查看 CallStack 窗口,可得如下結果:

#013:MyProj.exe!WndProc(HWND__ * hWnd=0x00050860, unsigned int message=0x00000500, unsigned int wParam=0x00000000, long lParam=0x00000000)? Line 147??? C++

#012:user32.dll!_InternalCallWinProc@20()? + 0x28 bytes???

#011:user32.dll!_UserCallWinProcCheckWow@32()? + 0xb7 bytes???

#010:user32.dll!_SendMessageWorker@20()? + 0xc8 bytes?

#009:user32.dll!_SendMessageW@16()? + 0x49 bytes??

#008:MyProj.exe!WndProc(HWND__ * hWnd=0x00050860, unsigned int message=0x00000111, unsigned int wParam=0x00008003, long lParam=0x00000000)? Line 136 + 0x15 bytes?? C++

#007:user32.dll!_InternalCallWinProc@20()? + 0x28 bytes???

#006:user32.dll!_UserCallWinProcCheckWow@32()? + 0xb7 bytes???

#005:user32.dll!_DispatchMessageWorker@8()? + 0xdc bytes??

#004:user32.dll!_DispatchMessageW@4()? + 0xf bytes

#003:MyProj.exe!wWinMain(HINSTANCE__ * hInstance=0x00400000, HINSTANCE__ * hPrevInstance=0x00000000, wchar_t * lpCmdLine=0x000208e0, #000:int nCmdShow=0x00000001)? Line 49 + 0xc bytes C++

#002:MyProj.exe!__tmainCRTStartup()? Line 589 + 0x35 bytes C

#001:MyProj.exe!wWinMainCRTStartup()? Line 414 C

#000:kernel32.dll!_BaseProcessStart@4()? + 0x23 bytes?

#000~#008 跟上面的相同,不再解釋。在 #009、#010,可以看到,函數調用到 SendMessage 內部了,在此猜想應該是 SendMessage 的內部處理。#011、#012 跟第二步中的 #006、#007 一樣,在第二部中,這兩個函數是為了調用消息處理函數而準備的代碼,#013 也是我們的消息處理函數,所以此兩行代碼的功能相等。

至此,我們證明了 SendMessage 的確是直接調用消息處理函數的,在消息處理函數返回前,SendMessage 等待。在所有的操作中,Ln003 斷點沒有去到,證明 SendMessage 不會將消息放入消息隊列中(在 PostMessage 分析中,此斷點將會跑到,接下來講述)。

?

第四步,F5繼續運行,此時彈出對話框,點擊對話框中的確定后,運行到斷點 Ln115 處。查看 CallStack 窗口,可得如下結果:

#008:MyProj.exe!WndProc(HWND__ * hWnd=0x00050860, unsigned int message=0x00000111, unsigned int wParam=0x00008003, long lParam=0x00000000)? Line 137??? C++

#007:user32.dll!_InternalCallWinProc@20()? + 0x28 bytes???

#006:user32.dll!_UserCallWinProcCheckWow@32()? + 0xb7 bytes???

#005:user32.dll!_DispatchMessageWorker@8()? + 0xdc bytes??

#004:user32.dll!_DispatchMessageW@4()? + 0xf bytes

#003:MyProj.exe!wWinMain(HINSTANCE__ * hInstance=0x00400000, HINSTANCE__ * hPrevInstance=0x00000000, wchar_t * lpCmdLine=0x000208e0, int nCmdShow=0x00000001)? Line 49 + 0xc bytes?? C++

#002:MyProj.exe!__tmainCRTStartup()? Line 589 + 0x35 bytes C

#001:MyProj.exe!wWinMainCRTStartup()? Line 414 C

#000:kernel32.dll!_BaseProcessStart@4()? + 0x23 bytes?

#000~008 跟第二步的完全相同,此時 SendMessage 也已經返回,所調用的堆棧也清空了。

至此,我們徹底撥開了 SendMessage 的疑云,了解了 SendMessage 函數的運行機制,綜述為,SendMessage 內部調用 SendMessageW、SendMessageWorker 函數做內部處理,然后調用 UserCallWinProcCheckWow、InternalCallWinProc 來調用我們代碼中的消息處理函數,消息處理函數完成之后,SendMessage 函數便返回了。

?

SendMessage 討論完之后,現在討論 PostMessage,將上面的所有斷點刪除,關閉調試。

第一步,在DispatchMessage(Ln003)函數處下個斷點,F5進行調試,此處結果跟 SendMessage 一樣,不再說明。

第二步,去除第一步下的斷點,在 WndProc(Ln101) 函數入口處下個斷點,F5 繼續運行,此處結果跟 SendMessage 一樣,不再說明。

第三步,去除第二步下的斷點,在 Ln003、Ln117、Ln118、Ln129 處分別下一個斷點,在菜單中選擇對應項,使程序運行至 Ln117,F10 下一步,可以看到已經運行到 break,PostMessage 函數返回了,此時 CallStack 沒有變化。

第四步,F5 繼續運行,此時程序運行到 Ln003,CallStack 跟第一步剛起來時一樣。

第五步,F5 繼續運行(由于有多個消息,可能要按多次),讓程序運行到 Ln129,此時 CallStack 跟第二步相同,為了方便說明,再次列舉如下:

#008:MyProj.exe!WndProc(HWND__ * hWnd=0x00070874, unsigned int message=0x00000501, unsigned int wParam=0x00000000, long lParam=0x00000000)? Line 151??? C++

#007:user32.dll!_InternalCallWinProc@20()? + 0x28 bytes???

#006:user32.dll!_UserCallWinProcCheckWow@32()? + 0xb7 bytes???

#005:user32.dll!_DispatchMessageWorker@8()? + 0xdc bytes??

#004:user32.dll!_DispatchMessageW@4()? + 0xf bytes

#003:MyProj.exe!wWinMain(HINSTANCE__ * hInstance=0x00400000, HINSTANCE__ * hPrevInstance=0x00000000, wchar_t * lpCmdLine=0x000208e0, int nCmdShow=0x00000001)? Line 49 + 0xc bytes?? C++

#002:MyProj.exe!__tmainCRTStartup()? Line 589 + 0x35 bytes C

#001:MyProj.exe!wWinMainCRTStartup()? Line 414 C

#000:kernel32.dll!_BaseProcessStart@4()? + 0x23 bytes?

由此可以看到,此調用是從消息循環中調用而來,DispatchMessageW、DispatchMessageWorker 是 DispatchMessage 的內部處理,UserCallWinProcCheckWow、InternalCallWinProc是為了調用我們的消息處理函數而準備的代碼。

至此,我們再次徹底撥開了 PostMessage 的疑云,了解了 PostMessage 函數的運行機制,綜述為,PostMessage 將消息放入消息隊列中,自己立刻返回,消息循環中的 GetMessage(PeekMessage 也可,本例中為演示)處理到我們發的消息之后,便按照普通消息處理方法進行處理。


總結

以上是生活随笔為你收集整理的SendMessage、PostMessage原理和源代码详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。