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 也可,本例中為演示)處理到我們發的消息之后,便按照普通消息處理方法進行處理。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/452727.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/452727.shtml
英文地址,請注明出處:http://en.pswp.cn/news/452727.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python協程--實現斐波那契數列(Fibonacci)的幾種方式

1.使用for遍歷list數組 # 使用for遍歷list數組 nums list() a 0 b 1 i 0while i < 10:nums.append(a)a, b b, abi 1for num in nums:print(num)2.使用迭代器完成 class Fibonacci(object):def __init__(self, all_num):self.all_num all_numself.current_num 0sel…

敏捷開發宣言

敏捷開發宣言&#xff1a; 1. 個體和交互勝過過程和工具 2. 可工作的軟件勝過面面俱到的文檔 3. 客戶協作勝過合同談判 4. 響應變化勝過遵循計劃 從上面的宣言可以看出&#xff0c;敏捷開發的核心是人 、協作、時刻可運行的軟件、變化。

java fast math,Java FastMath.signum方法代碼示例

import org.apache.commons.math3.util.FastMath; //導入方法依賴的package包/類/*** {inheritDoc}*/Overrideprotected double doSolve()throws TooManyEvaluationsException,NoBracketingException {double min getMin();double max getMax();// [x1, x2] is the bracketin…

什么是可行性研究

1.并非任何問題都有簡單明顯的解決辦法&#xff0c;事實上&#xff0c;許多問題不可能在預定的系統規模或時間期限之內解決。2.如果問題沒有可行的解&#xff0c;那么花費在這項工程上的任何時間、人力、軟硬件資源和經費&#xff0c;都是無謂的浪費。3.可行性研究的目的&#…

FTP服務的簡介和配置詳解

FTP服務的簡介和配置詳解注意&#xff1a;配置FTP服務時&#xff0c;最好關閉防火墻和selinux1、FTP服務簡介FTP 是File Transfer Protocol&#xff08;文件傳輸協議&#xff09;的英文簡稱&#xff0c;而中文簡稱為“文件傳輸協議”。用于Internet上的控制文件的雙向傳輸。同時…

Python協程--生成器(通過異常來判斷生成器已經結束)

以實現斐波那契數列為例&#xff1a; def create_num(all_num):a, b 0, 1current_num 0while current_num < all_num:yield a # 如果一個函數中有yield語句&#xff0c;那么這個就不在是函數&#xff0c;而是一個生成器的模板a, b b, abcurrent_num 1return "ok.…

四種類型轉換 cast

1.static_cast 2.dynamic_cast 3.const_cast 4. reinterpret_cast 例子1&#xff1a; float x; cout<<static_cast<int>(x); ... f(static_cast<string>("hello")); 例子2&#xff1a; class Car; class Cabriolet:pbulic Car { …

java獲取不重復訂單號,Java 生成永不重復的訂單號

package com.taiping.test;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Random;public class Test12 {/*** 生成永不重復的訂單號* param startLetter 訂單號開頭字符串* param size 訂單號中隨機的大寫字母個數* return*/public static String…

CSS中position屬性(abusolute | relative | static | fixed)區別

關于css3中position屬性的定義&#xff1a; 轉載于:https://www.cnblogs.com/tangjiao/p/9025934.html

什么是系統流程圖

系統流程圖是概括地描繪物理系統的傳統工具。它的基本思想是用圖形符號以黑盒子的形式描繪組成系統的每個部件&#xff08;程序&#xff0c;文檔&#xff0c;數據庫&#xff0c;人工過程等&#xff09;。系統流程圖表達的是數據在系統各部件之間流動的情況&#xff0c;而不是對…

Python協程--生成器(實現多任務)

0.生成器 1.使用yield完成多任務 import timedef task_1():while True:print("---1----")time.sleep(0.1)yielddef task_2():while True:print("---2----")time.sleep(0.1)yielddef main():t1 task_1()t2 task_2()# 先讓t1運行一會&#xff0c;當t1中遇…

技術分享連載(六十一)

資源管理 Q1&#xff1a;Unity5.4.1中&#xff0c;我將需要的Shader打到一個AssetBundle包中&#xff08;包含一個關聯了所有Shader的Shader Variants&#xff09;&#xff0c;分別用Shader.WarmupAllShaders和ShaderVariantCollection.WarmUp兩種方式進行預加載&#xff0c;后…

RabbitMQ(三) 集群配置

RabbitMQ——集群配置 之前不管是搞Redis、SQL、Mongo還是其他的東西&#xff0c;一律都沒說過集群要怎么搞&#xff0c;電腦實在是帶不動、說透徹點就是懶&#xff0c;懶得搭也懶得寫&#xff0c;今日深刻意識到錯誤&#xff0c;做學問是不能懶的&#xff0c;會被人噴的&#…

mysql update實質,UPDATE注射(mysql+php)的兩個模式

UPDATE注射(mysqlphp)的兩個模式2021-01-23 7:48:35424UPDATE注射(mysqlphp)的兩個模式文/安全天使SuperHei2005.8.11一.測試環境&#xff1a;OS:Windowsxpsp2php:php4.3.10(mysql4.1.9apache1.3.33二.測試數據庫結構&#xff1a;-----start-----數據庫:test------------------…

數據庫系統的特點

數據結構化數據的共享性高&#xff0c;冗余度低且易擴充數據獨立性高數據由數據庫管理系統統一管理和控制

SNF軟件開發機器人-子系統-導出-導入功能-多人合作時這個功能經常用到

導出 導出可以將資源表和子系統導出并形成一個json文件。 1.效果展示&#xff1a; 2.使用說明&#xff1a; 點擊導出按鈕后會彈出一個導出頁面。頁面的左側可以選擇功能&#xff0c;右側可以選擇資源表&#xff0c;選擇功能的同時右側中功能所需的資源表也會被選擇。當功能之間…

基于物理的渲染-用真實的環境光照亮物體

目前&#xff0c;在游戲引擎中用于照亮物體的光源非常豐富。其中&#xff0c;比較常用的有&#xff1a;平行方向光、點光源、聚光燈以及體積光等&#xff0c;但它們都是對真實光源的近似&#xff0c;并不能很好地模擬真實世界中的復雜光照情況。為了增加光照效果的真實感&#…

php中取出數組中指定的值,PHP除開數組中指定的值

PHP去除數組中指定的值//一維數組簡單的做法 unset($arr[array_search($value,$arr)])$arr array("a","b","c","d");function isHave($var){if($var!"b")return true;}$arr_filter array_values(array_filter($arr,"…

實體以及實體型和實體集

實體&#xff08;Entity&#xff09; 客觀存在并可相互區別的事物稱為實體。 可以是具體的人、事、物或抽象的概念。 屬性&#xff08;Attribute&#xff09; 實體所具有的某一特性稱為屬性。 一個實體可以由若干個屬性來刻畫。 實體型&#xff08;Entity Type&#xf…