// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"

#ifdef AFX_CORE1_SEG
#pragma code_seg(AFX_CORE1_SEG)
#endif

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define new DEBUG_NEW

/////////////////////////////////////////////////////////////////////////////
// Support for freeing the temp maps

void AFXAPI AfxLockTempMaps()
{
	AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
	++pState->m_nTempMapLock;
}

BOOL AFXAPI AfxUnlockTempMaps(BOOL bDeleteTemps)
{
	AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
	if (pState->m_nTempMapLock != 0 && --pState->m_nTempMapLock == 0)
	{
		if (bDeleteTemps)
		{
			if (bDeleteTemps != -1)
			{
				// allow COM libraries to be freed
				CWinThread* pThread = AfxGetThread();
				if (pThread != NULL && pThread->m_lpfnOleTermOrFreeLib != NULL)
					(*pThread->m_lpfnOleTermOrFreeLib)(FALSE, FALSE);
			}

			// clean up temp objects
			pState->m_pmapHGDIOBJ->DeleteTemp();
			pState->m_pmapHDC->DeleteTemp();
			pState->m_pmapHMENU->DeleteTemp();
			pState->m_pmapHWND->DeleteTemp();
			pState->m_pmapHIMAGELIST->DeleteTemp();
		}

#ifndef _AFX_PORTABLE
		CWinApp* pApp = AfxGetApp();
		_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
		// restore safety pool after temp objects destroyed
		if (pApp != NULL &&
			 (pThreadState->m_pSafetyPoolBuffer == NULL ||
			 _msize(pThreadState->m_pSafetyPoolBuffer) < pApp->m_nSafetyPoolSize) &&
			pApp->m_nSafetyPoolSize != 0)
		{
			// attempt to restore the safety pool to its max size
			size_t nOldSize = 0;
			if (pThreadState->m_pSafetyPoolBuffer != NULL)
			{
				nOldSize = _msize(pThreadState->m_pSafetyPoolBuffer);
				free(pThreadState->m_pSafetyPoolBuffer);
			}

			// undo handler trap for the following allocation
			BOOL bEnable = AfxEnableMemoryTracking(FALSE);
			pThreadState->m_pSafetyPoolBuffer = malloc(pApp->m_nSafetyPoolSize);
			if (pThreadState->m_pSafetyPoolBuffer == NULL)
			{
				TRACE1("Warning: failed to reclaim %d bytes for memory safety pool.\n",
					pApp->m_nSafetyPoolSize);
				// at least get the old buffer back
				if (nOldSize != 0)
				{
					//get it back
					pThreadState->m_pSafetyPoolBuffer = malloc(nOldSize);
					ASSERT(pThreadState->m_pSafetyPoolBuffer != NULL);
				}
			}
			AfxEnableMemoryTracking(bEnable);
		}
#endif  // !_AFX_PORTABLE
	}

	// return TRUE if temp maps still locked
	return pState->m_nTempMapLock != 0;
}

/////////////////////////////////////////////////////////////////////////////
// CHandleMap implementation

CHandleMap::CHandleMap(CRuntimeClass* pClass, size_t nOffset, int nHandles)
	: m_permanentMap(10), m_temporaryMap(4)
		// small block size for temporary map
{
	ASSERT(pClass != NULL);
	ASSERT(nHandles == 1 || nHandles == 2);

	m_temporaryMap.InitHashTable(7, FALSE); // small table for temporary map
	m_pClass = pClass;
	m_nOffset = nOffset;
	m_nHandles = nHandles;
}

