1

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!

Community
  • 1
  • 1
  • post a minimal version of your code that can be built and run that exhibits the bug, please. Otherwise hard to guess what is wrong. – jwezorek Oct 29 '19 at 23:49
  • @jwezorek Done! Sorry, I was hoping someone could relate to my problem without too much hassle. Notice it is necessary for the error to have that settings that turn the window into a borderless no title thing, yet I would like to keep that the same way! – Juan Manuel López Manzano Oct 30 '19 at 01:02

2 Answers2

3

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.

Strive Sun
  • 5,988
  • 1
  • 9
  • 26
  • Thank you very much for answering. I'm really sorry, you're right in that my minimal example did not show the behaviour present on the gif and changing it's background was a fix. I've tested changing my app main window background and the problem persisted. I resorted to uploading the whole app (it's not extensive so far), hoping not to give personal information away! Again, I appreciate your help and hope you didn't fiddle with the previous example for too long, I'm sorry. – Juan Manuel López Manzano Oct 30 '19 at 10:49
  • I haven't got 15 reputation in this stack yet, but your answer was appropriate to my question as it standed, so you'll have an upvote as soon as I'm able to cast votes! – Juan Manuel López Manzano Oct 30 '19 at 10:52
  • Thank you again for your help! I've posted a solution to the problem. If you happen to figure out how not deleting the HDC properly could translate to a global HBITMAP used in WM_PAINT being erased on unminimize don't hesitate to expand on the topic or post a better answer than mine, I'll accept your answer if it improves upon mine! – Juan Manuel López Manzano Oct 30 '19 at 13:36
  • @JuanManuelLópezManzano Don't care too much, it will be my pleasure to help you at any time. – Strive Sun Oct 31 '19 at 03:37
1

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!

  • 1
    You are leaking the `HBITMAP` that `SelectObject()` returns, and giving the `HDC` permission to potentially destroy your `bitmapBackgroundMainWindow` behind your back. You need to `SelectObject()` the original `HBITMAP` back into the `HDC` before destroying the `HDC`: "*[SelectObject] returns the previously selected object of the specified type. **An application should always replace a new object with the original, default object after it has finished drawing with the new object**.*" Also, use `DeleteDC()` to destroy the `HDC` instead of `DeleteObject()`, like the documentation says. – Remy Lebeau Oct 31 '19 at 01:54