C#消息泵探索(一)
消息泵
消息泵,又叫消息循环。
消息循环使用一个图形化用户界面下Microsoft Windows。具有GUI的Windows 程序是由事件驱动的。Windows为每个创建窗口的线程维护一个单独的消息队列。通常只有第一个线程创建窗口。Windows 放置消息每当鼠标活动发生在该线程的窗口上时,每当该窗口具有焦点时键盘活动发生时,以及其他时候,都将进入该队列。进程还可以将消息添加到自己的队列中。为了接受用户输入以及出于其他原因,具有窗口的每个线程必须不断地从其队列中检索消息,并对其采取行动。通过编写一个循环调用 GetMessage(阻塞消息并检索它),然后调用 DispatchMessage(调度消息),并无限重复,从而使进程做到这一点。这就是消息循环。主程序中通常有一个消息循环,它运行在主线程上,并且在每个创建的模态对话框中都有附加的消息循环。留言给进程的每个窗口都通过其消息队列,并由其消息循环处理。
因此,综上所述,消息循环是一种事件循环。
消息泵的实现
private bool LocalModalMessageLoop(Form form)
{
try
{
NativeMethods.MSG msg = default(NativeMethods.MSG);
bool flag = false;
bool flag2 = true;
while (flag2)
{
if (UnsafeNativeMethods.PeekMessage(ref msg, NativeMethods.NullHandleRef, 0, 0, 0))
{
if (msg.hwnd != IntPtr.Zero && SafeNativeMethods.IsWindowUnicode(new HandleRef(null, msg.hwnd)))
{
flag = true;
if (!UnsafeNativeMethods.GetMessageW(ref msg, NativeMethods.NullHandleRef, 0, 0))
{
continue;
}
}
else
{
flag = false;
if (!UnsafeNativeMethods.GetMessageA(ref msg, NativeMethods.NullHandleRef, 0, 0))
{
continue;
}
}
if (!PreTranslateMessage(ref msg))
{
UnsafeNativeMethods.TranslateMessage(ref msg);
if (flag)
{
UnsafeNativeMethods.DispatchMessageW(ref msg);
}
else
{
UnsafeNativeMethods.DispatchMessageA(ref msg);
}
}
if (form != null)
{
flag2 = !form.CheckCloseDialog(closingOnly: false);
}
}
else
{
if (form == null)
{
break;
}
if (!UnsafeNativeMethods.PeekMessage(ref msg, NativeMethods.NullHandleRef, 0, 0, 0))
{
UnsafeNativeMethods.WaitMessage();
}
}
}
return flag2;
}
catch
{
return false;
}
}
以上便是WinForm中关于消息泵实现的核心源码了,我们看到在进入方法后,会先执行UnsafeNativeMethods.PeekMessage方法进行消息的读取。
PeekMessage函数为一个消息检查线程消息队列,并将该消息(如果存在)放于指定的结构。
接下来我们看一下微软对PeekMessage的定义吧。