CObject* CHandleMap::FromHandle(HANDLE h)
{
	ASSERT(m_pClass != NULL);
	ASSERT(m_nHandles == 1 || m_nHandles == 2);

	if (h == NULL)
		return NULL;

	CObject* pObject = LookupPermanent(h);
	if (pObject != NULL)
		return pObject;   // return permanent one
	else if ((pObject = LookupTemporary(h)) != NULL)
	{
		HANDLE* ph = (HANDLE*)((BYTE*)pObject + m_nOffset);
		ASSERT(ph[0] == h || ph[0] == NULL);
		ph[0] = h;
		if (m_nHandles == 2)
		{
			ASSERT(ph[1] == h || ph[1] == NULL);
			ph[1] = h;
		}
		return pObject;   // return current temporary one
	}

	// This handle wasn't created by us, so we must create a temporary
	// C++ object to wrap it.  We don't want the user to see this memory
	// allocation, so we turn tracing off.

	BOOL bEnable = AfxEnableMemoryTracking(FALSE);
#ifndef _AFX_PORTABLE
	_PNH pnhOldHandler = AfxSetNewHandler(&AfxCriticalNewHandler);
#endif

	CObject* pTemp = NULL;
	TRY
	{
		pTemp = m_pClass->CreateObject();
		if (pTemp == NULL)
			AfxThrowMemoryException();

		m_temporaryMap.SetAt((LPVOID)h, pTemp);
	}
	CATCH_ALL(e)
	{
#ifndef _AFX_PORTABLE
		AfxSetNewHandler(pnhOldHandler);
#endif
		AfxEnableMemoryTracking(bEnable);
		THROW_LAST();
	}
	END_CATCH_ALL

#ifndef _AFX_PORTABLE
	AfxSetNewHandler(pnhOldHandler);
#endif
	AfxEnableMemoryTracking(bEnable);

	// now set the handle in the object
	HANDLE* ph = (HANDLE*)((BYTE*)pTemp + m_nOffset);  // after CObject
	ph[0] = h;
	if (m_nHandles == 2)
		ph[1] = h;

	return pTemp;
}

#ifdef _DEBUG   // out-of-line version for memory tracking
void CHandleMap::SetPermanent(HANDLE h, CObject* permOb)
{
	BOOL bEnable = AfxEnableMemoryTracking(FALSE);
	m_permanentMap[(LPVOID)h] = permOb;
	AfxEnableMemoryTracking(bEnable);
}
#endif //_DEBUG

#ifdef _DEBUG
void CHandleMap::RemoveHandle(HANDLE h)
{
	// make sure the handle entry is consistent before deleting
	CObject* pTemp = LookupTemporary(h);
	if (pTemp != NULL)
	{
		// temporary objects must have correct handle values
		HANDLE* ph = (HANDLE*)((BYTE*)pTemp + m_nOffset);  // after CObject
		ASSERT(ph[0] == h || ph[0] == NULL);
		if (m_nHandles == 2)
			ASSERT(ph[1] == h);
	}
	pTemp = LookupPermanent(h);
	if (pTemp != NULL)
	{
		HANDLE* ph = (HANDLE*)((BYTE*)pTemp + m_nOffset);  // after CObject
		ASSERT(ph[0] == h);
		// permanent object may have secondary handles that are different
	}
	// remove only from permanent map -- temporary objects are removed
	//  at idle in CHandleMap::DeleteTemp, always!
	m_permanentMap.RemoveKey((LPVOID)h);
}
#endif

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
}

/////////////////////////////////////////////////////////////////////////////

void PASCAL CWnd::DeleteTempMap()
{
	CHandleMap* pMap = AfxGetModuleThreadState()->m_pmapHWND;
	pMap->DeleteTemp();
}

void PASCAL CImageList::DeleteTempMap()
{
	CHandleMap* pMap = AfxGetModuleThreadState()->m_pmapHIMAGELIST;
	pMap->DeleteTemp();
}

void PASCAL CDC::DeleteTempMap()
{
	CHandleMap* pMap = AfxGetModuleThreadState()->m_pmapHDC;
	pMap->DeleteTemp();
}

void PASCAL CGdiObject::DeleteTempMap()
{
	CHandleMap* pMap = AfxGetModuleThreadState()->m_pmapHGDIOBJ;
	pMap->DeleteTemp();
}

void PASCAL CMenu::DeleteTempMap()
{
	CHandleMap* pMap = AfxGetModuleThreadState()->m_pmapHMENU;
	pMap->DeleteTemp();
}
