消息泵

消息泵,又叫消息循环。

消息循环使用一个图形化用户界面下Microsoft Windows。具有GUI的Windows 程序是由事件驱动的。Windows为每个创建窗口的线程维护一个单独的消息队列。通常只有第一个线程创建窗口。Windows 放置消息每当鼠标活动发生在该线程的窗口上时,每当该窗口具有焦点时键盘活动发生时,以及其他时候,都将进入该队列。进程还可以将消息添加到自己的队列中。为了接受用户输入以及出于其他原因,具有窗口的每个线程必须不断地从其队列中检索消息,并对其采取行动。通过编写一个循环调用 GetMessage(阻塞消息并检索它),然后调用 DispatchMessage(调度消息),并无限重复,从而使进程做到这一点。这就是消息循环。主程序中通常有一个消息循环,它运行在主线程上,并且在每个创建的模态对话框中都有附加的消息循环。留言给进程的每个窗口都通过其消息队列,并由其消息循环处理。

因此,综上所述,消息循环是一种事件循环。

​消息泵的实现

  1. private bool LocalModalMessageLoop(Form form)
  2. {
  3. try
  4. {
  5. NativeMethods.MSG msg = default(NativeMethods.MSG);
  6. bool flag = false;
  7. bool flag2 = true;
  8. while (flag2)
  9. {
  10. if (UnsafeNativeMethods.PeekMessage(ref msg, NativeMethods.NullHandleRef, 0, 0, 0))
  11. {
  12. if (msg.hwnd != IntPtr.Zero && SafeNativeMethods.IsWindowUnicode(new HandleRef(null, msg.hwnd)))
  13. {
  14. flag = true;
  15. if (!UnsafeNativeMethods.GetMessageW(ref msg, NativeMethods.NullHandleRef, 0, 0))
  16. {
  17. continue;
  18. }
  19. }
  20. else
  21. {
  22. flag = false;
  23. if (!UnsafeNativeMethods.GetMessageA(ref msg, NativeMethods.NullHandleRef, 0, 0))
  24. {
  25. continue;
  26. }
  27. }
  28. if (!PreTranslateMessage(ref msg))
  29. {
  30. UnsafeNativeMethods.TranslateMessage(ref msg);
  31. if (flag)
  32. {
  33. UnsafeNativeMethods.DispatchMessageW(ref msg);
  34. }
  35. else
  36. {
  37. UnsafeNativeMethods.DispatchMessageA(ref msg);
  38. }
  39. }
  40. if (form != null)
  41. {
  42. flag2 = !form.CheckCloseDialog(closingOnly: false);
  43. }
  44. }
  45. else
  46. {
  47. if (form == null)
  48. {
  49. break;
  50. }
  51. if (!UnsafeNativeMethods.PeekMessage(ref msg, NativeMethods.NullHandleRef, 0, 0, 0))
  52. {
  53. UnsafeNativeMethods.WaitMessage();
  54. }
  55. }
  56. }
  57. return flag2;
  58. }
  59. catch
  60. {
  61. return false;
  62. }
  63. }

以上便是WinForm中关于消息泵实现的核心源码了,我们看到在进入方法后,会先执行UnsafeNativeMethods.PeekMessage方法进行消息的读取。

PeekMessage函数为一个消息检查线程消息队列,并将该消息(如果存在)放于指定的结构。

接下来我们看一下微软对PeekMessage的定义吧。

我们可以看到该方法会获取到当前线程(其实也就是主线程)From触发的任何类型消息,当没有获取到消息或者窗体的字符集为Unicode时,将通过GetMessageW函数获取当前线程消息队列的消息,反之则调用GetMessageA。

我们可以通过winuser.h中的定义可以看到GetMessageA、GetMessageW均由GetMessage派生,二者的区别在于当前窗体的字符集是否为UNICODE。

GetMessage函数则是用于从当前线程的消息队列里取得一个消息并将其放于指定的结构。可通过GetMessage取得与指定窗口联系的消息和由PostThreadMesssge寄送的线程消息。GetMessage接收一定范围的消息值。GetMessage不接收属于其他线程或应用程序的消息。