我们可以看到该方法会获取到当前线程(其实也就是主线程)From触发的任何类型消息,当没有获取到消息或者窗体的字符集为Unicode时,将通过GetMessageW函数获取当前线程消息队列的消息,反之则调用GetMessageA。
我们可以通过winuser.h中的定义可以看到GetMessageA、GetMessageW均由GetMessage派生,二者的区别在于当前窗体的字符集是否为UNICODE。
GetMessage函数则是用于从当前线程的消息队列里取得一个消息并将其放于指定的结构。可通过GetMessage取得与指定窗口联系的消息和由PostThreadMesssge寄送的线程消息。GetMessage接收一定范围的消息值。GetMessage不接收属于其他线程或应用程序的消息。
而PostThreadMesssge的作用则是将一个消息放入到当前线程的消息队列里,不等待线程处理消息就返回。
这篇文章主要带大家宏观观察一下消息泵的几个主要组成部分,下篇文章将进行详细剖析。
然后进行的就是消息的预处理过程,日后有时间详细分析一下,先把预处理的核心代码贴在下面。
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
internal static PreProcessControlState PreProcessControlMessageInternal(Control target, ref Message msg) {
if (target == null) {
target = Control.FromChildHandleInternal(msg.HWnd);
}
if (target == null) {
return PreProcessControlState.MessageNotNeeded;
}
// reset state that is used to make sure IsInputChar, IsInputKey and
// ProcessUICues are not called multiple times.
// ISSUE: Which control should these state bits be set on? probably the target.
target.SetState2(STATE2_INPUTKEY, false);
target.SetState2(STATE2_INPUTCHAR, false);
target.SetState2(STATE2_UICUES, true);
Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control.PreProcessControlMessageInternal " + msg.ToString());
try {
Keys keyData = (Keys)(unchecked((int)(long)msg.WParam) | (int)ModifierKeys);
// Allow control to preview key down message.
if (msg.Msg == NativeMethods.WM_KEYDOWN || msg.Msg == NativeMethods.WM_SYSKEYDOWN) {
target.ProcessUICues(ref msg);
PreviewKeyDownEventArgs args = new PreviewKeyDownEventArgs(keyData);
target.OnPreviewKeyDown(args);
if (args.IsInputKey) {
Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "PreviewKeyDown indicated this is an input key.");
// Control wants this message - indicate it should be dispatched.
return PreProcessControlState.MessageNeeded;
}
}
PreProcessControlState state = PreProcessControlState.MessageNotNeeded;
if (!target.PreProcessMessage(ref msg)) {
if (msg.Msg == NativeMethods.WM_KEYDOWN || msg.Msg == NativeMethods.WM_SYSKEYDOWN) {
// check if IsInputKey has already procssed this message
// or if it is safe to call - we only want it to be called once.
if (target.GetState2(STATE2_INPUTKEY) || target.IsInputKey(keyData)) {
Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control didn't preprocess this message but it needs to be dispatched");
state = PreProcessControlState.MessageNeeded;
}
}
else if (msg.Msg == NativeMethods.WM_CHAR || msg.Msg == NativeMethods.WM_SYSCHAR) {
// check if IsInputChar has already procssed this message
// or if it is safe to call - we only want it to be called once.
if (target.GetState2(STATE2_INPUTCHAR) || target.IsInputChar((char)msg.WParam)) {
Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "Control didn't preprocess this message but it needs to be dispatched");
state = PreProcessControlState.MessageNeeded;
}
}
}
else {
state = PreProcessControlState.MessageProcessed;
}
return state;
}
finally {
target.SetState2(STATE2_UICUES, false);
}
}
结论
消息泵的本质就是一个主线程的消息队列,通过监听键盘的动作产生的消息,去除掉诸如F10、Menu、Tab等会影响焦点指示器和键盘提示的键,将剩余有用消息放进消息队列,用于读取。
C#消息泵探索(一)的更多相关文章
- 深入探讨MFC消息循环和消息泵
首先,应该清楚MFC的消息循环(::GetMessage,::PeekMessage),消息泵(CWinThread::PumpMessage)和MFC的消息在窗口之间的路由是两件不同的事情.在MFC ...
- libevent2源码分析之四:libevent2的消息泵
Dispatch类似于一个消息泵,在一个死循环中,不停地检查IO的状态(可以想像成不断从消息队列中读取消息),将状态的改变变成事件,再进行事件的响应. 主要代码如下: [event.c] int ev ...
- Android消息机制探索(Handler,Looper,Message,MessageQueue)
概览 Android消息机制是Android操作系统中比较重要的一块.具体使用方法在这里不再阐述,可以参考Android的官方开发文档. 消息机制的主要用途有两方面: 1.线程之间的通信.比如在子线程 ...
- Android消息传递之Handler消息机制
前言: 无论是现在所做的项目还是以前的项目中,都会遇见线程之间通信.组件之间通信,目前统一采用EventBus来做处理,在总结学习EventBus之前,觉得还是需要学习总结一下最初的实现方式,也算是不 ...
- Windows消息机制详解
消息是指什么? 消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉.一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向 Windows发出一个通知,告诉应用 ...
- Windows消息机制概述
消息是指什么? 消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉.一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向 Windows发出一个通知,告诉应用程 ...
- windows程序消息机制(Winform界面更新有关)
windows程序消息机制(Winform界面更新有关) 转自:http://www.cnblogs.com/blosaa/archive/2013/05/31/3109586.html 1. Win ...
- Windows消息拦截技术的应用
Windows消息拦截技术的应用 民航合肥空管中心 周毅 一.前 言 众所周知,Windows程式的运行是依靠发生的事件来驱动.换句话说,程式不断等待一个消息的发生,然后对这个消息的类型进行判断,再做 ...
- windows程序消息机制(Winform界面更新有关)--转
1. Windows程序消息机制 Windows GUI程序是基于消息机制的,有个主线程维护着消息泵.这个消息泵让windows程序生生不息. Windows程序有个消息队列,窗体上的所有消息是这个队 ...
- 对发给Application.Handle消息的三次执行(拦截)消息的过程
unit Main; interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms ...
随机推荐
- Iceberg 数据治理及查询加速实践
数据治理 Flink 实时写入 Iceberg 带来的问题 在实时数据源源不断经过 Flink 写入的 Iceberg 的过程中,Flink 通过定时的 Checkpoint 提交 snapshot ...
- uWSGI 结合 nginx 配置动静分离
uWSGI 结合 nginx 配置动静分离 目录 uWSGI 结合 nginx 配置动静分离 1 环境准备 2 初始配置文件 2.1 uwsgi 配置文件 2.2 Nginx配置文件 2.3 Djan ...
- JMeter BeanShell 学习
JMeter BeanShell 学习 Beanshell分为: JMeter有如下BeanShell组件: 定时器: BeanShell Timer 前置处理器:BeanShell PreProce ...
- 将现有vue项目基于electron打包成桌面应用程序 如何隐藏electron窗体的菜单栏
一.前言 项目本来打算采用B/S架构去做的,浏览器网址方式打开还是让用户不方便: 二.使用electron集成桌面应用 本身项目是使用vue-cli开发的,在使用electron之前,需要将本身的项目 ...
- Qt中的多窗体编程(续一)
在前面一节中,已经把所有需要的窗体都创建好了,下面将依次实现预设的功能. 一.实现点击菜单打开模式子窗体的功能. 1.在编辑模式下双击Forms下的"mainWindow.ui", ...
- [WPF]MVVM的数据绑定
啥是MVVM? 我理解的MVVM是Model(数据),View(界面),ViewModel(数据与界面之间的桥梁)的缩写,是一种编程模式.前期需要多花一些时间去编辑绑定,在后期维护方便.只需要关注数据 ...
- No.2.6
vw/vh:(能够使用vw单位设置网页元素的尺寸) 相对单位 相对视口的尺寸计算结果 vw:viewport width(1vw=1/100视口宽度) vh:viewport height(1vh=1 ...
- 第五周作业-N67044-张铭扬
1. 搭建chrony服务完成多个主机的时间同步. [root@centos8 ~]# yum -y install chrony [root@centos8 ~]# vim /etc/chrony ...
- mysql8使用tmpfs内存磁盘当内存数据库的配置方法
序: 内存关系数据库没有找到开源好用的,很多都是商用.虽然mysql有memory引擎,但写是整体锁表,没法用. 一直想将mysql放入内存中,搜索n次资料,没找到合适的,可能之前思路不对. 最近在测 ...
- Neo4j删除节点和关系、彻底删除节点标签名(转载备忘)
https://www.jianshu.com/p/59bd829de0de 总结提前: [1]先删关系,再删节点 [2]当记不得关系名时,type(r)可以查到关系名 [3]彻底删除节点标签名,需要 ...