CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 14 of 14
  1. #1
    Join Date
    Feb 2015
    Posts
    66

    Does BeginPaint() erase the background?

    Programming Windows (Fith Edition) book says the following:

    During the BeginPaint call, Windows erases the background of the client area if it hasn't been erased already. It erases the background using the brush specified in the hbrBackground field of the WNDCLASS structure used to register the window class.
    Does that mean that BeginPaint() is the one erasing the background, or does it just mean that BeginPaint() causes a WM_ERASEBKGND message to be sent?

  2. #2
    Join Date
    Nov 2000
    Location
    Voronezh, Russia
    Posts
    6,620

    Re: Does BeginPaint() erase the background?

    It does mean that WM_ERASEBKGND is sent independent prior to WM_PAINT, and only when it was handled properly (the handler returned non-zero) BeginPaint does not perform the erasure with the default window brush. Otherwise it does.
    Best regards,
    Igor

  3. #3
    Join Date
    Feb 2015
    Posts
    66

    Re: Does BeginPaint() erase the background?

    Quote Originally Posted by Igor Vartanov View Post
    It does mean that WM_ERASEBKGND is sent independent prior to WM_PAINT, and only when it was handled properly (the handler returned non-zero) BeginPaint does not perform the erasure with the default window brush. Otherwise it does.
    So when I does something like resizing my Window, a WM_ERASEBKGND message is sent followed by a WM_PAINT message.
    If the WM_ERASEBKGND handler does not erase the background, it should return 0. Now when WM_PAINT is processed, and BeginPaint() is called, it checks if "the update region is marked for erasing", and I suppose it will be marked for erasing because the WM_ERASEBKGND handler returned 0, so now BeginPaint() erases the background.

    Is this correct?

  4. #4
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: Does BeginPaint() erase the background?

    not in a position to actually try this atm, but knowing petzold, he's probably spot on.


    1) WM_PAINT isn't usually "sent" or "posted" to a window, but is rather a flag in the message queue that tells the queue manager that the window has invalidated areas and should return a WM_PAINT when there's no other more important messages to be handled.

    2) I do believe it is indeed during the handling of WM_PAINT that the BeginPaint actually causes a WM_ERASEBKGND to be SENT (and thus interrupting the WM_PAINT handler) if the window has background enabled.

    It is normally WM_ERASEBKGND that will do the actual erasing. You can of course override the handler, do nothing and decide to do erasing in the actual WM_PAINT handler, there's pros and cons to that approach.
    Last edited by OReubens; August 21st, 2015 at 08:56 AM.

  5. #5
    Join Date
    Nov 2000
    Location
    Voronezh, Russia
    Posts
    6,620

    Re: Does BeginPaint() erase the background?

    This is Spy++ log caught on Notepad window resize:
    Code:
    <00270> 00130656 R WM_WINDOWPOSCHANGED
    <00271> 00130656 S WM_IME_NOTIFY dwCommand:IMN_SETCOMPOSITIONWINDOW dwCommand:0000000B dwData:00000000
    <00272> 00130656 R WM_IME_NOTIFY
    <00273> 00130656 S WM_IME_NOTIFY dwCommand:IMN_SETCOMPOSITIONWINDOW dwCommand:0000000B dwData:00000000
    <00274> 00130656 R WM_IME_NOTIFY
    <00275> 00130656 S WM_PAINT hdc:00000000
    <00276> 00130656 S WM_NCPAINT hrgn:00000001
    <00277> 00130656 R WM_NCPAINT
    <00278> 00130656 S WM_ERASEBKGND hdc:FFFFFFFFCD011F72
    <00279> 00130656 R WM_ERASEBKGND fErased:True
    <00280> 00130656 R WM_PAINT
    <00281> 00130656 S EM_GETSEL lpdwStart:0015ECB0 lpdwEnd:0015ECB4
    <00282> 00130656 R EM_GETSEL wStart:0 wEnd:0 lpdwStart:0015ECB0 (0) lpdwEnd:0015ECB4 (0)
    <00283> 00130656 S EM_LINEFROMCHAR ich:0
    <00284> 00130656 R EM_LINEFROMCHAR iLine:0
    <00285> 00130656 S EM_LINEINDEX line:0
    <00286> 00130656 R EM_LINEINDEX ich:0
    <00287> 00130656 S EM_GETSEL lpdwStart:0015ECB0 lpdwEnd:0015ECB4
    <00288> 00130656 R EM_GETSEL wStart:0 wEnd:0 lpdwStart:0015ECB0 (0) lpdwEnd:0015ECB4 (0)
    <00289> 00130656 S EM_LINEFROMCHAR ich:0
    <00290> 00130656 R EM_LINEFROMCHAR iLine:0
    Best regards,
    Igor

  6. #6
    Join Date
    Nov 2000
    Location
    Voronezh, Russia
    Posts
    6,620

    Re: Does BeginPaint() erase the background?

    Quote Originally Posted by john_n View Post
    So when I does something like resizing my Window, a WM_ERASEBKGND message is sent followed by a WM_PAINT message.
    If the WM_ERASEBKGND handler does not erase the background, it should return 0. Now when WM_PAINT is processed, and BeginPaint() is called, it checks if "the update region is marked for erasing", and I suppose it will be marked for erasing because the WM_ERASEBKGND handler returned 0, so now BeginPaint() erases the background.

    Is this correct?
    Yep.
    Best regards,
    Igor

  7. #7
    Join Date
    Feb 2015
    Posts
    66

    Re: Does BeginPaint() erase the background?

    Quote Originally Posted by Igor Vartanov View Post
    Yep.
    I have done the following test:

    Code:
    case WM_PAINT:
    	{
    		PAINTSTRUCT ps;
    		HDC hdc = BeginPaint(hWnd, &ps);
    
    		EndPaint(hWnd, &ps);
    	}
    	break;
    case WM_ERASEBKGND:
    	{
    		return 0;
    	}
    	break;
    The WM_ERASEBKGND handler returns 0, so I thought that BeginPaint() will now erase the background, but this is not happening!

  8. #8
    Join Date
    Feb 2015
    Posts
    66

    Re: Does BeginPaint() erase the background?

    (Ignore my previous post).

    This is what I think happens:
    When I does something like resizing my Window, a WM_ERASEBKGND message is sent followed by a WM_PAINT message.
    It does not matter here what the WM_ERASEBKGND handler returns, and when the WM_PAINT message is processed, there is no way for it to know what WM_ERASEBKGND handler returned anyway!

    Now this is when BeginPaint() comes into play:
    If I call InvalidateRect() with the bErase parameter set to TRUE, for example: InvalidateRect(hWnd, NULL, TRUE); This is what happens:
    A WM_PAINT message is sent, and inside the WM_PAINT handler BeginPaint() is called, now BeginPaint() will know that it should send a WM_ERASEBKGND message (because I called InvalidateRect() with the bErase parameter set to TRUE).
    Now if the WM_ERASEBKGND handler returned 0, this means I did not erase the background, and BeginPaint() will set PAINTSTRUCT.fErase to TRUE indicating that the background must be erased inside the WM_PAINT handler.
    If the WM_ERASEBKGND handler returned 1, this means I erased the background, and BeginPaint() will set PAINTSTRUCT.fErase to FALSE.

    So BeginPaint() does not actually erase the background.

  9. #9
    Join Date
    Nov 2000
    Location
    Voronezh, Russia
    Posts
    6,620

    Re: Does BeginPaint() erase the background?

    It looks like I always was misguided by the Spy++ log, as BeginPaint documentation says the function really sends WM_ERASEBKGND, and you and OReubens are right in this matter. However, the same documentation says the function really uses default window brush to update the region before quitting. You need to test more careful, I believe.
    Best regards,
    Igor

  10. #10
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: Does BeginPaint() erase the background?

    WM_PAINT does not, EVER erase the background on it's own.

    neither does WM_ERASEBKGND for that matter. it's the default implementation of WM_ERASEBKGND that does. if you override and return 0, then this effectively means "I will do the background from WM_PAINT" or "I don't need the background erased because my WM_paint will paint every pixel of the invalid region 'somehow'".

  11. #11
    Join Date
    Feb 2015
    Posts
    66

    Re: Does BeginPaint() erase the background?

    Quote Originally Posted by Igor Vartanov View Post
    However, the same documentation says the function really uses default window brush to update the region before quitting. You need to test more careful, I believe.
    This test shows that BeginPaint() does not draw anything:

    Code:
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	switch(message)
    	{
    	case WM_LBUTTONDOWN:
    		InvalidateRect(hWnd, NULL, TRUE);
    		break;
    	case WM_ERASEBKGND:
    		return 1;
    		break;
    	case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
    
            EndPaint(hWnd, &ps);
        }
        break;
    	case WM_CLOSE:
    		DestroyWindow(hWnd);
    		break;
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		break;
    	default:
    		return DefWindowProc(hWnd, message, wParam, lParam);
    	}
    	return 0;
    }
    I returned 1 inside the WM_ERASEBKGND handler, so if BeginPaint() erases the background, it should not do it, but it did not.

  12. #12
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: Does BeginPaint() erase the background?

    like I said, neither WM_ERASEBKGND nor WM_PAINT actually do paint anything at all unless:

    * you're using the default handler for WM_ERASEBKGND (or calling DefWindowProc in that case), which rect-fills with the current brush (potentially changed from the default by a prior message, though granted this is an oddball case, it does happen sometimes in WM_CTLCOLOR).
    * you have a WM_ERASEBKGND handler , in which case that code paints (if at all)
    * you have a WM_PAINT handler, in which case it's that code that paints (if at all).

  13. #13
    Join Date
    Feb 2015
    Posts
    66

    Re: Does BeginPaint() erase the background?

    I said in my latest post:
    I returned 1 inside the WM_ERASEBKGND handler, so if BeginPaint() erases the background, it should not do it, but it did not.
    What I meant to say is: I returned 1 inside the WM_ERASEBKGND handler, so if BeginPaint() erases the background, it should do it, but it did not.

    Quote Originally Posted by OReubens View Post
    like I said, neither WM_ERASEBKGND nor WM_PAINT actually do paint anything at all unless:

    * you're using the default handler for WM_ERASEBKGND
    Of course WM_ERASEBKGND by itself does not paint anything and it's the default handler for WM_ERASEBKGND that has the code to do the painting.
    I mean how can a message by itself do anything at all, it's the message handler that does the action!

  14. #14
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: Does BeginPaint() erase the background?

    Quote Originally Posted by john_n View Post
    I said in my latest post:

    What I meant to say is: I returned 1 inside the WM_ERASEBKGND handler, so if BeginPaint() erases the background, it should do it, but it did not.
    And I corrected you. Sayign it doesn't, and that's exactly how it's supposed to be.

    BeginPaint() does not EVER under any surcumstances erase or paint anything.

    BeginPaint causes an WM_ERASEBKGND to be sent in which case that handler is supposed to do the background erasing explicitely or don't and return 1. (effectively lying that you did but didn't, to prevent DefaultWindowProc() from doing it explicitely).

    And when you do that, you are supposed to do any background erasing (if needed) explicitely in the rest of your WM_PAINT handler.
    failure to do so will lead to parts of the window being "unpainted" which can have a number of weird/funny/unexpected apparent effects.


    the typical reason to return 1 in WM_ERASEBKGND is because it's unnecessary because the actual work in WM_PAINT will paint every pixel in the invalidated region anyway, so the erasing is just wasted time.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured