Re: something about clipping in OnCustomdraw
On Oct 11, 11:58 pm, Joseph M. Newcomer <newco...@flounder.com> wrote:
The Detach is not required. There is no call on a destructor when a CD=
C* leaves scope.
joe
Welcome back from your trip, Joe; you were missed around here.
I became curious about your comment, which is correct as far as it
goes, i.e., it's certainly true that when a pointer to an object goes
out of scope, there is no call to the destructor of the pointed-to
object.
But I asked myself if your comment goes far enough. After all, if
there's a pointer to an object, then there's also an underlying
object. And the object must be destroyed at some point in time, such
that the destructor will also be called. Why, I asked myself,
wouldn't that also call ::DeleteDC() on the underlying handle to the
device context. After all, everyone knows that the CDC destructor
looks like this:
CDC::~CDC()
{
if (m_hDC != NULL)
::DeleteDC(Detach());
}
Incidentally, I think that most here recognize that the purpose of my
suggestion to call CDC::Detach() was to separate the HDC from the CDC
object, since Detach() would set m_hDC to NULL and ensure that the
underlying HDC would not be DeleteDC'd. "Bad things" (tm) probably
happen if the HDC that your control is using is suddenly deleted out
from under it.
All this led me to the wonderful (and arcane) world of temporary
objects and temporary vs. permanent maps. Through the documentation,
I learned that CWinApp::OnIdle will _supposedly_ cause a call,
eventually, to CDC::DeleteTempMap. From the documentation for
CDC::DeleteTempMap, I confirmed that the function is _supposedly_
called automatically by the CWinApp idle-time handler. I also learned
that DeleteTempMap indeed deletes any temporary CDC objects created
by ::FromHandle, which would of course invoke the CDC destructor.
Importantly, however, I learned that CDC::DeleteTempMap does _not_
destroy the device context handles (hDCs) temporarily associated with
the CDC objects.
In fact, though, when I traced through the calls _actually_ initiated
by CWinApp::OnIdle, I learned that there is actually never a call to
CDC::DeleteTempMap. So much for the documentation.
Instead, what is called is a function named CHandleMap::DeleteTemp().
This function does what is advertised for the CDC::DeleteTempMap
function, i.e., it does _not_ destroy the device context handles
(hDCs) temporarily associated with the CDC objects. However, it does
so through some of the gnarliest code I've seen, involving direct
manipulation of memory locations where member objects are _expected_
to appear. Here's the code (MFC ver 4.2, it might be different now):
void CHandleMap::DeleteTemp()
{
if (this == NULL)
return;
POSITION pos = m_temporaryMap.GetStartPosition();
while (pos != NULL)
{
HANDLE h; // just used for asserts
CObject* pTemp;
m_temporaryMap.GetNextAssoc(pos, (LPVOID&)h, (void*&)pTemp);
// zero out the handles
ASSERT(m_nHandles == 1 || m_nHandles == 2);
HANDLE* ph = (HANDLE*)((BYTE*)pTemp + m_nOffset); // after
CObject
ASSERT(ph[0] == h || ph[0] == NULL);
ph[0] = NULL;
if (m_nHandles == 2)
{
ASSERT(ph[1] == h || ph[1] == NULL);
ph[1] = NULL;
}
delete pTemp; // virtual destructor does the right thing
}
m_temporaryMap.RemoveAll(); // free up dictionary links etc
}
Yikes!! But it apparently works.
So, the end of the story is a common one: Joe is correct. The call to
CDC::Detach() is not needed. Now I know why much more thoroughly.
Thank you for the opportunity to explore some of the more arcane
features of MFC.