`
mmdev
  • 浏览: 13219126 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

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 消息处理详解

    总的来说,朱建强先生的"SendMessage PostMessage 消息处理详解"是一个深入理解Windows消息机制的好资源,无论是对Delphi开发者还是其他Windows编程人员,都能从中受益。通过学习和实践,你可以更好地掌握如何在你的...

    在C#中SendMessage和PostMessage的参数传递

    在C#中SendMessage和PostMessage的参数传递 在C#中可以使用Window API提供的SendMessage和PostMessage来传递参数。两者的区别简单介绍下:返回值的不同,我们先看一下 MSDN 里的声明: LRESULT SendMessage( HWND ...

    SendMessage与PostMessage区别

    在Windows编程中, SendMessage 和 PostMessage 是两个非常重要的API函数,它们都用于进程间或线程间的通信,向目标窗口发送消息。然而,两者的工作机制和应用场景有所不同。 **SendMessage** 函数是同步的,它会将...

    在C# API 消息处理 SendMessage PostMessage 进程通信 发消息 实例 文档资料

    主要示例在C#中的SendMessage,PostMessage,包含两个示例,一个资料文档,希望受用。 在C#中 API 消息处理 SendMessage PostMessage 进程通信 发消息 实例 文档资料

    PostMessage和SendMessage的区别

    通过详细解释PostMessage与SendMessage的工作原理,我们可以更好地理解这两种方法如何影响应用程序的性能和响应性。 ### PostMessage与SendMessage的基本概念 #### PostMessage函数 PostMessage函数用于将消息放入...

    c# 线程通信PostMessage SendMessage PeekMessage

    本主题将深入探讨`FindWindow`、`PostMessage`、`SendMessage`以及`PeekMessage`这四个关键函数,它们在Windows API中扮演着重要的角色,特别是在线程间通信和窗口消息处理方面。 1. **FindWindow**: `FindWindow...

    MFC程序通过SendMessage或PostMessage实现进程间通信,实现很简单

    本篇文章将深入探讨如何使用MFC中的`SendMessage`和`PostMessage`函数来实现简单的进程间通信。 首先,`SendMessage`和`PostMessage`都是Windows API中处理消息的核心函数,它们都用于在窗口之间发送消息。但两者的...

    MFC Sendmessage和Postmessage的使用

    2.两个对话框CMessageDlg和ConeDlg; 3.CMessageDlg为父窗口,ConeDlg为子窗口; 4.父窗口上有一个按钮用于弹出子窗口,子窗口也有一个按钮用来触发事件; 5.子窗口的按钮触发事件后,发送消息给父窗口,然后在...

    PostMessage SendMessage

    在Windows API中,`PostMessage`和`SendMessage`是两个重要的消息处理函数,它们用于线程间或线程内的通信,向目标窗口发送消息。这两个函数在编程中扮演着关键角色,尤其在处理用户界面(UI)更新和异步操作时。让...

    postMessage详解和demo

    **postMessage详解** `postMessage` 是Web API中一个非常重要的功能,用于在不同源(跨域)的浏览器窗口之间进行通信。这个API是HTML5引入的,解决了之前跨域安全策略限制下,如iframe、弹窗、多窗口之间无法直接...

    MFC通过SendMessage或PostMessage实现两个进程通信功能,实现很简单

    资源包含两个源程序,里面是借用MFC的消息通信实现简单的两个进程间通信,嗯主要是通过SendMessage或PostMessage,功能实现简单便捷。该资源是我想搞点分数,分多的大佬麻烦赏赐点,没分的可以找找我另一个同样的...

    Spy++ 原理初探VC++相关源代码

    在源代码中,你会看到关于Windows API的广泛使用,如`FindWindow`用于查找特定窗口,`GetWindowText`用于获取窗口标题,`EnumWindows`遍历所有窗口,以及`SendMessage`和`PostMessage`用于发送和处理消息。...

    PostMessage 后台模拟按键 MOUSE VC原码

    `PostMessage`函数就是...通过分析这两个文件,你可以更深入地理解如何在VC++中使用`PostMessage`进行后台模拟按键和鼠标操作。学习这段代码有助于提升你在Windows API和C++编程上的技能,特别是与人机交互相关的部分。

    Window 消息大全使用详解[WindowMessage.rar]-精品源代码

    2. **消息的发送与传递**:消息可以通过`PostMessage`或`SendMessage`函数发送。`PostMessage`是非阻塞的,消息会被放入队列等待处理;而`SendMessage`是阻塞的,直到消息被处理完毕才返回。 3. **窗口过程**:每个...

    PostMessage

    在实际开发中,开发者需要根据具体需求选择合适的消息传递方式,如PostMessage、SendMessage、QueueUserAPC等,以实现高效且稳定的程序运行。理解并熟练掌握PostMessage的使用,对于编写高质量的Windows应用程序至关...

    API函数SendMessage参数即Windows消息详解.doc

    SendMessage函数与PostMessage函数的区别在于,PostMessage将消息放入消息队列并立即返回,而SendMessage则会等待消息被处理完后再返回,因此SendMessage通常用于需要等待响应的情况,而PostMessage则适用于异步通信...

    通过PostMessage,SendMessage将CString发送和发布到Windows

    SendMessage将CString发送和发布到Windows”指的是在Microsoft的Windows操作系统环境中,使用C++编程语言,特别是MFC(Microsoft Foundation Classes)框架时,如何利用`PostMessage`和`SendMessage`这两个API函数来...

    windows运行原理源代码列表框控件

    本篇文章将深入探讨Windows运行原理,并以源代码为例,解析列表框控件的工作方式。 首先,我们需要理解Windows窗口程序的基本架构。Windows应用程序是基于事件驱动的,即用户操作(如点击、键盘输入等)会触发特定...

    VC中SendMessage和PostMessage的区别

    在VC++编程中,了解和正确使用`SendMessage`和`PostMessage`是非常关键的,它们都是Windows API中用于进程间通信的重要函数,用于向窗口发送消息。这两个函数虽然功能相似,但在实现方式和使用场景上有着显著的区别...

    进程间用postMessage发消息 ,在实际开发中的实例,

    通过`window.postMessage`方法,一个窗口可以向任何其他窗口发送消息,无论它们是否属于同一个源。接收端需要监听`message`事件来接收到的消息。这个API在Web应用程序中,特别是单页应用(SPA)和跨窗口通信时非常...

Global site tag (gtag.js) - Google Analytics