我有一些关于在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;
}
我的问题是:
pDC->SelectObject(pOldPen)
来选择pOldPen? 答案 0 :(得分:1)
简短回答:
为什么我需要pOldPen
?
因为你欠你的来电者。这不是礼物。
为什么我最后需要使用pDC->SelectObject(pOldPen)
来选择pOldPen
?
因为在DC中选择另一个资源是从DC中选择当前资源的唯一方法。
设备上下文在您通过调用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());
}
。