C#消息泵探索(二)
引言:
上篇文章里简单的解释了C#的消息泵原理,这里我们以winform为例详细地了解一下实现代码。
底层实现
[DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
public static extern bool TranslateMessage([In, Out] ref NativeMethods.MSG msg);
在System.Windows.Forms.UnsafeNativeMethods里,通过DLLImport的方法,引入了TranslateMessage方法。该方法的作用是将虚拟密钥消息转换为字符消息。 字符消息将发布到调用线程的消息队列,下次线程调用 GetMessage 或 PeekMessage 函数时要读取。
那么TranslateMessage是在什么情况下被调用的呢?
在System.Windows.Forms.Application中,有一个UnsafeNativeMethods.IMsoComponentManager.LocalModalMessageLoop,代码如下:
private bool LocalModalMessageLoop(Form form) {
try {
// Execute the message loop until the active component tells us to stop.
//
NativeMethods.MSG msg = new NativeMethods.MSG();
bool unicodeWindow = false;
bool continueLoop = true; while (continueLoop) { bool peeked = UnsafeNativeMethods.PeekMessage(ref msg, NativeMethods.NullHandleRef, 0, 0, NativeMethods.PM_NOREMOVE); if (peeked) { // If the component wants us to process the message, do it.
// The component manager hosts windows from many places. We must be sensitive
// to ansi / Unicode windows here.
//
if (msg.hwnd != IntPtr.Zero && SafeNativeMethods.IsWindowUnicode(new HandleRef(null, msg.hwnd))) {
unicodeWindow = true;
if (!UnsafeNativeMethods.GetMessageW(ref msg, NativeMethods.NullHandleRef, 0, 0)) {
continue;
} }
else {
unicodeWindow = false;
if (!UnsafeNativeMethods.GetMessageA(ref msg, NativeMethods.NullHandleRef, 0, 0)) {
continue;
}
} if (!PreTranslateMessage(ref msg)) {
UnsafeNativeMethods.TranslateMessage(ref msg);
if (unicodeWindow) {
UnsafeNativeMethods.DispatchMessageW(ref msg);
}
else {
UnsafeNativeMethods.DispatchMessageA(ref msg);
}
} if (form != null) {
continueLoop = !form.CheckCloseDialog(false);
}
}
else if (form == null) {
break;
}
else if (!UnsafeNativeMethods.PeekMessage(ref msg, NativeMethods.NullHandleRef, 0, 0, NativeMethods.PM_NOREMOVE)) {
UnsafeNativeMethods.WaitMessage();
}
}
return continueLoop;
}
catch {
return false;
}
}
我们可以看到,该方法先检索了一下当前form下是否存在消息,如果存在消息,则取出并TranslateMessage方法发布到调用线程的消息队列。
接着我们看一下LocalModalMessageLoop会在什么情况下被使用。
还是在System.Windows.Forms.Application中,有一个RunMessageLoopInner方法。代码过多,我就不贴了,免得有水博文之嫌。简单来说,主要业务是处理from的初始化工作,比如绑定线程事件等等,当然也有我们这篇博客的主角——消息泵。它的上层方法就很简单了,直接看代码吧。
internal void RunMessageLoop(int reason, ApplicationContext context) {
// Ensure that we attempt to apply theming before doing anything
// that might create a window. IntPtr userCookie = IntPtr.Zero;
if (useVisualStyles) {
userCookie = UnsafeNativeMethods.ThemingScope.Activate();
} try {
RunMessageLoopInner(reason, context);
}
finally {
UnsafeNativeMethods.ThemingScope.Deactivate(userCookie);
}
}
点击并拖拽以移动
最后就会看到我们非常眼熟的一个方法:
public static void Run(Form mainForm) {
ThreadContext.FromCurrent().RunMessageLoop(NativeMethods.MSOCM.msoloopMain, new ApplicationContext(mainForm));
}
至此,就完成了winform消息泵启动的全流程梳理,时隔一年,这个坑终于填了。
相关资料:
https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-peekmessagea
https://baike.baidu.com/item/%E6%B6%88%E6%81%AF%E5%BE%AA%E7%8E%AF/4970437?fr=aladdin
C#消息泵探索(二)的更多相关文章
- 深入探讨MFC消息循环和消息泵
首先,应该清楚MFC的消息循环(::GetMessage,::PeekMessage),消息泵(CWinThread::PumpMessage)和MFC的消息在窗口之间的路由是两件不同的事情.在MFC ...
- Android消息推送(二)--基于MQTT协议实现的推送功能
国内的Android设备,不能稳定的使用Google GCM(Google Cloud Messageing)消息推送服务. 1. 国内的Android设备,基本上从操作系统底层开始就去掉了Googl ...
- WCF扩展之实现ZeroMQ绑定和protocolBuffer消息编码(二)实现IRequestChannel(2016-03-15 12:35)
这是这个系列的第二篇,其他的文章请点击下列目录 WCF扩展之实现ZeroMQ绑定和protocolBuffer消息编码(一)概要设计 WCF扩展之实现ZeroMQ绑定和protocolBuffer消息 ...
- WPF的消息机制(二)- WPF内部的5个窗口之隐藏消息窗口
目录 WPF的消息机制(一)-让应用程序动起来 WPF的消息机制(二)-WPF内部的5个窗口 (1)隐藏消息窗口 (2)处理激活和关闭的消息的窗口和系统资源通知窗口 (3)用于用户交互的可见窗口 (4 ...
- libevent2源码分析之四:libevent2的消息泵
Dispatch类似于一个消息泵,在一个死循环中,不停地检查IO的状态(可以想像成不断从消息队列中读取消息),将状态的改变变成事件,再进行事件的响应. 主要代码如下: [event.c] int ev ...
- 初试kafka消息队列中间件二(采用java代码收发消息)
初试kafka消息队列中间件二(采用java代码收发消息) 上一篇 初试kafka消息队列中间件一 今天的案例主要是将采用命令行收发信息改成使用java代码实现,根据上一篇的接着写: 先启动Zooke ...
- 使用ABP SignalR重构消息服务(二)
使用ABP SignalR重构消息服务(二) 上篇使用ABP SignalR重构消息服务(一)主要讲的是SignalR的基础知识和前端如何使用SignalR,这段时间也是落实方案设计.这篇我主要讲解S ...
- Android消息机制探索(Handler,Looper,Message,MessageQueue)
概览 Android消息机制是Android操作系统中比较重要的一块.具体使用方法在这里不再阐述,可以参考Android的官方开发文档. 消息机制的主要用途有两方面: 1.线程之间的通信.比如在子线程 ...
- BA--空调系统一次泵和二次泵区别
通常来说,空调系统是按照满负荷设计的,但实际运行中,满负荷运行的 时间不足 3% ,空调设备绝大部分时间内在远低于额定负荷的情况下运转.在 部分负荷下,虽然冷水机组可以根据实际负荷调节相应的冷量输出, ...
- Linux环境进程间通信----系统 V 消息队列(二)
一.消息队列是一条由消息连接而成的链表,它保存在内核中,通过消息队列的引用标示符来访问. 二.消息队列不同于管道,通信的两个进程可以是完全无关的进程,它们之间不需要约定同步的方法.只要消息队列存在并且 ...
随机推荐
- 音频处理库:pydub与ffmpeg
一句话简介:pydub--音频处理库:ffmpeg--音视频编解码工具. 一. 安装 安装pydub pip install pydub pip install ffprobe 安装ffmpeg m ...
- JZOJ 6800.NOIP2020.9.19模拟spongebob
题目大意 求形如 \[\sum_{i=1}^n |a_ix + b_i| \] 的最小值 思路 我们显然可以先把系数 \(a\) 提出来 于是就成了 \(\sum_{i=1}^n |a_i|·|x + ...
- 如何快速把导出的csv表格数据导入到SqlServer中
(不要建自增字段,否则会出现第一个字段数据进不去的情况) 1.打开csv表格 2.打开要导入的表,右键编辑前两百行 3.表格中CTRL+C 数据库中CTRL+V 搞定! ** ...
- vue-cli 更新遇到的问题,卸载不掉旧版本2.9.6(可行)
今天更新vue-cli时,遇到问题:卸载不掉旧版本2.9 官网给的信息是:Vue CLI 的包名称由 vue-cli 改成了 @vue/cli. 如果你已经全局安装了旧版本的 vue-cli (1.x ...
- CSS 定位position
.link span { position: absolute; top: 0; left: 50%; transform: translateX(-50%); }
- MapboxGL基础
1. 概述 Mapbox GL 是一套开源库,用于在 Web.移动和桌面应用程序中嵌入可自定义且响应迅速的客户端地图.Mapbox GL 贴图以高帧速率呈现.缩写"GL"来自行业标 ...
- Django中获取用户IP方法
Django中通过request.META可以来获取用户的IP. request.META 是一个Python字典,包含了所有本次HTTP请求的Header信息,比如用户IP地址和用户Agent(通常 ...
- ModuleNotFoundError: No module named 'flask._compat'
出现 "ModuleNotFoundError: No module named 'flask._compat'" 提示的解决办法: 参考 https://zhuanlan.zhi ...
- MySql创建高性能的索引
创建高性能的索引1.树 减少数据的查询次数二叉树 平衡树 b树 节点存储key和datab+树 节点只存储key 叶子节点存储data innodb使用b+树 当页最大16kb可以存储1000个key ...
- vue样式穿透 滚动条隐藏 原生样式修改
样式穿透:deep,否则可能不能覆盖原有样式,vue2使用/v-deep/或者::v-deep,或者取消scoped,但不推荐,因为会影响到其他页面样式 <style lang="sc ...