繁体   English   中英

如何在 WM_PAINT 处理程序中捕获 assert()?

[英]How to catch assert() in WM_PAINT handler?

我有MSVC 2019,vc142 x64,SDK 10.0.18362.0,WINAPI游戏项目,启用JIT调试,定义_DEBUG 我使用标准库中的assert() #include <cassert> assert(expr)调用扩展为_wassert 如果将测试代码assert(false)放置在除WM_PAINT处理程序之外的任何位置,则会显示带有 Abort/Retry/Ignore 选项的 window 及其预期行为。

但是如果我在case WM_PAINT中有assert(false) ,则不会显示 assert window 。 程序只是中止并写入标准错误。 问题是从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!

即使从WM_PAINT触发了assert() ,我也希望使用 Abort/Retry/Ignore 选项进行相同的调试 window

在处理WM_PAINT消息时我们必须小心。 Windows 管理器不断监视屏幕的哪些部分需要重绘,并将此信息添加到窗口的更新区域。 当调用GetMessagePeekMessage并且没有更高优先级的消息时,将为具有非空更新区域的 windows 生成WM_PAINT消息。

BeginPaint使用更新区域准备窗口的设备上下文以限制绘画区域。 清除此更新区域后,window 甚至可以在开始绘制当前区域之前收集新的区域进行绘制。

如果我们省略BeginPaint调用,更新区域不会被清除(除非我们使用替代解决方案,例如ValidateRect )并且WM_PAINT始终准备好被调度。 这导致无休止的 stream 的WM_PAINT消息具有高处理器使用率。 请记住, DefWindowProc在内部处理WM_PAINT ,因此这些问题仅在您显式拦截WM_PAINT消息时才会出现。

在没有或之前调用BeginPaint的情况下在WM_PAINT处理程序中使用asert会导致消息框和WM_PAINT消息之间发生不必要的交互。 根据设置, assert可以显示消息框,该消息框开始新的(嵌套的)消息循环。 当要显示由assert构建的消息框时,将再次生成原始 window 的WM_PAINT 这会一次又一次地导致下一个嵌套循环。 在 32 次嵌套调用后, MessageBox失败并且程序被中止(通过调用abort函数)。

assert停止递归循环嵌套之前放置BeginPaint (或任何清除更新区域的替代方法)(至少直到下一个WM_PAINT被调度)并且assert可以正确显示消息框。

因此,如果您想在WM_PAINT处理程序中使用assert ,请将其放在BeginPaintValidateRect(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 在 Windows 10 上完成的测试 Debug build ucrt 10.0.17763.0

就我个人而言,我不知道这个限制,它看起来有点糟糕。 特别是对于放置在 window 过程顶部的最终断言。 幸运的是,附加的调试器至少在 output window 中显示错误,而且我从来没有在没有附加调试器的情况下运行调试版本。

如果不使用BeginPaintEndPaint函数,系统会在空闲时不断发送WM_PAINT消息,这会导致一些异常情况。

所以只需要修改代码如下:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        HDC hdc;
        PAINTSTRUCT ps;
        RECT rect;
    switch (message)
    {
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        assert(false);
        EndPaint(hWnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

Output: 在此处输入图像描述

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM