c++ - 如何在 WM_PAINT 处理程序中捕获 assert()?
问题描述
我有MSVC 2019,vc142 x64,SDK 10.0.18362.0,WINAPI游戏项目,启用JIT调试,_DEBUG
已定义。我assert()
从标准 lib使用#include <cassert>
。assert(expr)
呼叫扩展为_wassert
. 如果将测试代码assert(false)
放置在除WM_PAINT
处理程序之外的任何位置,则会显示一个带有 Abort/Retry/Ignore 选项的窗口,并且这是预期的行为。
但如果我有assert(false)
in case WM_PAINT
,则不会显示断言窗口。程序只是中止并写入标准错误。WM_PAINT
问题是从的处理程序(例如)调用了很多游戏逻辑Core::Update(dt)
,我无法捕获我的代码产生的任何断言。
WndProc 代码:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
{
assert(false);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
程序垃圾邮件此消息并且不会停止:
Program: ...t\source\repos\Test3\x64\Debug\WindowsProject1.exe
File: C:\Users\b2soft\source\repos\Test3\...\Windows...ct1.cpp
Line: 149
Expression: false
For information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts
(Press Retry to debug the application - JIT must be enabled)Assertion failed!
即使assert()
从WM_PAINT
解决方案
WM_PAINT
我们在处理消息时必须小心。Windows 管理器不断监视屏幕的哪些部分需要重绘,并将此信息添加到窗口的更新区域。当调用GetMessage
或PeekMessage
并且没有更高优先级的WM_PAINT
消息时,将为具有非空更新区域的窗口生成消息。
准备窗口的BeginPaint
设备上下文使用更新区域来限制绘画区域。清除此更新区域后,窗口甚至可以在开始绘制当前区域之前收集新的区域进行绘制。
如果我们省略BeginPaint
调用,更新区域不会被清除(除非我们使用替代解决方案,例如ValidateRect
)并且WM_PAINT
总是准备好被调度。WM_PAINT
这会导致处理器使用率高的无休止的消息流。请记住,DefWindowProc
在内部进行处理,WM_PAINT
因此这些问题仅在您显式拦截WM_PAINT
消息时才会出现。
在没有调用或调用之前使用asert
inWM_PAINT
处理程序BeginPaint
会导致消息框和WM_PAINT
消息之间发生不必要的交互。根据设置assert
可能会显示消息框,该消息框会启动新的(嵌套)消息循环。当再次生成原始窗口的消息框构建方式assert
时。WM_PAINT
这会一次又一次地导致下一个嵌套循环。在 32 次嵌套调用MessageBox
失败并且程序被中止(通过调用abort
函数)之后。
在停止递归循环嵌套之前放置BeginPaint
(或任何清除更新区域的替代方法)assert
(至少直到下一个WM_PAINT
被调度)并且assert
可以正确显示消息框。
因此,如果您想assert
在WM_PAINT
处理程序中使用,请将其放在BeginPaint
or之后ValidateRect(hWnd, NULL)
。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// assert here could lead to abort
switch (message)
{
case WM_PAINT:
{
// assert here could lead to abort
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// Update region cleared, no WM_PAINT will be generated
// until some event creates new dirty area
// assert here have chance to be handled properly
assert(false);
EndPaint(hWnd, &ps);
// assert here have chance to be handled properly
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
// assert here have chance to be handled properly
return 0;
}
请记住,这是基于观察到的行为而不是任何特定文档,因此您可能会根据设置、运行时版本甚至调试器是否附加而获得不同的结果。
使用 VC2017 x86 Debug build ucrt 10.0.17763.0 在 Windows 10 上完成的测试
就我个人而言,我不知道这个限制,它看起来有点糟糕。特别是对于放置在窗口过程顶部的最终断言。幸运的是,附加的调试器至少在输出窗口中显示错误,而且我从来没有在没有附加调试器的情况下运行调试版本。
推荐阅读
- javascript - 将内部 HTML 设置为无效 HTML 时如何保留原始 DOM 结构
- php - 无法将 AJAX POST 复制到 Postman
- python - request.GET.get() 是什么意思?
- batch-file - 通过批处理增加虚拟内存大小
- go - 使 jwt 编码更快
- linux - 汇编初学者问题:32 / 64 位代码差异
- python - 如何在python中执行快速浮点比较
- asynchronous - C++ 中异步编程的协程用法
- javascript - 在对象中生成新的属性名称而不会发生冲突
- java - 在两个设备之间同步 RecyclerView 数据?