关于OnPaint函数的工作原理(很详细,很实用) [转载]

地址:http://blog.csdn.net/foreverhuylee/article/details/21889025

用了两年的VC,其实对OnPaint的工作原理一直都是一知半解。这两天心血来潮,到BBS上到处发帖询问,总算搞清楚了,现在总结一下。

对于窗口程序,一般有个特点:窗口大部分的区域保持不变,只有不分区域需要重新绘制。如果将整个窗口全部刷新的画,就做了许多不必要的工作,因而,MFC采用了一套基于无效区的处理机制。在分析无效区处理之前,我们要明白一个现实,现在的机器还不够牛,如果够牛的话,我们干脆将整个窗口不断的重新绘制好了。事实上即使够牛也不行,对于一个单线程程序,通过一个while循环不断的刷新窗口,程序也无法相应其他消息(除非使用多线程),看来使用无效区的处理机制还是有其必然性的。

VC程序是基于消息机制的,你所做的任何操作,比如点击鼠标,拖动窗口,首先进入系统的消息队列。这里的系统消息队列包括多个程序的消息,系统再将消息发送给相应的程序。既然是队列,这就有一个先进先出的问题,屏幕上的无效区更新消息出现的频率就会特别高。比如当左上角更新的消息还没有处理,右下角更新的消息已经过来了。为了避免多次处理WM_PAINT消息,系统就将这些窗口更新消息合并到一条,只是将无效区范围变成包括这两次更新无效区范围在内的矩形区域。这样就减少了WM_PAINT消息的处理次数,提高了效率。

那么,在OnPaint消息处理函数中,又是怎样实现更新无效区的呢?首先,要明白MFC中所有绘图操作都是基于设备描述表(Device Context,简称DC)的,具体信息可参看任何一本VC教材。DC中包含了绘图设备的各种信息,对于屏幕绘图,其实就是有一块内存(显存),专门用来存放要显示到屏幕上的信息,显示器以85HZ的频率(我以前的显示器)将其内容刷新的屏幕上。这里就到了关键点,显示器的刷新是将显存中的内容完全更新到显示器上,不存在无效区处理的问题,那么,无效区的处理一定发生在DC的绘图处理上。事实确实如此,当程序调用OnPaint消息时,首先将无效区范围传递给DC,DC在进行绘图操作时,就只更新无效区范围内的信息,其他地方的不管,这就提高了效率。开启OnPaint函数有下面三种选择:

1)  直接发送WM_PAINT消息,用PostMessage(),SendMessage()函数发送WM_PAINT消息。使用以上两函数发送WM_PAINT消息,能将WM_PAINT消息发送到WINDOWS程序消息队列中,当WINDOWS将WM_PAINT消息发送给具体的消息处理函数时,如果窗口的无效区域为空则WINDOWS将不理睬该消息。若存在无效区域,则调用窗口处理函数处理。要注意的这里需要存在无效区域,因此要调用2)中的函数使得窗体(或者部分)无效,其处理过程与2)相同,将WM_PAINT消息送入消息处理队列。与3)不同的是WM_PAINT并不立即处理;

2)  调用相应的API实现WM_PAINT消息的发送:Invalidate(),InvalidateRect(), InvalidateRgn():以上函数将窗口的特定区域标定为无效,当WINDOWS检测到窗口中存在无效区域时将向消息队列发送WM_PAINT 消息。我当时用的就是Invalidate()函数;

3)  UpdateWindow():该函数调用后WINDOWS将向窗口发送一个非队列化的WM_PAINT消息,它不经过消息循环而直接发送给了窗口消息处理函数。如果窗口无效区域不存在,WINDOWS将不理睬该消息。注意这里因为要使得窗口无效区不存在,因此还是调用Invalidate(),InvalidateRect(), InvalidateRgn()函数,和2)中不同的是这里的WM_PAINT消息会被立即处理,而2)中是加入消息处理队列。

简单起见,你可以使用2)中方案进行问题解决。

现在你明白OnPaint的处理是怎么一回事了吧?这里还想说一下Invalidate和UpdateWindow的区别。Invalidate在消息队列中加入一条WM_PAINT消息,其无效区为整个客户区。而UpdateWindow直接发送一个WM_PAINT消息,其无效区范围就是消息队列中WM_PAINT消息(最多只有一条)的无效区。效果很明显,调用Invalidate之后,屏幕不一定马上更新,因为WM_PAINT消息不一定在队列头部,而调用UpdateWindow会使WM_PAINT消息马上执行的,绕过了消息队列。如果你调用Invalidate之后想马上更新屏幕,那就加上UpdateWindow()这条语句。

时间: 2024-10-02 19:34:37

关于OnPaint函数的工作原理(很详细,很实用) [转载]的相关文章

C++学习笔记27,虚函数的工作原理

C++规定了虚函数的行为,但是将实现交给了编译器的作者. 通常,编译器处理虚函数的方法是给每一个对象添加一个隐藏成员.隐藏成员中保存了一个指向函数地址数组的指针. 这个数组称为虚函数表(virtual function table,vtbl).虚函数表中存储了为类对象进行声明的虚函数的地址. 例如:基类对象包含一个指针,该指针指向基类的虚函数表. 派生类对象包含一个指针,该指针指向一个独立的虚函数表.如果派生类提供了虚函数的新定义,虚函数表将保存新的函数地址. 如果派生类没有重新定义虚函数,虚函

