Solved

Problems with WM_ERASEBKGND in DialogBox

Posted on 2004-10-06
10
1,432 Views
Last Modified: 2013-12-03
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
Comment
Question by:skirmish76
  • 5
  • 5
10 Comments
 
LVL 11

Expert Comment

by:KurtVon
Comment Utility
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
 

Author Comment

by:skirmish76
Comment Utility
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
 
LVL 11

Expert Comment

by:KurtVon
Comment Utility
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
 
LVL 11

Expert Comment

by:KurtVon
Comment Utility
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
 

Author Comment

by:skirmish76
Comment Utility
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
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 

Author Comment

by:skirmish76
Comment Utility
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
 
LVL 11

Expert Comment

by:KurtVon
Comment Utility
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
 

Author Comment

by:skirmish76
Comment Utility
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
 
LVL 11

Accepted Solution

by:
KurtVon earned 275 total points
Comment Utility
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
 

Author Comment

by:skirmish76
Comment Utility
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

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

In this article, I will show how to use the Ribbon IDs Tool Window to assign the built-in Office icons to a ribbon button.  This tool will help us to find the OfficeImageId that corresponds to our desired built-in Office icon. The tool is part of…
For a while now I'v been searching for a circular progress control, much like the one you get when first starting your Silverlight application. I found a couple that were written in WPF and there were a few written in Silverlight, but all appeared o…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

6 Experts available now in Live!

Get 1:1 Help Now