WPF 消息循环(二)

们已经知道,win32/MFC/WinForm/WPF 都依靠消息循环驱动,让程序跑起来。

这里就介绍 WPF 中是如何使用消息循环来驱动程序的。
在这里插入图片描述

1. 背景

只听说过 Dispatcher ,哪里来的消息循环?

WPF 启动运行堆栈:

>	WpfApp1.exe!WpfApp1.App.OnStartup(System.Windows.StartupEventArgs e)17	C#PresentationFramework.dll!System.Windows.Application..ctor.AnonymousMethod__1_0(object unused)	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler)	WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl()	mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state)	WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext executionContext, System.Threading.ContextCallback callback, object state)	WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke()	WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue()	WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)	WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)	WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o)	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler)	WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs)	WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam)	[本机到托管的转换]	[托管到本机的转换]	WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame)	PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore)	PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window)	WpfApp1.exe!WpfApp1.App.Main()	

可以发现 倒数第 4 行的PushFrameImpl 这个方法。
去看其源码,就发现了熟悉的消息循环 :

private void PushFrameImpl(DispatcherFrame frame)
{SynchronizationContext synchronizationContext = null;SynchronizationContext synchronizationContext2 = null;MSG msg = default(MSG);_frameDepth++;try{synchronizationContext = SynchronizationContext.Current;synchronizationContext2 = new DispatcherSynchronizationContext(this);SynchronizationContext.SetSynchronizationContext(synchronizationContext2);try{while (frame.Continue && GetMessage(ref msg, IntPtr.Zero, 0, 0)){TranslateAndDispatchMessage(ref msg);}if (_frameDepth == 1 && _hasShutdownStarted){ShutdownImpl();}}finally{SynchronizationContext.SetSynchronizationContext(synchronizationContext);}}finally{_frameDepth--;if (_frameDepth == 0){_exitAllFrames = false;}}
}

主要的 GetMessage 和 TranslateAndDispatchMessage 代码:

private bool GetMessage(ref MSG msg, IntPtr hwnd, int minMessage, int maxMessage)
{UnsafeNativeMethods.ITfMessagePump messagePump = GetMessagePump();try{if (messagePump == null){return UnsafeNativeMethods.GetMessageW(ref msg, new HandleRef(this, hwnd), minMessage, maxMessage);}messagePump.GetMessageW(ref msg, hwnd, minMessage, maxMessage, out var result);return result switch{-1 => throw new Win32Exception(), 0 => false, _ => true, };}finally{if (messagePump != null){Marshal.ReleaseComObject(messagePump);}}
}
private void TranslateAndDispatchMessage(ref MSG msg)
{bool flag = false;if (!ComponentDispatcher.RaiseThreadMessage(ref msg)){UnsafeNativeMethods.TranslateMessage(ref msg);UnsafeNativeMethods.DispatchMessage(ref msg);}
}

可以理解为:Dispatcher 对消息循环的操作进行了“封装” 。
那,Dispatcher 是谁?

2. Dispatchcer

大部分WPF对象,都是 DispatcherObject。这意味着,可以在 DispatcherObject 中(如 Window 中), 使用 this.Dispatchcer 获取到 Dispatchcer 。
在这里插入图片描述
一般我们会通过三种方式获取 Dispatchcer :

// App.Current.Dispatcher;(Application.Current.Dispatcher)
var dispatcher1 = App.Current.Dispatcher;// CurrentDispatcher;
var dispatcher2 = System.Windows.Threading.Dispatcher.CurrentDispatcher;// System.Windows.Threading.DispatcherObject.Dispatcher;
var dispatcher3 = this.Dispatcher;

可分为两类:

  • 当前线程的 Dispatcher:

    System.Windows.Threading.Dispatcher.CurrentDispatcher;

  • 创建对应对象的 Dispatcher:

    App.Current.Dispatcher;

    DispatcherObject.Dispatcher;

Dispatcher 和线程是什么关系?

  • Dispatcher 属于线程(与线程一一对应)。
  • 在单 UI 线程应用程序中,WPF的 UI 对象在获取 this.Dispatcher 属性时,不同对象取的都是同一个Dispatcher实例。(因为都是同一个 UI 线程创建的。)
  • 在默认的 WPF UI 线程中: App.Current.Dispatcher = DispatcherObject.Dispatcher

所有的线程(UI线程,普通线程)都有 Dispatcher 吗?
是的。

在所有线程中,调用 System.Windows.Threading.Dispatcher.CurrentDispatcher
都会得到一个属于这个线程的 Dispatcher 对象。(不用的时候不会创建)
所以:如果你想在一个后台线程中,使用 Dispatcher.CurrentDispatcher.Invoke
将操作封送到 UI 线程,是做不到的。因为这时候获取到的 Dispatcher 不是UI线程的 Dispatcher, 而是当前线程自己的 Dispatcher。

