WPF的消息机制(二)- WPF内部的5个窗口之隐藏消息窗口
目录
(1)隐藏消息窗口
(2)处理激活和关闭的消息的窗口和系统资源通知窗口
(3)用于用户交互的可见窗口
(4)用于UI窗口绘制的可见窗口
WPF的消息机制(三)-WPF输入事件的来源
WPF的消息机制(四)-WPF中UI的更新
WPF内部的5个窗口
对于Windows系统来说,它是一个消息系统,消息系统的核心就是窗口。对于WPF来说也是如此。那么WPF内部为什么需要窗口,又存在哪些窗口呢?
在上一篇,我们频繁的提及“线程”,“Dispatcher”其实,运行WPF应用程序所在的线程就是WPF所谓的UI线程,在Application.Run之后,调用Dispatcher.Run时会检查当前线程是否已经存在了一个Dispatcher对象,如果没有就构造一个,在这里,一个线程对应一个Dispatcher。因此,WPF的对象在获取this.Dispatcher属性时,不同对象取的都是同一个Dispatcher实例。另外,前面提到的“消息循环”,“消息队列”等都是Win32应用程序的概念,我们知道,提起这些概念,必然会跟Win32的“窗口”,“Handle”,“WndProc”之类的概念离不开,那么WPF里面究竟有没有“窗体”,“Handle”,“WndProc”呢?
我想说的是:有,还不止一个,只不过没有暴露出来,外面不需要关心这些。
通常情况下,一个WPF应用程序在运行起来的时候,后台会创建5个Win32的窗口,帮助WPF系统来处理操作系统以及应用程序内部的消息。在这5个窗口中,只有一个是可见的,可以处理输入事件与用户交互,其他4个窗口都是不可见的,帮助WPF处理来自其他方面的消息。接下来我会来介绍究竟这5个Win32的窗口如何帮助WPF处理消息,我会根据每个窗口创建的顺序来介绍。
隐藏消息窗口
创建时机:在Application的构造函数调用基类DispatcherObject的构造函数的时候,会创建一个Dispatcher对象,在Dispatcher的私有构造函数当中。
用途:实现WPF线程模型的异步调用。
谈到异步调用,相信许多人都不陌生。WinForm下,我们通常为了使一些花费较多时间的方法调用不影响UI的响应,会将这个操作分为很多步,然后使用BeginInvoke调用每一步,这样UI响应就不会被阻塞。BeginInvoke的本质是往消息队列当中PostMessage,而不是直接调用,与此同时,UI行为(MouseMove)导致系统也往消息队列当中PostMessage更新UI,但由于彼此花费的时间很短,就感觉两个消息是被同时处理似的,界面就不会觉得被阻塞了。WPF同样面临这样的问题,他是如何解决的呢?在这里Window 1#起着至关重要的作用。通过下面一副图我们来看看Window 1#在做什么事情?