而PostThreadMesssge的作用则是将一个消息放入到当前线程的消息队列里,不等待线程处理消息就返回。

这篇文章主要带大家宏观观察一下消息泵的几个主要组成部分,下篇文章将进行详细剖析。

然后进行的就是消息的预处理过程,日后有时间详细分析一下,先把预处理的核心代码贴在下面。

  1. [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
  2. internal static PreProcessControlState PreProcessControlMessageInternal(Control target, ref Message msg) {
  3. if (target == null) {
  4. target = Control.FromChildHandleInternal(msg.HWnd);
  5. }
  6. if (target == null) {
  7. return PreProcessControlState.MessageNotNeeded;
  8. }
  9. // reset state that is used to make sure IsInputChar, IsInputKey and
  10. // ProcessUICues are not called multiple times.
  11. // ISSUE: Which control should these state bits be set on? probably the target.
  12. target.SetState2(STATE2_INPUTKEY, false);
  13. target.SetState2(STATE2_INPUTCHAR, false);
  14. target.SetState2(STATE2_UICUES, true);
  15. Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.PreProcessControlMessageInternal " + msg.ToString());
  16. try {
  17. Keys keyData = (Keys)(unchecked((int)(long)msg.WParam) | (int)ModifierKeys);
  18. // Allow control to preview key down message.
  19. if (msg.Msg == NativeMethods.WM_KEYDOWN || msg.Msg == NativeMethods.WM_SYSKEYDOWN) {
  20. target.ProcessUICues(ref msg);
  21. PreviewKeyDownEventArgs args = new PreviewKeyDownEventArgs(keyData);
  22. target.OnPreviewKeyDown(args);
  23. if (args.IsInputKey) {
  24. Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "PreviewKeyDown indicated this is an input key.");
  25. // Control wants this message - indicate it should be dispatched.
  26. return PreProcessControlState.MessageNeeded;
  27. }
  28. }
  29. PreProcessControlState state = PreProcessControlState.MessageNotNeeded;
  30. if (!target.PreProcessMessage(ref msg)) {
  31. if (msg.Msg == NativeMethods.WM_KEYDOWN || msg.Msg == NativeMethods.WM_SYSKEYDOWN) {
  32. // check if IsInputKey has already procssed this message
  33. // or if it is safe to call - we only want it to be called once.
  34. if (target.GetState2(STATE2_INPUTKEY) || target.IsInputKey(keyData)) {
  35. Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control didn't preprocess this message but it needs to be dispatched");
  36. state = PreProcessControlState.MessageNeeded;
  37. }
  38. }
  39. else if (msg.Msg == NativeMethods.WM_CHAR || msg.Msg == NativeMethods.WM_SYSCHAR) {
  40. // check if IsInputChar has already procssed this message
  41. // or if it is safe to call - we only want it to be called once.
  42. if (target.GetState2(STATE2_INPUTCHAR) || target.IsInputChar((char)msg.WParam)) {
  43. Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control didn't preprocess this message but it needs to be dispatched");
  44. state = PreProcessControlState.MessageNeeded;
  45. }
  46. }
  47. }
  48. else {
  49. state = PreProcessControlState.MessageProcessed;
  50. }
  51. return state;
  52. }
  53. finally {
  54. target.SetState2(STATE2_UICUES, false);
  55. }
  56. }

结论

消息泵的本质就是一个主线程的消息队列,通过监听键盘的动作产生的消息,去除掉诸如F10、Menu、Tab等会影响焦点指示器和键盘提示的键,将剩余有用消息放进消息队列,用于读取。