你最常用 Dispatcher 做什么?
跨线程访问UI
为什么不能直接跨线程访问UI?
在这里插入图片描述

3. Dispatcher 如何实现跨线程的调用

最常使用 Dispatcher 的创建就是,在后台线程更新 UI ,那 Dispatcher 是如何做到的呢。

当你调用

private void Foo()
{this.Width = 100;
}Application.Current.Dispatcher.Invoke(() =>
{Foo();
});

时,Dispatcher 究竟做了什么,把操作转移到 UI 线程上去了。
在这里插入图片描述

  1. 将调用的 Delegate 和优先级包装成一个DispatcherOperation放入Dispatcher维护的优先级队列当中,这个Queue是按 DispatcherPriority 排序的,总是高优先级的 DispatcherOperation 先被处理。
  2. 往当前线程的消息队列当中Post一个名为 MsgProcessQueue 的 Message。(这个消息是WPF自己定义的。)这个消息被Post到消息队列之前,还要设置 MSG.Handle,这个Handle就是Window 1#的Handle。指定Handle是为了在消息循环Dispatch消息的时候,指定哪个窗口的 WndProc 处理这个消息。
  3. 消息循环读取消息。
  4. 系统根据获取消息的Handle,发现跟Window1#的Handle相同,那么这个消息派发到Window1#的窗口过程,让其处理。
  5. 在窗口过程中,优先级队列当中取一个DispatcherOperation。
  6. 执行 DispatcherOperation.Invoke 方法,Invoke方法的核心就是调用 DispatcherOperation 构造时传入的Delegate,也就是 Dispatcher.BeginInvoke 传入的Delegate。最终这个Foo()方法就被执行了。

4 回顾

  • WPF 底层仍然靠信息循环来驱动。
  • Dispatcher 使用消息循环来实现跨进程的委托调用。
  • Dispatcher 属于线程,需要理解当前拿到的 Dispatcher 到底是哪个 Dispatcher 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/490550.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PostgreSQL 常用运维SQL整理

一、查询并杀会话 -- 查询会话 select pid,usename,client_addr,client_port,query_start,query,wait_event from pg_stat_activity; -- 杀会话 select pg_terminate_backend(pid号); -- 使用如下命令自动生成杀会话语句 select datid,datname,pid,usesysid,usename,applicat…

JS中的Promise用法大全

目录 一、相关概念介绍1.什么是Promise2.Promise状态3.创建Promise4.then()方法5.catch方法6.链式调用7.异步编程 二、使用1.构造Promise2.executor 函数3.then() 方法4.then() 方法返回的Promise的状态 三、Async/Await语法糖四、Promise应用场景五、总结 一、相关概念介绍 1…

【新界面】基于卷积神经网络的垃圾分类(Matlab)

