引言:

上篇文章里简单的解释了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#消息泵探索(二)的更多相关文章

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

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

  2. Android消息推送(二)--基于MQTT协议实现的推送功能

    国内的Android设备,不能稳定的使用Google GCM(Google Cloud Messageing)消息推送服务. 1. 国内的Android设备,基本上从操作系统底层开始就去掉了Googl ...

  3. WCF扩展之实现ZeroMQ绑定和protocolBuffer消息编码(二)实现IRequestChannel(2016-03-15 12:35)

    这是这个系列的第二篇,其他的文章请点击下列目录 WCF扩展之实现ZeroMQ绑定和protocolBuffer消息编码(一)概要设计 WCF扩展之实现ZeroMQ绑定和protocolBuffer消息 ...

  4. WPF的消息机制(二)- WPF内部的5个窗口之隐藏消息窗口

    目录 WPF的消息机制(一)-让应用程序动起来 WPF的消息机制(二)-WPF内部的5个窗口 (1)隐藏消息窗口 (2)处理激活和关闭的消息的窗口和系统资源通知窗口 (3)用于用户交互的可见窗口 (4 ...

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

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

  6. 初试kafka消息队列中间件二(采用java代码收发消息)

    初试kafka消息队列中间件二(采用java代码收发消息) 上一篇 初试kafka消息队列中间件一 今天的案例主要是将采用命令行收发信息改成使用java代码实现,根据上一篇的接着写: 先启动Zooke ...

  7. 使用ABP SignalR重构消息服务(二)

    使用ABP SignalR重构消息服务(二) 上篇使用ABP SignalR重构消息服务(一)主要讲的是SignalR的基础知识和前端如何使用SignalR,这段时间也是落实方案设计.这篇我主要讲解S ...

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

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

  9. BA--空调系统一次泵和二次泵区别

    通常来说,空调系统是按照满负荷设计的,但实际运行中,满负荷运行的 时间不足 3% ,空调设备绝大部分时间内在远低于额定负荷的情况下运转.在 部分负荷下,虽然冷水机组可以根据实际负荷调节相应的冷量输出, ...

  10. Linux环境进程间通信----系统 V 消息队列(二)

    一.消息队列是一条由消息连接而成的链表,它保存在内核中,通过消息队列的引用标示符来访问. 二.消息队列不同于管道,通信的两个进程可以是完全无关的进程,它们之间不需要约定同步的方法.只要消息队列存在并且 ...

随机推荐

  1. 一文详解 jitpack 多渠道maven库发布

    先说一下,为什么会有这篇文章? 最初接触 JitPack 时,发现网络上大量涉及JitPack的教程不可用.通过两天的研究才搞明白: 1.不同的gradle版本,gradle api使用方式 与 Ji ...

  2. LG P2633 Count on a tree

    \(\text{Solution}\) 树上主席树板子 \(\text{Code}\) #include <cstdio> #include <algorithm> #defi ...

  3. ABAP 辨析CO|CN|CA|NA|CS|NS|CP|NP

    1.文档说明 本篇文档将通过举例,解析字符的比较运算符之间的用法和区别,涉及到的操作符:CO|CN|CA|NA|CS|NS|CP|NP 2.用法和区别 用法总览 以下举例,几乎都使用一个字符变量和一个 ...

  4. 用C++ 弹奏武侠音乐:沧海一声笑

    前言: 参照网路代码,用编程语言来弹奏一首音乐,搞点轻松的 原文:点击此处 完整代码: 以下为Visual Stuido 2022测试可用,控制台程序.运行之后,会自动弹奏沧海一声笑,并且出现歌词.主 ...

  5. Postgresql invalid command \N数据恢复处理

    做一个单表恢复工作,数据在1000多W,说是报了错误导不进去,环境与流程见下:恢复步骤: 1.导出语句 pg_dump -h xxxxx -p 5432 -U postgres -b -Fp db_t ...

  6. JavaScript之this、let、const关键字

    各场景下的this this的意思:百度翻译为:这.这么.本 在JavaScript中,表示当前对象的引用关键字,没有特殊含义. 在一个方法中,this表示该方法所属的对象. 如果单独使用,this表 ...

  7. 红米手机刷 LineageOS (实操)

    参考:https://miuiver.com/how-to-flash-lineageos/ 实操机型:红米note8 1. 下载Android Platform-Tools 2. 下载对应机型的TW ...

  8. Oracle DataGuard 出现 GAP 修复

    下面我们通过实验来进行演示如何修复: 一.主库切几个最新的归档,然后手工删掉,重新开启DG同步. 1.备库关闭应用日志和数据库 SQL> ALTER DATABASE RECOVER MANAG ...

  9. TDSQL-C Serverless 服务是如何实现通过接入层来实现恢复感知

    在一些极限的测试场景下,数据库实例会频繁的自动启停,这时候如何保证数据库实例停止后快速恢复呢?如何保证在恢复数据库实例时无需用户重复链接,直到恢复访问? 站在用户的角度考虑,谁都不希望数据库每次启停都 ...

  10. Mysql习题系列(二):多表查询(一篇学会做Mysql多表查询题,超详细~)

    Mysql8.0习题系列 软件下载地址 提取码:7v7u 数据下载地址 提取码:e6p9 文章目录 Mysql8.0习题系列 1.多表查询1 1.1题目 1.2答案 1.显示所有员工的姓名,部门号和部 ...