C#消息泵探索(一)的更多相关文章

  1. 深入探讨MFC消息循环和消息泵

    首先,应该清楚MFC的消息循环(::GetMessage,::PeekMessage),消息泵(CWinThread::PumpMessage)和MFC的消息在窗口之间的路由是两件不同的事情.在MFC ...

  2. libevent2源码分析之四:libevent2的消息泵

    Dispatch类似于一个消息泵,在一个死循环中,不停地检查IO的状态(可以想像成不断从消息队列中读取消息),将状态的改变变成事件,再进行事件的响应. 主要代码如下: [event.c] int ev ...

  3. Android消息机制探索(Handler,Looper,Message,MessageQueue)

    概览 Android消息机制是Android操作系统中比较重要的一块.具体使用方法在这里不再阐述,可以参考Android的官方开发文档. 消息机制的主要用途有两方面: 1.线程之间的通信.比如在子线程 ...

  4. Android消息传递之Handler消息机制

    前言: 无论是现在所做的项目还是以前的项目中,都会遇见线程之间通信.组件之间通信,目前统一采用EventBus来做处理,在总结学习EventBus之前,觉得还是需要学习总结一下最初的实现方式,也算是不 ...

  5. Windows消息机制详解

    消息是指什么?      消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉.一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向 Windows发出一个通知,告诉应用 ...

  6. Windows消息机制概述

    消息是指什么?     消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉.一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向 Windows发出一个通知,告诉应用程 ...

  7. windows程序消息机制(Winform界面更新有关)

    windows程序消息机制(Winform界面更新有关) 转自:http://www.cnblogs.com/blosaa/archive/2013/05/31/3109586.html 1. Win ...

  8. Windows消息拦截技术的应用

    Windows消息拦截技术的应用 民航合肥空管中心 周毅 一.前 言 众所周知,Windows程式的运行是依靠发生的事件来驱动.换句话说,程式不断等待一个消息的发生,然后对这个消息的类型进行判断,再做 ...

  9. windows程序消息机制(Winform界面更新有关)--转

    1. Windows程序消息机制 Windows GUI程序是基于消息机制的,有个主线程维护着消息泵.这个消息泵让windows程序生生不息. Windows程序有个消息队列,窗体上的所有消息是这个队 ...

  10. 对发给Application.Handle消息的三次执行(拦截)消息的过程

    unit Main; interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms ...

随机推荐

  1. DIVFusion_ Darkness-free infrared and visible image fusion 论文解读

    研究 背景: ​ 当前图像融合方法都是针对正常照明的红外与可见光图像设计的,无法有效处理夜景下的情况. ​ 而针对夜景下的融合可以分为以下两个步骤,1 可见光图像增强,2 可见光图像与红外图像融合.但 ...

  2. HashTable HashMap concurrentHashMap区别

    HashTable HashMap concurrentHashMap区别 HashMap.HashTable.ConcurrentHashMap都是map接口的实现类 1.(同步性)HashTabl ...

  3. JS结束时间与当前时间间隔

    实现内容: 1.时间戳 1587024986952 转成年月日时分秒 2020-04-16 16:16:46 2.当前时间new Date()转成年月日时分秒2019-04-17 10:27:27 3 ...

  4. 把 URL 中文和一堆百分号转换成字符串

    https://www.cnblogs.com/Enziandom/tag/Web%20%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91 JS 有解析这样的 URL 的函数,主 ...

  5. LeetCode-2044 统计按位或能得到最大值子集的数目

    来源:力扣(LeetCode)链接:https://leetcode-cn.com/problems/count-number-of-maximum-bitwise-or-subsets 题目描述 给 ...

  6. 线程join detach 僵尸线程

    进程死亡后,由父进程负责回收PCB资源,不回收则会出现僵尸进程 对于线程来说,任何一个线程都可以回收另一个线程的资源 在子线程终止后,通常在主线程中通过pthread_join来回收子线程的资源,获取 ...

  7. .NET core api返回烦人的null

    默认的时候  把这个为null的去掉  只需要加入这一行代码 即可搞定 builder.Services.AddMvc().AddJsonOptions(o => { o.JsonSeriali ...

  8. ABAP学习(35):常用Function

    ABAP Function ABAP Coding过程中比较好用的Function整理. 1.获取Domain信息 代码实例: "****************************** ...

  9. JS 时间的获取和比较

    JS获取时间 获取当前时间 var date = new Date(); 可指定某种格式来获取时间,或者将字符串转换成时间 var date = new Date("2019-09-24 T ...

  10. (jmeter笔记)有序递增和无序递增

    有序递增:计数器 Track counter independently for each user: 不勾选,每个线程引用,顺延递增 勾选 ,每个线程引用,重新计算 Reset counter on ...