今天遇到一个比较有意思的bug, 这里简单记录下。

Bug的症状是通过拖拉边框把我们客户端主窗口拖小之后,再最大化,会发现窗口显示有问题, 看起来像是刷新问题, 有些地方显示的不对了。
这里要说明的是我这里的主窗口是非常复杂的窗口, 里面集成了很多组件(cpmponent),有很多层的子窗口。 这个问题只有在特定条件下才会发生, 正常情况下都是好的。
遇到这种问题,我们怎么处理? 
首先当然是观察症状, 究竟是刷新问题, 还是Layout出错了。
我们可以通过Spy++查看窗口层次是不是正确, 窗口位置是不是对的。
查看结果是窗口的层次和Layout位置都没有问题。
既然我们这里遇到的刷新问题,所以我们要从WM_PAINT消息着手, 我们通过Spy++查看相关窗口的WM_PAINT是不是正确。
很快我们就会发现某个窗口正在不停地收到WM_PAINT消息, 很可能与我们的bug有关。
一个窗口不停的收到WM_PAINT重画, 无非大概有几类原因:
正常情况是我们正在做动画, 可能是通过定时器之类的东西让窗口不停地InvalidateRect重画某块区域, 我们的窗口明显不属于这种情况。
讨论异常情况前先讨论WM_PAINT消息,我们知道WM_PAINT消息里一定要调用BeginPaint和EndPaint, 前者告诉系统绘画开始,系统会把当前窗口的无效区域变得有效, 后者结束某次绘画。
异常情况有时是WM_PAINT消息里我们的消息处理函数在某些条件下直接返回了,从而没有调用BeginPaint告诉窗口无效区域已经有效, 这样会因为因为窗口一直有无效区域存在,导致窗口一直收到WM_PAINT消息。
还有一种异常情况情况是我们是在WM_PAINT消息里调用BeginPaint后又调用了InvalidateRect, 这样会导致窗口后面会再次收到WM_PAINT消息, 最后窗口陷入WM_PAINT的死循环。 
那么我们这里的问题窗口属于哪类? 用什么方法可以判断出来?
注意到这里关键的三个API:BeginPaint, EndPaint, InvalidateRect的第一个参数都是窗口句柄, 我们可以通过WinDbg的API断点来跟踪执行过程, Attach WinDbg到我们的主窗口进程,比如我们的窗口句柄是0x209A0, 我们可以这样设置API断点:
bp USER32!NtUserInvalidateRect ".if(dwo(@esp+0x4)==0x209A0) {kv;gc} .else {gc}"
bp USER32!NtUserBeginPaint ".if(dwo(@esp+0x4)==0x209A0) {kv;gc} .else {gc}"
bp USER32!NtUserEndPaint ".if(dwo(@esp+0x4)==0x209A0) {kv;gc} .else {gc}"
上面的条件断点表示,当调用我们的对应的API,并且第一个参数(窗口句柄)是我们的目标窗口时,打印堆栈。
很快我定位出Bug发生的原因了, 条件断点显示了API如下的调用次序:
BeginPaint->InvalidateRect->InvalidateRect->EndPaint
找到Bug的原因后,然后把Bug assign给该模块的负责人。 (看我够意思吧,不仅找到原因,还把调用栈都提供了)
另外 ,后面还发现这个bug发生时窗口的某些行为会不对, 测试发现原因是所有窗口的定时器都不能正常工作了。
关于这个问题, 你能想到原因吗? 
如果想不到, 请把我的这篇博客《从点击Button到弹出一个MessageBox, 背后发生了什么》看一遍。
如果看了还想不到, 重点看第4条。
最后, 简单总结下:计算机的好处是它永远不会欺骗你, 它只会按部就班的执行, 所以很多看似奇怪(甚至看似不可思议的问题), 只要你理解了程序背后的机制原理,都是可以找出根本原因的。