HTTPS详解二:SSL / TLS 工作原理和详细握手过程

HTTPS 详解一:附带最精美详尽的 HTTPS 原理图 HTTPS详解二:SSL / TLS 工作原理和详细握手过程 在上篇文章HTTPS详解一中,我已经为大家介绍了 HTTPS 的详细原理和通信流程,但总感觉少了点什么,应该是少了对安全层的针对性介绍,那么这篇文章就算是对HTTPS 详解一的补充吧.还记得这张图吧. HTTPS 和 HTTP的区别 显然,HTTPS 相比 HTTP最大的不同就是多了一层 SSL (Secure Sockets Layer 安全套接层)或 TLS (Transp

[转组第7天] | 函数的工作原理

2018-05-04 <C++反汇编和逆向技术>第六章 函数的工作原理 读书笔记 debug版本的函数调用: call func func: push ebp ;保存ebp mov ebp,esp sub esp,40h ;抬高esp,开辟栈空间 push ... ;保存寄存器 ... pop ... ;还原寄存器 add esp,40h ;降低esp,释放局部变量空间 cmp ebp,esp ;检测栈平衡 call __chkesp ;进入栈平衡错误检测函数 mov esp,ebp ;还原e

Zookeeper 3、Zookeeper工作原理(详细)

1.Zookeeper的角色 » 领导者(leader),负责进行投票的发起和决议,更新系统状态 » 学习者(learner),包括跟随者(follower)和观察者(observer),follower用于接受客户端请求并想客户端返回结果,在选主过程中参与投票 » Observer可以接受客户端连接,将写请求转发给leader,但observer不参加投票过程,只同步leader的状态,observer的目的是为了扩展系统,提高读取速度 » 客户端(client),请求发起方 • Zookee

函数的工作原理——划分RAM搞不懂啊???

1.看到<21天学会C++>P92的函数工作原理之划分RAM,感觉还是迷迷糊糊,不太明白,进一步查询??? 2.程序启动时,操作系统(如DOS,Windows等)将依据编译器的需求设置各种内存区域. 对于一个C++程序员来说,经常需要关心的是全局名称空间.自由存储器.寄存器.代码空间和堆栈. 3.寄存器:CPU中的一个特殊存储区域,任意给定时刻指向下一行代码代码的寄存器组的寄存器被称为指令指针.指令指针的任务是跟踪接下来将执行哪一行代码. 4.代码空间:代码本身存放在代码空间中,每行代码都被转

函数的工作原理

函数的工作借助于栈. 栈在内存中是一块特殊的存储空间,它的存储原则是"先进后出",最先被存储的数据最后被释放. esp被称为栈顶指针,ebp称为栈底指针,通过这两个指针寄存器保存当前栈的起始地址与结束地址. esp与ebp之间所构成的空间便成为栈帧.通常,在VC++中,栈帧中可以寻址的数据有局部变量.函数返回地址.函数参数等.不同的两次函数调用,所形成的栈帧也不同.当由一个函数进入到另一个函数中时,就会针对所调用的函数形成所需的栈空间,形成此函数的栈帧.当这个函数结束调用时,需要清除掉

基于arm的C++反汇编 函数的工作原理

栈帧的形成和关闭 各种调用方式的考擦 使用 fp或sp寻址 函数的参数 与返回值 arm指令中立即数存放位置 gdbserver 调试环境 栈帧的形成和关闭 ??栈在内存中是一块特殊的存储空同, 它的存储原则是"先进后出", 即最先被存储的数据最后被释放, 汇编过程通常使用 push 指令与 POP指令对栈空间执行数据压入和数据弹出操作. ??栈结构在内存中占用一段连续的存储空间, 通过sp与 fp这两个栈指针寄存器(在x86上是esp,ebp)来保存当前栈的起始地址与结束地址(又称为

DataGridView很详细的用法(转载)

一.DataGridView 取得或者修改当前单元格的内容: 当前单元格指的是 DataGridView 焦点所在的单元格,它可以通过 DataGridView 对象的 CurrentCell 属性取得.如果当前单元格不存在的时候,返回Nothing(C#是null) // 取得当前单元格内容 Console.WriteLine(DataGridView1.CurrentCell.Value); // 取得当前单元格的列 Index Console.WriteLine(DataGridView1

JVM 垃圾回收器工作原理及使用实例介绍(转载自IBM),直接复制粘贴,需要原文戳链接

原文 https://www.ibm.com/developerworks/cn/java/j-lo-JVMGarbageCollection/ 再插一个关于线程和进程上下文,待判断 http://blog.sina.com.cn/s/blog_75e9551f01016cm3.html 垃圾收集基础 Java 语言的一大特点就是可以进行自动垃圾回收处理,而无需开发人员过于关注系统资源,例如内存资源的释放情况.自动垃圾收集虽然大大减轻了开发人员的工作量,但是也增加了软件系统的负担. 拥有垃圾收集