Search code examples
winapimfcgdi

Do I need to re-select the previous pen into the DC, just before releasing it?


I have the following dialog-box class:

class CInputDlg : public CDialog
{
public:
    CInputDlg(CWnd* pParent = NULL);
    virtual ~CInputDlg();

DECLARE_MESSAGE_MAP();

protected:
    afx_msg BOOL OnInitDialog();
    afx_msg void OnCancel();
    afx_msg void OnPaint();

private:
    CPen m_Pen;
};

Here is the implementation of the callback routines:

BOOL CInputDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    m_Pen.CreatePen(...);
    return TRUE;
}

void CInputDlg::OnCancel()
{
    m_Pen.DeleteObject();
    CDialog::OnCancel();
}

void CInputDlg::OnPaint()
{
    CPaintDC dc(this);
    CDC* pDC = GetDC();
    pDC->SelectObject(m_Pen);
    ...
    ReleaseDC(pDC);
}

As you can see, in the OnPaint routine, I do not re-select the previous pen into the DC.

I do this under the assumption that there is no need to, since I release the DC anyway.

Finally, in the OnCancel routine, I delete the pen (which is possibly selected in some DC).

Am I wrong in doing so, or is my assumption above correct?

MSDN isn't very clear about when I can or should delete objects.

I guess that this question is due to my poor understanding of DCs.

Thank you.


Solution

  • It is an error to release a device context without restoring it to its initial state. At any given time a device context stores a reference to an object of 7 graphics object types. Those objects are owned by the device context, and need to be cleaned up, when the device context is destroyed.

    SelectObject

    selects an object into the specified device context (DC). The new object replaces the previous object of the same type.

    Not restoring a device context therefore leaves it with a reference to an object it does not own. When it comes time to tear it down, bad things happen1.

    The rules are simple: Always restore a DC before passing it back to its owner (either the caller, or the system). If keeping tracking of each graphics object is too tedious or impracticable, you can use SaveDC on entry and RestoreDC on exit.

    Bonus reading:


    1 In theory anyway. Because there is so much bad code out there, the system has implemented a fair bit of resilience against buggy GDI resource management.