记一个界面刷新相关的Bug的更多相关文章

  1. VUE的一个数据绑定与页面刷新相关的bug

    1.场景: N层嵌套的循环查询业务场景,框架是vue.其中在最后一层查完之后,还需要查其中每一项的两个属性,类型都是列表.查完之后将其赋值给一个变量用于页面展示.代码如下: (1)异常代码: getS ...

  2. MFC截图和界面刷新相关问题

    问题描写叙述:         就是首先用CDC来截图,保存图片的路径通过dlg窗体来手动设置并传入.但是截下来的图片就会连带那个对话框也截图下来.         就是这样.我想截后面那个图.前面这 ...

  3. iOS开发——常见错误——使用MJRefresh返回上一个界面蹦掉的情况

    最近在使用MJRefresh框架时发现了一个bug 下面是我的源代码 前一个界面 -(void)tableView:(UITableView *)tableView didSelectRowAtInd ...

  4. Android界面刷新之invalidate与postInvalidate的区别

    Android的invalidate与postInvalidate都是用来刷新界面的. 在UI主线程中,用invalidate():本质是调用View的onDraw()绘制. 主线程之外,用postI ...

  5. 记一个社交APP的开发过程——基础架构选型(转自一位大哥)

    记一个社交APP的开发过程——基础架构选型 目录[-] 基本产品形态 技术选型 最近两周在忙于开发一个社交App,因为之前做过一点儿社交方面的东西,就被拉去做API后端了,一个人头一次完整的去搭这么一 ...

  6. Android之界面刷新(invalidate和postInvalidate使用)

    Android中实现view的更新有两组方法,一组是invalidate,另一组是postInvalidate,其中前者是在UI线程自身中使用,而后者在非UI线程中使用. Android提供了Inva ...

  7. Android界面刷新

    Android的invalidate与postInvalidate都是用来刷新界面的,用法区别在于: 1)invalidate():实例化一个Handler对象,并重写handleMessage方法调 ...

  8. Android界面刷新方法

    Android提供了Invalidate方法实现界面刷新,但是Invalidate不能直接在线程中调用,因为他是违背了单线程模型:Android UI操作并不是线程安全的,并且这些操作必须在UI线程中 ...

  9. IE的CSS相关的BUG(整理一)

    本来不想弄这个ie的bug的,真的很想让它快点死掉,可是事与愿违啊,没办法,还是贴出来,以备自用. 这个网页(http://haslayout.net/css/index)上例举了所有的IE和CSS相 ...

随机推荐

  1. mac端的优秀抓包工具——Charles使用

    http://my.oschina.net/u/2340880/blog/508688 mac端的优秀抓包工具——Charles使用 一.简介 二.安装与使用 三.使用Charles在mac上进行抓包 ...

  2. (python) 标准模块sys和os的使用

    一.sys模块 包含了系统的相关的功能.我们来学习sys.argv,它包含命令行参数. 例子:定义了一个add函数,用来实现两个整数的相加. #! coding=utf-8 # usersys.py ...

  3. 开博了,hello world

    hello  world     hello  world    hello  world     hello  world   hello  world     hello  world    he ...

  4. delegate notification kvo三者比较

    IOS 如何选择delegate.notification.KVO? 前面分别讲了delegate.notification和KVO的实现原理,以及实际使用步骤,我们心中不禁有个疑问,他们的功能比较类 ...

  5. ubuntu添加桌面或launcher快捷方式

    以eclipse为例,自行下载的. 创建文件/usr/share/applications/eclipse-kepler.desktop 文件内容: #------------------------ ...

  6. 复合梯形公式与Simpson公式的数值积分

    #include <iostream>#include<math.h>#include<stdio.h>using namespace std; float f(f ...

  7. IT小喇叭,关注于移动互联网创新、创业的科技媒体

    IT小喇叭(itxiaolaba.cn),成立于2015年6月,成都芮嘉科技有限公司旗下品牌.主要负责媒体资源整合,媒体渠道接入,产品宣传,资源整合.对接等,是企业产品宣传,品牌营销的首选. 通过近一 ...

  8. 透明activity来实现悬浮蔗罩

    第一步, activity的代码, 关键代码是进出动画采用淡入淡出方式,让蔗罩出现消失更自然,全屏 public class RobotGuidceV2Activity extends RoboAct ...

  9. 关于webapp的一点思考

    早上上班路上碰到发传单推广app的小MM被上司训斥,忽然想起一个问题,现在的nativeapp推广成本到底多高?能不能用浏览器访问app的方式降低这个成本. 现在很多app采用壳+web内容的方式,但 ...

  10. Codeforces #380 div2 B(729B) Spotlights

    B. Spotlights time limit per test 1 second memory limit per test 256 megabytes input standard input ...