为何这段C++代码中CWnd::UpdateWindow会出现冻结?
MFC老旧C++应用冻结问题排查
维护一个基于MFC的老旧C++应用,该应用自2018年以来一直正常运行,近期出现奇怪的冻结问题,经排查冻结源于以下代码中的CWnd::UpdateWindow方法:
void CVisBeregning::process_messages() { MSG msg; if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } if (vb->GetSafeHwnd()) { vb->UpdateWindow(); } }
CVisBeregning是派生自CDialog的类,vb是其静态成员,定义如下:
static CVisBeregning * vb;
vb在类的构造函数中被赋值为this,用于外部代码的回调(需全局访问该类的唯一实例)。
冻结触发场景
冻结发生在第一个if判断返回false时,即无消息需要处理的场景。测试中该方法被调用十几次后,首次出现无消息情况时触发冻结。将第二个if块移至第一个if内可规避问题,但需明确冻结原因。
补充信息
if (vb->GetSafeHwnd())检查是必要的,因为应用有两种运行模式,其中一种模式下类未关联窗口,此时不会调用UpdateWindow。- 开发环境:Visual Studio 2019、Windows 10 x64。
2022-09-26补充细节
CVisBeregning对象在同一线程中调用外部优化求解程序Mosel,该程序的不同事件会触发CVisBeregning类的不同方法回调:
- 每次Mosel找到新解时,会调用方法将解写入
CVisBeregning对话框的文本控件; - 定期触发
userinterrupt回调,对应方法检查用户是否尝试取消运行,若则终止Mosel执行。
添加process_messages()方法是为了避免Windows将对话框标记为“未响应”。
未冻结时的调用栈
> Gassopt.exe!CVisBeregning::process_messages() Line 647 C++ Gassopt.exe!CVisBeregning::mi_userinterrupt(Vimactx * ctx, void * libctx) Line 759 C++ [External Code] Gassopt.exe!CVisBeregning::RunMosel(ATL::CStringT<char,StrTraitMFC<char,ATL::ChTraitsCRT<char>>> parameters, CDialog * calling_dialog, int inBatchId) Line 448 C++ Gassopt.exe!CVisBeregning::OnPaint() Line 155 C++
冻结前的调用栈
> Gassopt.exe!CVisBeregning::process_messages() Line 653 C++ Gassopt.exe!CVisBeregning::mi_userinterrupt(Vimactx * ctx, void * libctx) Line 759 C++ [External Code]
额外观察
外部程序的调用在CVisBeregning的OnPaint()方法中完成(该方法未执行任何绘制操作),虽已避免重复调用,但怀疑重写OnPaint()却不做绘制是否合理。原设计用DoModal()初始化对象,为确保RunMosel()在窗口激活后执行才采用此方案,或许应改用消息或定时器实现。
内容的提问来源于stack exchange,提问作者Michal Kaut
相关产品推荐
相关产品推荐

