2

Case in point:

void CMainWindow::OnPaint ()
{
    CRect rect;
    GetClientRect (&rect);

    CPaintDC dc (this);
    dc.SetViewportOrg (rect.Width () / 2, rect.Height () / 2);
    dc.SetBkMode (TRANSPARENT);

    for (int i=0; i<3600; i+=150) {
        LOGFONT lf;
        ::ZeroMemory (&lf, sizeof (lf));
        lf.lfHeight = 160;
        lf.lfWeight = FW_BOLD;
        lf.lfEscapement = i;
        lf.lfOrientation = i;
        ::lstrcpy (lf.lfFaceName, _T ("Arial"));

        CFont font;
        font.CreatePointFontIndirect (&lf);

        CFont* pOldFont = dc.SelectObject (&font);
        dc.TextOut (0, 0, CString (_T ("          Hello, MFC")));

        //WHY THIS LINE?
        dc.SelectObject (pOldFont);
    }
}

The code prints " Hello, MFC" in a circle around the origin (which is moved to the center of the window).

Output

Why is that CFont pointer created and then the dc selects it as the font? Is that just good programming practice or does this app actually need it?

I've seen similar code on the web doing this with Bitmaps and other device context objects. What's the purpose?

When I remove that last line of the code, nothing changes. Thanks in advance for the help.

Moe45673
  • 854
  • 10
  • 20

1 Answers1

6

Device Context:

A device context is a structure that defines a set of graphic objects and their associated attributes, as well as the graphic modes that affect output. The graphic objects include a pen for line drawing, a brush for painting and filling, a bitmap for copying or scrolling parts of the screen, a palette for defining the set of available colors, a region for clipping and other operations, and a path for painting and drawing operations.

At any time, there is exactly one graphic object selected into a device context. Since the system stores a set of default objects into a device context when it is created, an application must retain that state, when the device context is handed back to the system for cleanup. That is what

dc.SelectObject (pOldFont);

is responsible for.

This requirement is documented under SelectObject:

This function returns the previously selected object of the specified type. An application should always replace a new object with the original, default object after it has finished drawing with the new object.


Note: This is not related to MFC, but rather the Windows GDI. MFC merely implements an automatic resource management wrapper. State management still requires explicit code.
IInspectable
  • 46,945
  • 8
  • 85
  • 181
  • 1
    I am confused by your explanation. MFC has automatic resource management. Do you mean `dc.SelectObject (pOldFont)` is still necessary? – Barmak Shemirani Jul 05 '15 at 20:02
  • @Barmak: The line of code you are quoting performs **state** management. This is necessary with MFC as well. I was referring to **resource** management: You don't have to call `DeleteObject` on the `font` object; this is done for you in the d'tor. – IInspectable Jul 06 '15 at 08:47
  • I am in big trouble if that's the case. I thought newer versions of MFC don't need this. I don't Restore `oldfont`. I use `GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS)` to monitor GDI handles, it still shows zero leaks. Taskmanager's "GDI objects" looks the same with or without restoring `oldfont` (Windows 8 testing). Earlier, I had looked through `CDC::SelectObject` and `CDC` and I concluded MFC is automatically restoring `oldfont`. But now I looked at it again, I am not sure what it's doing. Anyway, I'll use `SaveDC` and `RestoreDC` in the mean time. – Barmak Shemirani Jul 06 '15 at 10:15
  • @Barmak: Again, you are confusing **state** with **resource** management. While MFC provides the infrastructure to manage resources (hence you are not seeing any resource leaks), you are still responsible for managing state yourself. Failing to restore a DC's state before handing it back leaves a reference to a now invalid resource selected into the DC, and enclosing functions could try to access it (e.g. for cleanup). The failure mode is similar to that of a dangling pointer: As long as you do not try to access it, the bug will go by unnoticed. – IInspectable Jul 06 '15 at 11:15
  • Thanks very much! What I didn't understand was that SelectObject() is returning a pointer to the object that is being replaced. I assumed it was returning a pointer to the currently selected object (ie the object passed as a parameter), like most C++ methods. Now it all makes sense. – Moe45673 Jul 07 '15 at 18:44