Saving the DC context

The Problem

Here's a classic case:

void CMyView::OnPaint()
   {
    CPaintDC dc(this);
    CFont f;
    f.CreateFont(...); // parameters not shown
    dc.SelectObject(&f);
    dc.TextOut(...); // whatever...
   } // destructors called here...

Looks pretty good, right? Wrong. Look at what happens. The destructors are called when the context exits. This means that the DC will be freed (in the case of a CPaintDC this means that ::EndPaint will be called), and the destructor for the CFont will be called, which means that a ::DeleteObject will be called. (This has other implications, for example, if you are doing a CWnd::SetFont call, where the font has to have a lifetime beyond the lifetime of the variable; see my essay on this topic).

Strictly speaking, there is no specified order in which the destructors are known to be called. I checked the C++ standard, and although all sorts of issues are specified in order of execution of destructors, the order in which they are called for auto variables (that is, ordinary stack variables) seems to be unspecified. In practice, it appears to be in the inverse order of their declaration, that is, the font, which is declared after the DC, will be destroyed first, then the DC will be destroyed. This loses, because when the font is destroyed it is still selected into the font. (You may suspect that by declaring your fonts before the CPaintDC will solve this. I would consider this an egregious programming blunder. For one thing, I'm not sure that deleting the DC first properly sets the values in the font so that it knows it is no longer in a DC (it could be selected into several DCs). I would never attempt this.

The proper thing to do is to restore the state of the DC before destroying it. Typically, this is done by saving the contents of the DC when you do a SelectObject and then restoring them, for example,

void CMyView::OnPaint()
   {
    CPaintDC dc(this);
    CFont f;
    f.CreateFont(...); // parameters not shown
    CFont * oldfont = dc.SelectObject(&f);
    dc.TextOut(...); // whatever...
    dc.SelectObject(oldfont);
   } // destructors called here...

This will now work properly. When the destructor for the font is called, it is no longer in a DC, and it will be deleted.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“C++ : Where friends have access to your private members.” - Gavin Russell Baker