Win10是否会用新的WM_PAINT消息中断正在进行的WM_PAINT处理?
关于WM_PAINT消息嵌套调用的问题分析与解决
我之前也踩过这个GDI重绘的坑,咱们来好好捋清楚这个问题:
问题现象复盘
运行时错误的堆栈跟踪显示窗口过程被
WM_PAINT消息调用了两次。窗口正处于处理第一条WM_PAINT消息的重绘过程中(调用BeginPaint后、EndPaint前),突然窗口过程再次被调用以处理新的WM_PAINT消息。这一情况显然发生在调用SetBkColor或SelectObject之后。已知应用调用UpdateWindow(或使用SendMessage发送WM_PAINT)时可能出现该现象。
核心原因拆解
这种嵌套调用的本质是GDI操作触发了系统的隐式重绘请求。当你在BeginPaint获取窗口DC后,调用SetBkColor修改背景色、SelectObject切换GDI对象(比如画笔、画刷)这类操作时,Windows系统会认为当前窗口的绘制环境发生了变化,需要重新刷新内容,于是会再次投递WM_PAINT消息。如果此时你还在第一次WM_PAINT的处理流程里(没调用EndPaint释放DC),就会出现嵌套调用的情况。
另外你提到的UpdateWindow或SendMessage(WM_PAINT)是显式触发重绘的场景,这种情况下系统会直接调用窗口过程处理WM_PAINT,而不是把消息放到消息队列,所以很容易在当前重绘还没完成时就触发第二次调用。
可行的解决办法
- 杜绝重绘过程中的显式重绘触发:仔细检查
WM_PAINT的处理代码,确保在BeginPaint到EndPaint的区间内,没有调用UpdateWindow、InvalidateRect、RedrawWindow这类会标记窗口区域无效的函数,哪怕是间接调用的第三方函数也要排查清楚。 - 提前初始化并缓存GDI对象:不要每次处理
WM_PAINT都临时创建和选择GDI对象(比如画笔、字体),可以在窗口创建时(比如WM_CREATE消息里)就初始化好这些对象,在WM_PAINT里直接复用,减少SelectObject的调用次数,降低系统触发隐式重绘的概率。 - 采用双缓冲绘制:先把所有绘制操作在内存DC中完成,最后一次性将内存DC的内容复制到窗口DC上。这种方式让整个重绘过程变成原子操作,中间的GDI状态变更不会触发系统的即时重绘请求,从根源上避免嵌套调用。
- 添加重入标记防止嵌套:给窗口类加一个布尔成员变量(比如
m_isPainting),进入WM_PAINT处理时设为true,调用EndPaint后再设为false。如果再次收到WM_PAINT消息时发现m_isPainting为true,可以直接返回,或者用PostMessage把重绘请求放到消息队列,等当前重绘完成后再处理。
内容的提问来源于stack exchange,提问作者solmaker
相关产品推荐
相关产品推荐

