|
|
//---------------------------------------------------------------------------
// RenderList.cpp - manages list of CRemderObj objects
//---------------------------------------------------------------------------
#include "stdafx.h"
#include "RenderList.h"
#include "Render.h"
//---------------------------------------------------------------------------
#define MAKE_HTHEME(recycle, slot) (HTHEME)IntToPtr((recycle << 16) | (slot & 0xffff))
//---------------------------------------------------------------------------
CRenderList::CRenderList() { _iNextUniqueId = 0;
ZeroMemory(&_csListLock, sizeof(_csListLock)); if( !InitializeCriticalSectionAndSpinCount(&_csListLock, 0 ) ) { ASSERT(0 == _csListLock.DebugInfo); } } //---------------------------------------------------------------------------
CRenderList::~CRenderList() { for (int i=0; i < _RenderEntries.m_nSize; i++) { //---- ignore refcount here (end of process) ----
if (_RenderEntries[i].pRenderObj) { //Log(LOG_RFBUG, L"DELETED CRenderObj at: 0x%08x", _RenderEntries[i].pRenderObj);
delete _RenderEntries[i].pRenderObj; } }
SAFE_DELETECRITICALSECTION(&_csListLock); } //---------------------------------------------------------------------------
HRESULT CRenderList::OpenRenderObject(CUxThemeFile *pThemeFile, int iThemeOffset, int iClassNameOffset, CDrawBase *pDrawBase, CTextDraw *pTextObj, HWND hwnd, DWORD dwOtdFlags, HTHEME *phTheme) { HRESULT hr = S_OK; CAutoCS autoCritSect(&_csListLock);
CRenderObj *pRender = NULL; int iUsedSlot = -1; int iNextAvailSlot = -1;
//---- see if OK to share an existing CRenderObj ----
BOOL fShare = ((! pDrawBase) && (! pTextObj) && (! LogOptionOn(LO_TMHANDLE))); if (fShare) { if ((dwOtdFlags) && (dwOtdFlags != OTD_NONCLIENT)) // bits other than nonclient are set
fShare = FALSE; }
//---- loop for sharing and finding first avail entry ----
for (int i=0; i < _RenderEntries.m_nSize; i++) { RENDER_OBJ_ENTRY *pEntry = &_RenderEntries[i]; pRender = pEntry->pRenderObj;
//---- skip over available entries ----
if (! pRender) { if (iNextAvailSlot == -1) // take first found slot
iNextAvailSlot = i;
continue; }
if ((fShare) && (! pEntry->fClosing)) { pRender->ValidateObj();
int iOffset = int(pRender->_pbSectionData - pRender->_pbThemeData);
if ((pRender->_pThemeFile == pThemeFile) && (iOffset == iThemeOffset)) { pEntry->iRefCount++; iUsedSlot = i; Log(LOG_CACHE, L"OpenRenderObject: found match for Offset=%d (slot=%d, refcnt=%d)", iThemeOffset, i, pEntry->iRefCount); break; } } }
if (iUsedSlot == -1) // not found
{ if (iNextAvailSlot == -1) // add to end
iUsedSlot = _RenderEntries.m_nSize ; else iUsedSlot = iNextAvailSlot;
_iNextUniqueId++;
hr = CreateRenderObj(pThemeFile, iUsedSlot, iThemeOffset, iClassNameOffset, _iNextUniqueId, TRUE, pDrawBase, pTextObj, dwOtdFlags, &pRender); if (FAILED(hr)) goto exit;
//Log(LOG_RFBUG, L"ALLOCATED CRenderObj at: 0x%08x", pRender);
//---- extract theme file Load ID ----
THEMEHDR *th = (THEMEHDR *)pRender->_pbThemeData; int iLoadId = 0; if (th) iLoadId = th->iLoadId;
RENDER_OBJ_ENTRY entry = {pRender, 1, 1, 0, iLoadId, FALSE, hwnd};
if (iUsedSlot == _RenderEntries.m_nSize) // add new entry
{ if (! _RenderEntries.Add(entry)) { delete pRender;
hr = MakeError32(E_OUTOFMEMORY); goto exit; }
Log(LOG_CACHE, L"OpenRenderObject: created new obj AT END (slot=%d, refcnt=%d)", pRender->_iCacheSlot, 1); } else // use an existing slot
{ entry.dwRecycleNum = _RenderEntries[iUsedSlot].dwRecycleNum + 1;
_RenderEntries[iUsedSlot] = entry;
Log(LOG_CACHE, L"OpenRenderObject: created new obj SLOT REUSE (slot=%d, refcnt=%d, recycle=%d)", iUsedSlot, 1, _RenderEntries[iUsedSlot].dwRecycleNum); }
}
if (SUCCEEDED(hr)) { RENDER_OBJ_ENTRY *pEntry = &_RenderEntries[iUsedSlot];
*phTheme = MAKE_HTHEME(pEntry->dwRecycleNum, iUsedSlot);
//---- for debugging refcount issues ----
if (LogOptionOn(LO_TMHANDLE)) { WCHAR buff[MAX_PATH];
if (hwnd) GetClassName(hwnd, buff, ARRAYSIZE(buff)); else buff[0] = 0;
//if (lstrcmpi(pRender->_pszClassName, L"window")==0)
{ //Log(LOG_TMHANDLE, L"OTD: cls=%s (%s), hwnd=0x%x, htheme=0x%x, new refcnt=%d",
// pRender->_pszClassName, buff, hwnd, *phTheme, pEntry->iRefCount);
} } }
exit: return hr; } //---------------------------------------------------------------------------
BOOL CRenderList::DeleteCheck(RENDER_OBJ_ENTRY *pEntry) { BOOL fClosed = FALSE;
if ((! pEntry->iRefCount) && (! pEntry->iInUseCount)) { //Log(LOG_RFBUG, L"DELETED CRenderObj at: 0x%08x", pEntry->pRenderObj);
delete pEntry->pRenderObj;
//---- important: don't use RemoveAt() or entries will shift and ----
//---- our "SlotNumber" model between RenderList & CacheList will ----
//---- be broken ----
pEntry->pRenderObj = NULL; pEntry->fClosing = FALSE;
fClosed = TRUE; }
return fClosed; } //---------------------------------------------------------------------------
HRESULT CRenderList::CloseRenderObject(HTHEME hTheme) { CAutoCS autoCritSect(&_csListLock); HRESULT hr = S_OK;
int iSlotNum = (DWORD(PtrToInt(hTheme)) & 0xffff); DWORD dwRecycleNum = (DWORD(PtrToInt(hTheme)) >> 16);
if (iSlotNum >= _RenderEntries.m_nSize) { Log(LOG_BADHTHEME, L"Illegal Theme Handle: 0x%x", hTheme);
hr = MakeError32(E_HANDLE); goto exit; }
RENDER_OBJ_ENTRY *pEntry = &_RenderEntries[iSlotNum]; if ((! pEntry->pRenderObj) || (pEntry->fClosing) || (pEntry->dwRecycleNum != dwRecycleNum)) { Log(LOG_BADHTHEME, L"Expired Theme Handle: 0x%x", hTheme);
hr = MakeError32(E_HANDLE); goto exit; }
//---- allow for our iRefCount to have been set to zero explicitly ----
if (pEntry->iRefCount > 0) pEntry->iRefCount--;
#if 0
//---- for debugging refcount issues ----
if (LogOptionOn(LO_TMHANDLE)) { CRenderObj *pRender = pEntry->pRenderObj;
Log(LOG_TMHANDLE, L"CTD: cls=%s, hwnd=0x%x, htheme=0x%x, new refcnt=%d", pRender->_pszClassName, pEntry->hwnd, hTheme, pEntry->iRefCount); } #endif
DeleteCheck(pEntry); exit: return hr; } //---------------------------------------------------------------------------
HRESULT CRenderList::OpenThemeHandle(HTHEME hTheme, CRenderObj **ppRenderObj, int *piSlotNum) { CAutoCS autoCritSect(&_csListLock); HRESULT hr = S_OK;
int iSlotNum = (int)(DWORD(PtrToInt(hTheme)) & 0xffff); DWORD dwRecycleNum = (DWORD(PtrToInt(hTheme)) >> 16);
if (iSlotNum >= _RenderEntries.m_nSize) { Log(LOG_BADHTHEME, L"Illegal Theme Handle: 0x%x", hTheme);
hr = MakeError32(E_HANDLE); goto exit; }
RENDER_OBJ_ENTRY *pEntry = &_RenderEntries[iSlotNum]; if ((! pEntry->pRenderObj) || (pEntry->fClosing) || (pEntry->dwRecycleNum != dwRecycleNum)) { Log(LOG_BADHTHEME, L"Expired Theme Handle: 0x%x", hTheme);
hr = MakeError32(E_HANDLE); goto exit; }
if (pEntry->iInUseCount > 25) { Log(LOG_BADHTHEME, L"Warning BREAK: high ThemeHandle inuse count=%d", pEntry->iInUseCount); }
pEntry->iInUseCount++;
*ppRenderObj = pEntry->pRenderObj; *piSlotNum = iSlotNum;
exit: return hr; } //---------------------------------------------------------------------------
void CRenderList::CloseThemeHandle(int iSlotNum) { CAutoCS autoCritSect(&_csListLock); RENDER_OBJ_ENTRY *pEntry = &_RenderEntries[iSlotNum];
if (pEntry->iInUseCount <= 0) { Log(LOG_ERROR, L"Bad iUseCount on CRenderObj at slot=%d", iSlotNum); } else { pEntry->iInUseCount--; DeleteCheck(pEntry); } } //---------------------------------------------------------------------------
void CRenderList::FreeRenderObjects(int iThemeFileLoadId) { CAutoCS autoCritSect(&_csListLock);
int iFoundCount = 0; int iClosedCount = 0;
//---- theme hooking has been turned off - mark all ----
//---- our objects so they can be freed as soon ----
//---- as all wrapper API's are exited so that ----
//---- we don't hold open those big theme files in memory ----
for (int i=0; i < _RenderEntries.m_nSize; i++) { RENDER_OBJ_ENTRY *pEntry = &_RenderEntries[i];
if (pEntry->pRenderObj) { if ((iThemeFileLoadId == -1) || (iThemeFileLoadId == pEntry->iLoadId)) { iFoundCount++;
HTHEME hTheme = MAKE_HTHEME(pEntry->dwRecycleNum, i);
Log(LOG_BADHTHEME, L"Unclosed RenderList[]: class=%s, hwnd=0x%x, htheme=0x%x, refcnt=%d", pEntry->pRenderObj->_pszClassName, pEntry->hwnd, hTheme, pEntry->iRefCount);
pEntry->fClosing = TRUE; // don't grant further access to this obj
pEntry->iRefCount = 0; // free it as soon as callers have exited
if (DeleteCheck(pEntry)) // delete now or mark for "delete on API exit"
{ //---- just deleted it ----
iClosedCount++; } } } }
Log(LOG_TMHANDLE, L"FreeRenderObjects: iLoadId=%d, found-open=%d, closed-now=%d", iThemeFileLoadId, iFoundCount, iClosedCount); } //---------------------------------------------------------------------------
#ifdef DEBUG
void CRenderList::DumpFileHolders() { CAutoCS autoCritSect(&_csListLock); if (LogOptionOn(LO_TMHANDLE)) { //---- find number of CRenderObj's ----
int iCount = 0; _RenderEntries.m_nSize;
for (int i=0; i < _RenderEntries.m_nSize; i++) { if (_RenderEntries[i].pRenderObj) iCount++; }
if (! iCount) { Log(LOG_TMHANDLE, L"---- No CRenderObj objects ----"); } else { Log(LOG_TMHANDLE, L"---- Dump of %d CRenderObj objects ----", iCount);
for (int i=0; i < _RenderEntries.m_nSize; i++) { RENDER_OBJ_ENTRY *pEntry = &_RenderEntries[i];
if (pEntry->pRenderObj) { CRenderObj *pr = pEntry->pRenderObj; THEMEHDR *th = (THEMEHDR *)pr->_pbThemeData; int iLoadId = 0;
if (th) iLoadId = th->iLoadId;
LPCWSTR pszClass = NULL;
if (pr->_pszClassName) pszClass = pr->_pszClassName;
Log(LOG_TMHANDLE, L" RenderObj[%d]: class=%s, refcnt=%d, hwnd=0x%x", i, pszClass, pEntry->iRefCount, pEntry->hwnd);
} } } } } #endif
//---------------------------------------------------------------------------
|