基于CNN的垃圾识别与分类GUI【新界面】 有需要可直接联系我,基本都在在线,能秒回!可加我看演示视频,不懂可以远程教学 1.此项目设计包括两份完整的源代码,有GUI界面的代码和无GUI界面系统的代码。 (以下部…

pytorch bilstm crf的教程,注意 这里不支持批处理,要支持批处理 用torchcrf这个。

### Bi-LSTM Conditional Random Field ### pytorch tutorials https://pytorch.org/tutorials/beginner/nlp/advanced_tutorial.html ### 模型主要结构: ![title](sources/bilstm.png) pytorch bilstm crf的教程,注意 这里不支持批处理 Python version…

bugku-simple MQTT-wp解析

1.下载题目打开题目,是一个流量包,题目说是MQTT,然后打开流量之后的流量都是MQTT,我们来搜一下MQTT是什么流量 MQTT流量: 是一种基于发布订阅模式的轻量级的通讯协议,并且该协议构建于TCP/IP协议之上&…

HCIA-Access V2.5_2_2网络通信基础_TCP/IP协议栈报文封装

TCP/IP协议栈的封装过程 用户从应用层发出数据先会交给传输层,传输层会添加TCP或者UDP头部,然后交给网络层,网络层会添加IP头部,然后交给数据链路层,数据链路层会添加以太网头部和以太网尾部,最后变成01这样…

Windows 与 Linux 下 Ping IPv6 地址 | 常用网络命令

注:本文为网络命令相关文章合辑。 未整理去重。 一、IPv6 概述 IPv6 即 “Internet 协议版本 6”,因 IPv4 地址资源面临耗尽问题而被引入以替代 IPv4。IPv6 则提供了理论上多达 2 128 2^{128} 2128 个地址,有效解决地址不足困境。 IPv6 具…

关卡选择与布局器

unity布局管理器 使用unity布局管理器轻松对关卡选择进行布局。 实现过程 准备普通按钮button设置字体和对应的sprite设置父gameobject(levelbase) 再创建UI.image(selectbackground)布局背景和大小gameobject(grid…

python学opencv|读取图像(十三)BGR图像和HSV图像互相转换深入

【1】引言 前序学习过程中,我们偶然发现:如果原始图像是png格式,将其从BGR转向HSV,再从HSV转回BGR后,图像的效果要好于JPG格式。 文章链接为: python学opencv|读取图像(十二)BGR图…

程序算术题-2

程序算术题-2 输出所有组合逻辑实例代码 输出所有排列逻辑实例代码 输出所有组合 计算一组数字按n位数组合的所有组合。 逻辑 /*** param stringBuilder 用于组合的拼接* param list 组合数序列* param level 目前位数* param exceptedLevel 组合期待位数*/…

VQ-VAE和VAE 的区别是什么?

第一行所展示的就是普通的VAE,它的核心是通过encoder和decoder,将像素空间的图像压缩到一个提取了核心特征的隐变量向量。VQ-VAE的思想是,即使VAE中压缩的这个隐变量中的向量提取了图片中的核心特征信息,但是这些信息仍然可能存在冗余&#x…

使用Qt Creator设计可视化窗体(一)

一、创建项目 打开 Qt Creator ,在菜单栏中选中: “文件” --------> “新建文件或项目” ;或者使用快捷键:Ctrl n;或者直接点击:“new” Qt 中的构建工具有三种可供选择,分别是&#…

Python3 爬虫 Scrapy 与 Redis

Scrapy是一个分布式爬虫的框架,如果把它像普通的爬虫一样单机运行,它的优势将不会被体现出来。因此,要让Scrapy往分布式爬虫方向发展,就需要学习Scrapy与Redis的结合使用。Redis在Scrapy的爬虫中作为一个队列存在。 一、Scrapy_r…

Docker 学习

Docker 学习 Docker 概念 Docker 安装 一般是在服务器里 Docker阿里云镜像加速 配置主要是Linux命令 Docker命令大纲及帮助文档的使用 docker帮助文档 查看docker命令 docker --help查看某个命令,例如ps的详细文档 docker ps --help也可查阅 [docker官方帮助手…

超牛免费 机械臂模型、工业机器人模型下载网站集合

‌机械臂是一种高精度、多输入多输出的复杂系统,能够模仿人手的动作,按照给定程序、轨迹和要求实现自动抓取、搬运等功能‌。它通常由执行机构、驱动装置、控制系统以及传感器等组成,能够完成各种复杂的动作。‌ 机械臂在工业、医学、娱乐、…

【Python技术】同花顺wencai涨停分析基础上增加连板分析

周末,有读者加我, 说 之前的涨停分析 是否可以增加连板分析。 这个可以加上。 先看效果 这里附上完整代码: import streamlit as st import pywencai import pandas as pd from datetime import datetime, timedelta import plotly.graph_o…

小程序子组件调用父组件方法、父组件调用子组件方法

1、子组件调用父组件方法 子组件this.triggerEvent(finish); startShare(e) {let url config.apiUrl "/business/lzShare/edit";let data this.data.currentData;util.httpPut(url, data).then((res) > {this.triggerEvent(finish);console.log(res.result);})…

怎样使用Eclipse创建Maven的Java WEB 项目

文章目录 1、第一种方式(选择 archetype 方式) 1.1、第一步:创建项目1.2、第二步:配置jre1.3、第三步:配置tomcat1.4、第四步:设置为WEB3.11.5、第五步:配置Maven的编译级别 1.5.1、第一种方法…

C语言刷题

1. 题目描述 根据给出的三角形3条边a:b.c(a.b,c<100.000)&#xff0c;计算三角形的周长和面积。 输入描述: 一行&#xff0c;三角形3条边(能构成三角形)&#xff0c;中间用一个空格隔开. 输出描述: 一行&#xff0c;三角形周长和面积保留两位小数&#xff0c;中问用一个空…

C语言动态内存管理【进阶--5--】

文章目录 [toc] 动态内存管理一、作用即意义二、动态内存函数的介绍Ⅰ、malloc()函数、free()函数Ⅱ、calloc()函数Ⅲ、realloc()函数 三、常见的动态内存错误Ⅰ、对NULL指针的解引用操作Ⅱ、对动态开辟空间的越界访问Ⅲ、对非动态开辟的内存使用free释放Ⅳ、使用free释放动态开…