Link to home
Start Free TrialLog in
Avatar of skirmish76
skirmish76

asked on

Problems with WM_ERASEBKGND in DialogBox

First up: I am not using MFC, I am using plain a old Win32 DialogBox(...).

I read an article saying I could create a gradient background by handling the WM_ERASEBKGND message in my dialogue's message proc.  I've done that and it looks good but the problem is as soon as I move my dialogue everything goes bad.  Other controls don't repaint properly if at all and if move the dialogue so that any part of it leaves the viewport then I get artifacts everywhere (everywhere on my moniter, not just the dialog).

Prior to this I didn't overload WM_ERASEBKGND, instead I just returned a solid brush from WM_CTLCOLORDLG and everything was happy (though boring).  What am I doing wrong?

Here is a truncated version of my msg proc:
<cpp>
INT_PTR CShell::MsgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {    
        case WM_ERASEBKGND:
        {
            //if(HDC(wParam) != GetDC(Instance()->hWnd)) return FALSE;
            //doesn't work

            RECT rect, row;
            BYTE r, g, b;
            GetClientRect(hDlg, &rect);
            row.left = 0;
            row.right = rect.right;
           
            //--- loop through 1 pixel high rows ---//
            for(int i = 0; i < rect.bottom; ++i)
            {
                r = static_cast<BYTE>(r1 + (i * (r2-r1) / rect.bottom));
                g = static_cast<BYTE>(g1 + (i * (g2-g1) / rect.bottom));
                b = static_cast<BYTE>(b1 + (i * (b2-b1) / rect.bottom));

                row.top = i;
                row.bottom = i+1;
                FillRect(reinterpret_cast<HDC>(wParam), &row, CreateSolidBrush(RGB(r, g, b)));
            }


            return TRUE;
        }
    }
    return FALSE;
}</cpp>
Avatar of KurtVon
KurtVon

Hmm, works for me.  The only problem I can see is that you are leaking GDI handles like crazy.  Maybe your system is just running out when you are testing?

Change the FillRect line to

HBRUSH hBrush = CreateSolidBrush(RGB(r, g, b));
FillRect(reinterpret_cast<HDC>(wParam), &row, hBrush);
DeleteObject(hBrush);

and that should solve the leak.

Hope this helps.
Avatar of skirmish76

ASKER

Thanks for the reply Kurt.  I added the clean up you suggest but the problem remains.  Still now at least it is bleeding the GDI dry.

Cheers
-A-
Hmm, like I said, it works fine on my machine.

Did you override WM_PAINT also?  I left all messages except the WM_ERASEBKGND blank.
What I'd suggest is try commenting out all dialog functionality except the creation needed code and the WM_ERASEBKGND handler.  Just the minimum code to keep the program from crashing.  Then test it again and see if the drawing problem still happens.  If it doesn't, then add back in the code until it starts again.

If it does still happen try replacing your drawing code with a simple PatBlt and see if it still causes trouble.  If not, it's part of the drawing code, maybe something still isn't being freed correctly.

If it does still happen, try commenting out the WM_ERASEBKGND message handler entirely.  If it doesn't happen (and the dialog should look pretty odd in this case, always showing what was under it before it moved)  try explicityly casting the parameter just once.

Hope this helps.
I commented out all the other messages and I still have the problem.  I suspected that maybe several WM_ERASEBKGND messages were being sent before the first had completed but I checked for that and it is fine.

I can put as much code in that message as I like and it fine, the one line causing all the problems is:
FillRect(reinterpret_cast<HDC>(wParam), &row, CreateSolidBrush(RGB(r, g, b)));

If I comment it out then the app is fine (of course it has no background).

The redraw is painfully slow (you can see the trace across the dialog) when it is stationary but when I move the dialog so any of it goes outside of screen space or another app gets focus then it completely flakes out - components just aren't redrawn fully and vertical stripes appear on the entire screen.
I replaced the FillRect with a PatBlt and the problem remains, however it is now only evident if I move the window so it leaves the screen or move another window on top of it.  

If I alt+tab from the app and then back to it though it does repaint properly.

By handling the WM_ERASEBKGND  message for my dialogue it seems to screw with the repainting of other components (or maybe it is painting over them).
You did change the line to

FillRect(reinterpret_cast<HDC>(wParam), &row, hBrush);

right?  You see, the call CreateSolidBrush uses up one GDI resource, and if you don't hold onto the handle it returns in a variable, then it is impossible to delete the object.  Creating too many GDI handles without deleting them will slow down, and eventually halt, all graphics operations.  That's what was causing the leak before.

Changing it to a PatBlt still caused problems, but only with the redraw?  Does the inner loop now look like

        r = static_cast<BYTE>(r1 + (i * (r2-r1) / rect.bottom));
        g = static_cast<BYTE>(g1 + (i * (g2-g1) / rect.bottom));
        b = static_cast<BYTE>(b1 + (i * (b2-b1) / rect.bottom));

        row.top = i;
        row.bottom = i+1;
        /*
        HBRUSH hBrush = CreateSolidBrush(RGB(r, g, b));
        FillRect(reinterpret_cast<HDC>(wParam), &row, hBrush);
        DeleteObject(hBrush);
        */
        PatBlt(reinterpret_cast<HDC>(wParam), row.left, row.top, row.right - row.left, 1, WHITENESS);

because I'm not getting any drawing problems this way either.
Yep I deleted the brush as you suggested.  To be honest my initial code style is slap it together and if it works then clean it up.  I should probably get in the habit of cleaning as I go.  Here is my unabridged code with the PatBlt and the FillRect commented out:

<code>
case WM_ERASEBKGND:
{
    RECT rect, row;
    BYTE r, g, b, r1, g1, b1, r2, g2, b2;
    GetClientRect(hDlg, &rect);
    row.left = 0;
    row.right = rect.right;
   
    //--- extract clr elements ---//
    r1 = static_cast<BYTE>((Instance()->clrTop & 255 << 16) >> 16);
    g1 = static_cast<BYTE>((Instance()->clrTop & 255 << 8)  >> 8);
    b1 = static_cast<BYTE>(Instance()->clrTop & 255);
    r2 = static_cast<BYTE>((Instance()->clrBtm & 255 << 16) >> 16);
    g2 = static_cast<BYTE>((Instance()->clrBtm & 255 << 8)  >> 8);
    b2 = static_cast<BYTE>(Instance()->clrBtm & 255);

    //--- loop through 1 pixel high rows ---//
    for(int i = 0; i < rect.right; ++i)
    {
        r = static_cast<BYTE>(r1 + (i * (r2-r1) / rect.right));
        g = static_cast<BYTE>(g1 + (i * (g2-g1) / rect.right));
        b = static_cast<BYTE>(b1 + (i * (b2-b1) / rect.right));

        row.top = 0;
        row.bottom = rect.bottom;
        row.left = i;
        row.right = i+1;

        //FillRect Method
        //HBRUSH hBrush = CreateSolidBrush(RGB(r, g, b));
        //FillRect(GetDC(Instance()->hWnd), &row, hBrush);
        //DeleteObject(hBrush);
       
        HBRUSH hBrush = CreateSolidBrush(RGB(r, g, b));
        SelectObject(Instance()->hDC, hBrush);
        PatBlt(Instance()->hDC, i, 0, 1, rect.bottom, PATCOPY);
        DeleteObject(hBrush);
    }

    return TRUE;
}
</code>

I can't figure out what is going, I am running some pretty processor and gpu intensive stuff in this app and then I change the background render and kaput!  I should learn from my mistakes, don't try to be too smart.

Cheers
-A-
ASKER CERTIFIED SOLUTION
Avatar of KurtVon
KurtVon

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Kurt you are a champion, that worked a treat.  Thanks for your continued help with this problem, I guess it is pretty clear I am a GDI newbie.  While on the topic I have another question about GDI if you fancy the points:  https://www.experts-exchange.com/questions/21158212/BitBlt-Flickering.html

Thanks again mate.
Adam