渲染到设备上下文(CDC / HDC)时的资源管理

时间:2013-07-31 13:30:47

标签: winapi mfc gdi

我有一些关于在MFC中画中风的问题。假设已经事先声明了类CStroke。示例代码如下所示。

BOOL CStroke::DrawStroke(CDC* pDC)
{
    CPen penStroke;
    if ( !penStroke.CreatePen(PS_SOLID, m_nPenWidth, m_color) )
        return FALSE;
    CPen *pOldPen = pDC->SelectObject(&penStroke);
    pDC->MoveTo(m_pointArray[0]);

    for( int i = 0; i < m_pointArray.GetSize(); i++ )
    {
        pDC->LineTo(m_pointArray[i]);
    }

    pDC->SelectObject(pOldPen);
    return TRUE;
}

我的问题是:

  1. 为什么我需要pOldPen?
  2. 为什么我需要使用pDC->SelectObject(pOldPen)来选择pOldPen?

1 个答案:

答案 0 :(得分:1)

简短回答:

  1. 为什么我需要pOldPen

    因为你欠你的来电者。这不是礼物。

  2. 为什么我最后需要使用pDC->SelectObject(pOldPen)来选择pOldPen

    因为在DC中选择另一个资源是从DC中选择当前资源的唯一方法。


  3. 设备上下文在您通过调用SelectObject将其选择为DC时,代表您使用GDI资源。在任何时候,DC只有一种graphics object的每种类型(笔,画笔,位图,......)被选入其中。但是,它不会为您管理资源。资源管理留给应用程序。

    不幸的是,上面的MFC实现隐藏了一个关于资源管理的重要细节。将代码转换为简单的WinAPI实现将使CPen隐藏的内容更加明显:

    BOOL CStroke::DrawStroke(HDC hDC)
    {
        // Create a new pen resource
        HPEN penStroke = CreatePen(PS_SOLID, m_nPenWidth, m_color);
        if ( penStroke == NULL )
            return FALSE;
    
        // Select it into the device context
        HPEN oldPen = static_cast<HPEN>( SelectObject(hDC, &penStroke) );
    
        // Render strokes
        if ( m_pointArray.GetSize() > 0 )  // bugfix *
        {
            pDC->MoveTo(m_pointArray[0]);
    
            for( int i = 1; i < m_pointArray.GetSize(); i++ )  // bugfix **
            {
                pDC->LineTo(m_pointArray[i]);
            }
        }
    
        // Select pen out of DC
        HPEN penCreatedAbove = static_cast<HPEN>( SelectObject(hDC, oldPen) );
    
        // Clean up our resource (this is what CPen::~CPen() hides)
        DeleteObject(penCreatedAbove)
    
        return TRUE;
    }
    

    由于上面的代码创建了一个图形对象(参见CreatePen),它也负责释放与之相关的资源。要释放资源,应用程序会调用DeleteObject。此API调用具有以下要求:

      

    请勿在绘图对象(笔或画笔)静止时删除它   被选入DC。

    为了满足该前提条件,需要从DC中选择在上面的代码中创建的笔。这就是对SelectObject(hDC, oldPen)的调用。 (我通过引入一个存储返回值的变量使这一点变得更加明显。)

    由于您必须在DC中选择某些以将资源撤回,所以实际上只有很少的选项:此时您既不拥有也不必销毁的唯一图形对象是{ {1}}。这还可以确保您可以嵌套呈现代码,如以下伪代码所示:

    oldPen

    为了完整性并将此实现与MFC代码对齐,这里是相关的MFC实现:

    HPEN bestPenEver = CreatePen();
    HPEN oldPen = SelectObject(bestPenEver);
    
    // Do some painting with bestPenEver
    
        call DrawStroke()
    
    // Do some more painting with bestPenEver
    SelectObject(oldPen);
    DeleteObject(bestPenEver);
    

    CPen::CreatePen(int nPenStyle, int nWidth, COLORREF crColor) { return Attach(::CreatePen(nPenStyle, nWidth, crColor)); } 是一个自动变量,因此当控制离开封闭块时(即CPen penStroke返回时),析构函数就会运行。析构函数调用它的基类实现DrawStroke,如下所示:

    CGdiObject::DeleteObject

    错误修正*不再访问空序列的第一个元素。

    bugfix **第一个元素已用于BOOL CGdiObject::DeleteObject() { if (m_hObject == NULL) return FALSE; return ::DeleteObject(Detach()); }