[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1483
  • Last Modified:

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>
0
skirmish76
Asked:
skirmish76
  • 5
  • 5
1 Solution
 
KurtVonCommented:
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.
0
 
skirmish76Author Commented:
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-
0
 
KurtVonCommented:
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.
0
NFR key for Veeam Backup for Microsoft Office 365

Veeam is happy to provide a free NFR license (for 1 year, up to 10 users). This license allows for the non‑production use of Veeam Backup for Microsoft Office 365 in your home lab without any feature limitations.

 
KurtVonCommented:
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.
0
 
skirmish76Author Commented:
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.
0
 
skirmish76Author Commented:
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).
0
 
KurtVonCommented:
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.
0
 
skirmish76Author Commented:
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-
0
 
KurtVonCommented:
Ah, I see the problem with the PatBlt code.  When you select an object into a DC, you must select the old object back in before delting it.  So change that section to

        HBRUSH hBrush = CreateSolidBrush(RGB(r, g, b));
        HGDIOBJ hOldBrush = SelectObject(Instance()->hDC, hBrush);
        PatBlt(Instance()->hDC, i, 0, 1, rect.bottom, PATCOPY);
        SelectObject(Instance()->hDC, hOldBrush);
        DeleteObject(hBrush);

I'm also not clear why the Instance()->hDC is used.  You must never hold on to a DC across messages for any reason -- Windows recycles them for use with other controls so they have a good chance of not being valid on the next message.  WM_ERASEBKGND always passes the current HDC as the wParam.  You can save soome headaches by assigning it to a local variable within the call, for convenience, but in the call, only draw to that DC.
0
 
skirmish76Author Commented:
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:  http://www.experts-exchange.com/Programming/Programming_Platforms/Win_Prog/Q_21158212.html

Thanks again mate.
Adam
0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 5
  • 5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now