c++winapiwm-paint

Please confirm the best way to Restore the DC when a custom Pen and a custom Brush are selected


Can someone confirm if this sample code from Microsoft fails to restore the custom brush set with SetDCBrushColor?


    case WM_PAINT:        
        {    
            hdc = BeginPaint(hWnd, &ps);
        //    Initializing original object
            HGDIOBJ original = NULL;
        //    Saving the original object
            original = SelectObject(hdc,GetStockObject(DC_PEN));

        //    ...

            SelectObject(hdc, GetStockObject(BLACK_PEN));
            Rectangle(hdc,0,0,200,200);

            SelectObject(hdc, GetStockObject(DC_PEN));
            SelectObject(hdc, GetStockObject(DC_BRUSH));
            SetDCBrushColor(hdc, RGB(255,0,0));
            SetDCPenColor(hdc, RGB(0,0,255));
            Rectangle(hdc,100,300,200,400);
            SetDCBrushColor(hdc, RGB(0,255,0));
            Rectangle(hdc,300,150,500,300);

        //   Restoring the original object
            SelectObject(hdc,original);
        }
        
        break;
//...

Paul Watt, and other posters, suggest we use SelectObject to restore changes to the pen and brush separately, or that we use SaveDC and RestoreDC.

https://www.codeproject.com/Articles/224754/Guide-to-Win32-Memory-DC

// Setup paint for first layer.
HGDIOBJ hOldBrush = ::SelectObject(hDC, hBrush);
HGDIOBJ hOldPen   = ::SelectObject(hDC, hPen);
HGDIOBJ hOldFont  = ::SelectObject(hDC, hFont);
HGDIOBJ hOldMan   = ::SelectObject(hDC, hBmp);

// ... Paint a motley display

::SelectObject(hDC, hOldBrush);
::SelectObject(hDC, hOldPen);
::SelectObject(hDC, hOldFont);
::SelectObject(hDC, hOldMan);

Or use SaveDC and RestoreDC to make it easy:

// Take a snap-shot of the current state of the DC
//https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-savedc
  int SaveDC(
    __in HDC hdc,           // Handle to the DC
);

// Restore the DC to a previously saved state
int RestoreDC(
    __in HDC hdc,           // Handle to the DC
    __in int nSavedDC       // Saved state claim ticket
);

I realize that I need to use DeleteObject on any brushes/pens I create that are not stock objects.

Did Microsoft forget to use SelectObject with a previously saved version of the original brush? (Or should Microsoft have used SaveDC and RestoreDC?)


Solution

  • Did Microsoft forget to use SelectObject with a previously saved version of the original brush?

    I would say: Yes, they did!

    In the case of the SetDCPenColor call, there isn't a problem, as restoring the original (saved) pen will revert the changes made. It seems that the example forgets to save (and subsequently restore) the original brush; if this is a custom brush, then simply restoring the brush colour with SetDCBrushColor would (IMHO) not be sufficient restoration.

    But the issue doesn't end there! The linked example has also 'forgotten' to call EndPaint! From here:

    Each call to BeginPaint must have a corresponding call to the EndPaint function.


    Or should Microsoft have used SaveDC and RestoreDC?

    For both convenience and code safety, I would say this is the best policy: saving and restoring the entire DC state(s) avoids any issues that may later arise if/when additional changes to the DC are made in future code revisions. Bracketing your code in SaveDC() and RestoreDC() calls also has the advantage of restoring font colours, text modes, mapping modes, et cetera.

    However, for code that may possibly be executed extremely frequently, saving and restoring only the components you actually change may show performance improvements (especially on older or slower processors).