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 消息队列(二)
一.消息队列是一条由消息连接而成的链表,它保存在内核中,通过消息队列的引用标示符来访问. 二.消息队列不同于管道,通信的两个进程可以是完全无关的进程,它们之间不需要约定同步的方法.只要消息队列存在并且 ...
随机推荐
- appium 在linux安装和使用(持续更新)
appium V1.10 centos7.4 安装 安装步骤 1. 安装node 为了得到npm(node package manager,nodejs的安装包管理工具,可以通过npm来下载appiu ...
- 一文详解 jitpack 多渠道maven库发布
先说一下,为什么会有这篇文章? 最初接触 JitPack 时,发现网络上大量涉及JitPack的教程不可用.通过两天的研究才搞明白: 1.不同的gradle版本,gradle api使用方式 与 Ji ...
- Cesium JulianDate(十八)
代表天文朱利安日期,它是自4712年1月1日(公元前4713年)正午以来的天数.为了提高精度,该类存储的日期部分和秒数部分是分开的.并且为了算术安全和表示闰秒,该日期始终存储在国际原子时间标准中 (T ...
- dotnet总结
初衷: 搞了几年.Net ,一直没在博客园做过系统的总结,并打算按照下面的结构来写 语言层面 类型系统 [查看]: https://www.cnblogs.com/francisXu/p/136027 ...
- .net webapi+jwt demo
一.新建.net webapi程序 二.nuget包搜索jwt,点击安装 三.在model文件夹下建立三个主要类: public class AuthInfo { /// &l ...
- 基于Python的OpenGL 06 之摄像机
1. 引言 本文基于Python语言,描述OpenGL的摄像机 前置知识可参考: 基于Python的OpenGL 05 之坐标系统 - 当时明月在曾照彩云归 - 博客园 (cnblogs.com) 笔 ...
- 化学式latex语法
$2 CO_2 \stackrel{光}{\underset{\text{催化剂}}{\longrightarrow}}2 Fe + 3 CO_2$ $\ce{CO2}$ $\ce{CU2+}$ $\ ...
- 理论+实战,详解Sharding Sphere-jdbc
摘要:Apache ShardingSphere 是一款分布式的数据库生态系统,它包含两大产品:ShardingSphere-Proxy和ShardingSphere-JDBC. 本文分享自华为云社区 ...
- dotnet core 托管
1.托管形式 dotnet core的托管形式分为 进程内托管(InProcess).进程外托管 (OutOfProcess) 新建的Web程序csproj带有 <AspNetCoreHosti ...
- 如何将PDF文件中的部分信息隐藏或遮盖呢?
由于工作需要,总是需要对PDF文件中的内容进行部分隐藏.之前,作为VIP,可以使用某软件对PDF中的信息进行部分遮盖,现在,VIP到期了,我也不想继续花钱了(哭穷,嘻嘻) 在信息时代,只要会百 ...