Rich Edit Control paints whole application black after unminimize

253 Views Asked by At

SOLVED: I've posted my solution as an answer.

Here's my problem: (gif) (Sort of solved if I reload the bitmaps for painting the background image when unminimizing before any WM_PAINT message.)

background disappearing

It happens whenever I unminimize the application, time when the app first displays OK (for a brief split second unless you are stepping with the debugger), and suddenly turns black (or whatever color has been set as hbrBackground in the app window classes). I can prevent this behaviour by reloading the HBITMAPs used in WM_PAINT, which are global variables and initialised with their corresponding values at app startup.

The gif starts showing the app reopened after a minimize, with the debugger stepping through the parent window of the Rich Edit Control message loop, the moments just before and after the background of all windows turns black, and then stepping into the Rich Edit Control subclass message loop, into WM_PAINT.

  • This never happens if I'm switching between apps without the app in question never having been minimized before.
  • This never happens if the Rich Edit Control (RICHEDIT50W) hasn't displayed any text before, ie. the app works OK if no text is ever displayed.
  • This is the window tree:
    • Main Window
      • Some Child Windows
      • Child Window 1
        • Rich Edit Control

The stepping goes out of the Child Window 1 WndProc; into the WM_PAINT of the Rich Edit Control inside the WndSubclassProcWhatever callback.

Some of the things I've done before realizing that a call to LoadImage() just after unminimize could fix the background issue:

  • Intercept the message loop of the Rich Edit Control with a subclass, and handle (as well as in every other window) messages as: WM_COMMAND, WM_IME_NOTIFY, WM_NCPAINT, WM_WINDOWPOSCHANGED, WM_WINDOWPOSCHANGING, WM_ERASEBKGND... Mainly returning something different than the DefSubclassProc/DefWindowProcW.

  • Calling ValidateRect() as soon as the app is reopened...

    It has happened before that instead of the whole app turning black, only the text "highlighting" or the Rich Edit Control parent turned black, with the whole app turning black after another minimize unminimize cycle.

I'm using Visual Studio Community 2019 with default settings in an updated Windows 10, and seeing this problem both in release and debug builds.

I'm now looking forward to prevent the bitmaps from "unloading", thus saving many seemingly unnecessary LoadImage() calls. SOLVED

I tried uploading a minimal version of the code, yet the behaviour turned out not to be exactly the same, so thanks for the answer given before!

2

There are 2 best solutions below

4
Strive Sun On

This has nothing to do with Rich Edit Control , even if you delete all of the controls, this will happen.

All you have to do is add a default color to the window background when you register the window.

Here:

ATOM MyRegisterClass(HINSTANCE hInstance)
    {
        WNDCLASSEXW wcex;

        wcex.cbSize = sizeof(WNDCLASSEX);

        wcex.style = CS_DBLCLKS;
        wcex.lpfnWndProc = WndProc;
        wcex.cbClsExtra = 0;
        wcex.cbWndExtra = 0;
        wcex.hInstance = hInstance;
        wcex.hIcon = NULL; // Procesás WM_GETICON
        wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
        wcex.hbrBackground = NULL;
        wcex.lpszMenuName = NULL;
        wcex.lpszClassName = L"mainWindowClass";
        wcex.hIconSm = NULL; // Procesás WM_GETICON

        return RegisterClassExW(&wcex);
    }

Click again after minimize the window will cause it to redraw with the default background color, But you set the background color to NULL here. So try to change wcex.hbrBackground = NULL to wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1)

Updated:

It sounds like you have the same problem as I have encountered before.

Here is my previous code:

 case WM_PAINT:
        {
            PAINTSTRUCT ps;
           HDC hdc =  BeginPaint(hWnd, &ps);
            // TODO: Add any drawing code that uses hdc here... 
           hdcMem = CreateCompatibleDC(hdc);
           HGDIOBJ previousbit = SelectObject(hdcMem, hBmp);
           AlphaBlend(hdc, 0, 0, width_1, height_1, hdcMem, 0, 0, width_1, height_1, bf);
           DeleteDC(hdcMem);
           EndPaint(hWnd, &ps);
        }
        break;
case WM_MOUSEWHEEL:
    {
        if (GET_WHEEL_DELTA_WPARAM(wParam) > 0 && bTrans <= 234)
        {
            bTrans += 20;
            bf.SourceConstantAlpha = bTrans;
            InvalidateRect(hWnd, NULL, TRUE);
        }
        if (GET_WHEEL_DELTA_WPARAM(wParam) < 0 && bTrans >= 20)
        {
            bTrans -= 20;
            bf.SourceConstantAlpha = bTrans;
            InvalidateRect(hWnd, NULL, TRUE);
        }
        return 0;
    }

I slide the mouse wheel, it will trigger the InvalidateRect(hWnd, NULL, TRUE);

But if I delete DeleteDC(hdcMem), it will return a main window without a picture.

The debug snapshot is :

enter image description here

Yes, you can find previousbit == NULL.

As @Remy Lebeau said that, you are leaking the HBITMAP that SelectObject() returns, and giving the HDC permission to potentially destroy your bitmapBackgroundMainWindow behind your back.

This is the main cause.

1
Juan Manuel López Manzano On

A call to DeleteObject() fixed the issue.

This is the code from one of the bitmap-background window WM_PAINT messages fixed with the corresponding DeleteObject() call:

    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hWnd, &ps);
    HDC temporaryDC = CreateCompatibleDC(hdc);
    BITMAP bitmapToBitBlt;
    HGDIOBJ hgdiobjToBitBlt = SelectObject(temporaryDC, bitmapBackgroundMainWindow);
    GetObjectW(bitmapBackgroundMainWindow, sizeof(BITMAP), &bitmapToBitBlt);
    BitBlt(hdc, 0, 0, bitmapToBitBlt.bmWidth, bitmapToBitBlt.bmHeight, temporaryDC, 0, 0, SRCCOPY);
    DeleteObject(temporaryDC); // This fixes the app.
    EndPaint(hWnd, &ps);
    return 0;

As Windows Docs state, after calling CreateCompatibleDC():

When you no longer need the memory DC, call the DeleteDC function. We recommend that you call DeleteDC to delete the DC. However, you can also call DeleteObject with the HDC to delete the DC.

How does this translate to my app unexpected behaviour, I don't know, feel free to clarify in the comments!