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 消息队列(二)
一.消息队列是一条由消息连接而成的链表,它保存在内核中,通过消息队列的引用标示符来访问. 二.消息队列不同于管道,通信的两个进程可以是完全无关的进程,它们之间不需要约定同步的方法.只要消息队列存在并且 ...
随机推荐
- 滴水 10/13号完成 打印出DOS PE头 节表 开源
#include<stdio.h> #include<Windows.h> int szie2; #pragma warning(disable : 4996) LPVOID ...
- 个人博客系统Typecho情侣主题模板Cupid
个人博客系统Typecho情侣主题模板Cupid 转载:https://zcjun.com/3175.html
- 从零开始使用阿里云OSS服务(白嫖)
阿里云对象存储服务OSS使用记录 1 新人免费开通OSS服务 访问 阿里云官网,登录账号(个人新用户账号),首页搜索 对象存储OSS,点击下方的直达. 点击立即开通,此时会在一个新网页中完成开通 完成 ...
- Python_使用Python将一个文件夹下的多个文件夹中的图片复制到一个文件夹中
代码: # coding=utf-8 import os import shutil #目标文件夹(最终要复制到的文件夹),此处为相对路径,也可以改为绝对路径 determination = '/.. ...
- PostGIS之空间索引
1. 概述 PostGIS 是PostgreSQL数据库一个空间数据库扩展,它添加了对地理对象的支持,允许在 SQL 中运行空间查询 PostGIS官网:About PostGIS | PostGIS ...
- vue中引入静态图片
vue+webpack中,可以使用require imageList: [ { url: require('../image/pig.png') } ] vue+vite没有require方法 // ...
- TiDB日常运维手册
1.处理dm不兼容DDL 迁移中断,执行跳过操作 tiup dm list tiup dm display xxx-dm-prod tiup dmctl --master-addr 10.0.xx.x ...
- Vue 权限控制 使用自定义指令 代替v-if
参考了vue-element-admin 指令库的permission 使用Demo(demo.vue) <template> <p>admin组:<span v-per ...
- golang 中使用mysql报错:“ scannable dest type slice with >1 columns (4) in result”
query := fmt.Sprintf("SELECT .... infos = make([]*struct, 0, 10) err = s.db.GetContext(ctx, &am ...
- 采集存储计算处理卡设计原理图:619-基于6U VPX的双FMC ZU19EG 采集存储计算处理卡
619-基于6U VPX的双FMC ZU19EG 采集存储计算处理卡 基于6U VPX的双FMC ZU19EG 采集存储计算处理卡 一.板卡概述 该板卡是采集.存储.计算.管理一体的高集成度.加固 ...