简短的回答:
为什么我需要pOldPen
?
因为你欠你的来电者。这不是作为礼物的意思。
为什么需要我用pDC->SelectObject(pOldPen)
selectpOldPen
到底?
因为选择另一个资源进入 DC 是选择当前资源退出 DC 的唯一方法。
当通过调用将 GDI 资源选择到 DC 时,设备上下文会代表您使用 GDI 资源SelectObject
。在任何时候,DC 中都只有一个图形对象(钢笔、画笔、位图……)被选中。但是,它不会为您管理资源。资源管理留给应用程序。
不幸的是,上面的 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 中选择某些东西才能将资源拉回,因此实际上很少有选择:此时您既不需要也不需要销毁的唯一图形对象是oldPen
. 这也确保您可以嵌套渲染代码,如下面的伪代码所示:
HPEN bestPenEver = CreatePen();
HPEN oldPen = SelectObject(bestPenEver);
// Do some painting with bestPenEver
call DrawStroke()
// Do some more painting with bestPenEver
SelectObject(oldPen);
DeleteObject(bestPenEver);
为完整起见,并将此实现与 MFC 代码对齐,这里是相关的 MFC 实现:
CPen::CreatePen(int nPenStyle, int nWidth, COLORREF crColor)
{ return Attach(::CreatePen(nPenStyle, nWidth, crColor)); }
CPen penStroke
是一个自动变量,所以它的析构函数在控制离开封闭块时运行(即DrawStroke
返回时)。析构函数调用它的基类实现CGdiObject::DeleteObject
,如下所示:
BOOL CGdiObject::DeleteObject()
{
if (m_hObject == NULL)
return FALSE;
return ::DeleteObject(Detach());
}
错误修正 * 不再访问空序列的第一个元素。
错误修正 ** 第一个元素已用于MoveTo
.