WPF也是通过BeginInvoke来解决的,而Wpf的BeginInvoke是在Dispatcher上面暴露了,因为整个消息系统都是Dispatcher在协调。从上面图可以看出Dispatcher在调用BeginInvoke之后所经历的流程,最终是什么时候Foo()被真正执行的。
第一步,就是将调用的Delegate和优先级包装成一个DispatcherOperation放入Dispatcher维护的优先级队列当中,这个Queue是按DispatcherPriority排序的,总是高优先级的DispatcherOperation先被处理。关于优先级相关知识可以参考MSDN对WPF线程模型的解释。
第二步,往当前线程的消息队列当中Post一个名为MsgProcessQueue的Message。这个消息是WPF自己定义的,见Dispatcher的静态构造函数当中的
_msgProcessQueue = UnsafeNativeMethods.RegisterWindowMessage("DispatcherProcessQueue");
这个消息被Post到消息队列之前,还要设置MSG.Handle,这个Handle就是Window 1#的Handle。指定Handle是为了在消息循环Dispatch消息的时候,指定哪个窗口的WndProc(窗口过程)处理这个消息。在这里所有BeginInvoke引起的消息都是Window1#的窗口过程来处理的。
第三步,消息循环读取消息。
第四步,系统根据获取消息的Handle,发现跟Window1#的Handle相同,那么这个消息派发到Window1#的窗口过程,让其处理。
第五步,在窗口过程中,优先级队列当中取一个DispatcherOperation。
第六步,执行DispatcherOperation.Invoke方法,Invoke方法的核心就是调用DispatcherOperation构造时传入的Delegate,也就是Dispatcher.BeginInvoke传入的Delegate。最终这个Foo()方法就被执行了。
通过上面的六步过程,一次Dispatcher.BeginInvoke就被处理完成。而这个过程需要消息不断的流动,就必须加入消息队列,最后还要特定的窗口过程处理,而核心的东西就是这个隐藏的Window1#,他在WPF当中只负责处理异步调用,其他的消息他不关心,剩余的4个窗口在处理。这个Window1#在WPF当中被包了一层壳子,如果感兴趣,你可以去查看类型MessageOnlyHwndWrapper。
开发工具
ComponentOne Studio WPF 是专为桌面应用程序开发所准备的一整套控件包,崇尚优雅和创新,以“触控优先”为设计理念,内含轻量级高性能表格控件,和大量类型丰富的2D和3D图表控件,能使开发的应用程序更富创意。
WPF的消息机制(二)- WPF内部的5个窗口之隐藏消息窗口的更多相关文章
- window消息机制二
消息机制 windows是一个消息驱动的系统,会有一个总的系统消息的队列,鼠标.键盘等等都会流入到这个队列中,同时会为每个线程维护一个消息队列(注意默认是有GUI调用的线程才有,对于没有GUI或者窗口 ...
- Windows 消息循环(2) - WPF中的消息循环
接上文: Windows 消息循环(1) - 概览 win32/MFC/WinForm/WPF 都依靠消息循环驱动,让程序跑起来. 本文介绍 WPF 中是如何使用消息循环来驱动程序的. 4 消息循环在 ...
- WPF入门教程系列二十三——DataGrid示例(三)
DataGrid的选择模式 默认情况下,DataGrid 的选择模式为“全行选择”,并且可以同时选择多行(如下图所示),我们可以通过SelectionMode 和SelectionUnit 属性来修改 ...
- C#中的WinForm的消息机制简述,及消息机制下Invoke,和BeginInvoke的使用和区别
在Invoke或者BeginInvoke的使用中无一例外地使用了委托Delegate,至于委托的本质请参考我的另一随笔:对.net事件的看法. 一.为什么Control类提供了Invoke和Begin ...
- Android全面解析之由浅及深Handler消息机制
前言 很高兴遇见你~ 欢迎阅读我的文章. 关于Handler的博客可谓是俯拾皆是,而这也是一个老生常谈的话题,可见的他非常基础,也非常重要.但很多的博客,却很少有从入门开始介绍,这在我一开始学习的时候 ...
- CCBPM工作流引擎的消息机制与设计
keyword:ccflowjflow 消息机制流程引擎 自己主动发送短信 发送邮件 发送消息 流程引擎微信连接 消息接口 关于ccbpm: 我们把ccflow jflow两个版本号的工作流引擎统称为 ...
- 第十章:Android消息机制
Android的消息机制主要是指Handler的云心机制,Handler的运行需要底层的MessageQueue和Looper支持. Handler是Android消息机制的上层接口. 通过Handl ...
- 史上最详细的Android消息机制源码解析
本人只是Android菜鸡一个,写技术文章只是为了总结自己最近学习到的知识,从来不敢为人师,如果里面有不正确的地方请大家尽情指出,谢谢! 606页Android最新面试题含答案,有兴趣可以点击获取. ...
- 【转】深入Windows内核——C++中的消息机制
上节讲了消息的相关概念,本文将进一步聊聊C++中的消息机制. 从简单例子探析核心原理 在讲之前,我们先看一个简单例子:创建一个窗口和两个按钮,用来控制窗口的背景颜色.其效果 图1.效果图 Win32 ...
随机推荐
- Openssl 生成证书server.key and server.crt
1.key的生成 openssl genrsa -des3 -out server.key 2048 这样是生成rsa私钥,des3算法,openssl格式,2048位强度.server.key是密钥 ...
- 5、C#基础 - C#的值类型
1.C#的值类型 有几个特点: 存储在栈里 基于值类型的变量直接包含值(值类型存储实际值). 将一个值类型变量赋给另一个值类型变量时,将复制包含的值. 这与引用类型变量的赋值不同,引用类型变量的赋值只 ...
- Linux下实现CAD数据的导出
近期公司项目涉及到CAD的导出,而且部署服务器申请不到Windows下的,所以技术上的解决方案就是寻求如何在Linux下实现CAD数据的导出. 于是百度了一下,找了几个相关库和软件. 1.dxflib ...
- 42.Linux应用调试-初步制作系统调用(用户态->内核态)
1首先来讲讲应用程序如何实现系统调用(用户态->内核态)? 我们以应用程序的write()函数为例: 1)首先用户态的write()函数会进入glibc库,里面会将write()转换为swi(S ...
- 玩了几天的ARToolKit
因为玩的不深,就说一说配置环境出错的解决. 假如大家网上下载的ARToolKit下面有glut32.dll,那按照教程来配置环境的时候(其中有一步是:将OpenGL的dll拷贝到windows sys ...
- Android之通过网络播放一首简单的音乐
首先,附上程序执行后的效果.例如以下图所看到的: 一.部署一个web项目到tomcatserver上: 1.这个小程序是结合网络来播放一首音乐的,首先,把我们搞好的一个web项目放置在tomcat安装 ...
- 介绍一个法国的时间戳server
特别说明: 以下介绍的法国时间戳server已经停止服务了.我曾发Email给相关站点的管理员.对方回复说他也不知道什么时候能恢复服务,有可能就是遥遥无期了.所以以下的内容仅有參考价值.没法实践了. ...
- 【cogs 597】【dp】交错匹配
597. 交错匹配 ★☆ 输入文件:crossa.in 输出文件:crossa.out 简单对照 时间限制:1 s 内存限制:128 MB [问题描写叙述] 有两行自然数. UP[1..N] . DO ...
- urllib2的基本使用
urlopen 1 import urllib2 2 3 # 向指定的url发送请求,并返回服务器响应的类文件对象 4 response = urllib2.urlopen("http:// ...
- 字符设备 Vs. 块设备 Character Device Vs. Block Device
字符设备是指驱动发送/接受单个字符(例如字节)的设备. 块设备是指驱动发送/接受整块数据(例如512个字节为一个块)的设备. 常见的字符设备:串口,并口,声卡. 常见的块设备:硬盘(最小读取单位为扇区 ...