?
Solved

Problems with WM_ERASEBKGND in DialogBox

Posted on 2004-10-06
10
Medium Priority
?
1,465 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 5
  • 5
10 Comments
 
LVL 11

Expert Comment

by:KurtVon
ID: 12238074
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
ID: 12239293
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
ID: 12240077
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
NEW Veeam Agent for Microsoft Windows

Backup and recover physical and cloud-based servers and workstations, as well as endpoint devices that belong to remote users. Avoid downtime and data loss quickly and easily for Windows-based physical or public cloud-based workloads!

 
LVL 11

Expert Comment

by:KurtVon
ID: 12241725
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
ID: 12245845
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
 

Author Comment

by:skirmish76
ID: 12246460
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
ID: 12248539
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
ID: 12248738
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 1100 total points
ID: 12248873
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
ID: 12249049
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: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article describes how to add a user-defined command button to the Windows 7 Explorer toolbar.  In the previous article (http://www.experts-exchange.com/A_2172.html), we saw how to put the Delete button back there where it belongs.  "Delete" is …
What my article will show is if you ever had to do processing to a listbox without being able to just select all the items in it. My software Visual Studio 2008 crystal report v11 My issue was I wanted to add crystal report to a form and show…
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…
This is my first video review of Microsoft Bookings, I will be doing a part two with a bit more information, but wanted to get this out to you folks.

777 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