簡體   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