#include "namellst.h" #include "sfstr.h" #include "dbg.h" #define ARRAYSIZE(a) (sizeof((a))/sizeof((a)[0])) //============================================================================= //============================================================================= //== CNamedElem == //============================================================================= //============================================================================= /////////////////////////////////////////////////////////////////////////////// // Public HRESULT CNamedElem::GetName(LPTSTR psz, DWORD cch, DWORD* pcchRequired) { return SafeStrCpyNReq(psz, _pszElemName, cch, pcchRequired); } #ifdef DEBUG LPCTSTR CNamedElem::DbgGetName() { return _pszElemName; } #endif /////////////////////////////////////////////////////////////////////////////// // Protected CNamedElem::CNamedElem() : _pszElemName(NULL) {} CNamedElem::~CNamedElem() { if (_pszElemName) { _FreeName(); } } HRESULT CNamedElem::_SetName(LPCTSTR pszElemName) { ASSERT(!_pszElemName); HRESULT hres; DWORD cch = lstrlen(pszElemName) + 1; ASSERT(cch); _pszElemName = (LPTSTR)LocalAlloc(LPTR, cch * sizeof(TCHAR)); if (_pszElemName) { hres = SafeStrCpyN(_pszElemName, pszElemName, cch); #ifdef DEBUG if (SUCCEEDED(hres)) { _pszRCAddRefName = _pszElemName; // __FILE__ and __LINE__ should come from original file CRefCounted::_RCCreate(__FILE__, __LINE__); } #endif } else { hres = E_OUTOFMEMORY; } return hres; } HRESULT CNamedElem::_FreeName() { ASSERT(_pszElemName); LocalFree((HLOCAL)_pszElemName); return S_OK; } //============================================================================= //============================================================================= //== CNamedElemList == //============================================================================= //============================================================================= /////////////////////////////////////////////////////////////////////////////// // Public HRESULT CNamedElemList::Init(NAMEDELEMCREATEFCT createfct, NAMEDELEMGETFILLENUMFCT enumfct) { HRESULT hres; _createfct = createfct; _enumfct = enumfct; _pcs = new CRefCountedCritSect(); if (_pcs) { InitializeCriticalSection(_pcs); hres = S_OK; } else { hres = E_OUTOFMEMORY; } return hres; } HRESULT CNamedElemList::GetOrAdd(LPCTSTR pszElemName, CNamedElem** ppelem) { HRESULT hres = E_INVALIDARG; *ppelem = NULL; if (pszElemName) { CElemSlot* pes; EnterCriticalSection(_pcs); hres = _GetElemSlot(pszElemName, &pes); if (SUCCEEDED(hres)) { if (S_FALSE != hres) { // Got one hres = pes->GetNamedElem(ppelem); pes->RCRelease(); } else { // None found hres = _Add(pszElemName, ppelem); if (SUCCEEDED(hres)) { #ifdef DEBUG TRACE(TF_NAMEDELEMLISTMODIF, TEXT("Added to '%s': '%s'"), _szDebugName, pszElemName); #endif hres = S_FALSE; } } } LeaveCriticalSection(_pcs); } return hres; } HRESULT CNamedElemList::Get(LPCTSTR pszElemName, CNamedElem** ppelem) { HRESULT hres = E_INVALIDARG; *ppelem = NULL; if (pszElemName) { CElemSlot* pes; EnterCriticalSection(_pcs); hres = _GetElemSlot(pszElemName, &pes); if (SUCCEEDED(hres)) { if (S_FALSE != hres) { // Got one hres = pes->GetNamedElem(ppelem); pes->RCRelease(); } } LeaveCriticalSection(_pcs); } return hres; } HRESULT CNamedElemList::Add(LPCTSTR pszElemName, CNamedElem** ppelem) { HRESULT hres = E_INVALIDARG; if (pszElemName) { EnterCriticalSection(_pcs); hres = _Add(pszElemName, ppelem); LeaveCriticalSection(_pcs); #ifdef DEBUG if (SUCCEEDED(hres)) { TRACE(TF_NAMEDELEMLISTMODIF, TEXT("Added to '%s': '%s'"), _szDebugName, pszElemName); } #endif } return hres; } HRESULT CNamedElemList::Remove(LPCTSTR pszElemName) { HRESULT hres = E_INVALIDARG; if (pszElemName) { EnterCriticalSection(_pcs); hres = _Remove(pszElemName); LeaveCriticalSection(_pcs); #ifdef DEBUG if (SUCCEEDED(hres)) { if (S_FALSE != hres) { TRACE(TF_NAMEDELEMLISTMODIF, TEXT("Removed from '%s': '%s'"), _szDebugName, pszElemName); } else { TRACE(TF_NAMEDELEMLISTMODIF, TEXT("TRIED to remove from '%s': '%s'"), _szDebugName, pszElemName); } } #endif } return hres; } HRESULT CNamedElemList::ReEnum() { HRESULT hres = E_FAIL; if (_enumfct) { #ifdef DEBUG TRACE(TF_NAMEDELEMLISTMODIF, TEXT("ReEnum '%s' beginning"), _szDebugName); #endif EnterCriticalSection(_pcs); hres = _EmptyList(); if (SUCCEEDED(hres)) { CFillEnum* pfillenum; hres = _enumfct(&pfillenum); if (SUCCEEDED(hres)) { TCHAR szElemName[MAX_PATH]; LPTSTR pszElemName = szElemName; DWORD cchReq; do { hres = pfillenum->Next(szElemName, ARRAYSIZE(szElemName), &cchReq); if (S_FALSE != hres) { if (E_BUFFERTOOSMALL == hres) { pszElemName = (LPTSTR)LocalAlloc(LPTR, cchReq * sizeof(TCHAR)); if (pszElemName) { hres = pfillenum->Next(pszElemName, cchReq, &cchReq); } else { hres = E_OUTOFMEMORY; } } if (SUCCEEDED(hres) && (S_FALSE != hres)) { hres = _Add(pszElemName, NULL); if (FAILED(hres)) { // continue enum even if a device is not fully installed hres = S_OK; } #ifdef DEBUG if (SUCCEEDED(hres)) { TRACE(TF_NAMEDELEMLISTMODIF, TEXT("Added to '%s': '%s'"), _szDebugName, pszElemName); } #endif } if (pszElemName && (pszElemName != szElemName)) { LocalFree((HLOCAL)pszElemName); } } } while (SUCCEEDED(hres) && (S_FALSE != hres)); pfillenum->RCRelease(); } } LeaveCriticalSection(_pcs); #ifdef DEBUG if (SUCCEEDED(hres)) { TRACE(TF_NAMEDELEMLISTMODIF, TEXT("ReEnumed '%s'"), _szDebugName); } #endif } return hres; } HRESULT CNamedElemList::EmptyList() { HRESULT hres; EnterCriticalSection(_pcs); hres = _EmptyList(); LeaveCriticalSection(_pcs); #ifdef DEBUG if (SUCCEEDED(hres)) { TRACE(TF_NAMEDELEMLISTMODIF, TEXT("Emptied '%s'"), _szDebugName); } #endif return hres; } HRESULT CNamedElemList::GetEnum(CNamedElemEnum** ppenum) { HRESULT hres; CNamedElemEnum* penum = new CNamedElemEnum(); if (penum) { CElemSlot* pesTail; EnterCriticalSection(_pcs); hres = _GetTail(&pesTail); if (SUCCEEDED(hres)) { hres = penum->_Init(pesTail, _pcs); if (SUCCEEDED(hres)) { *ppenum = penum; } else { delete penum; } if (pesTail) { pesTail->RCRelease(); } } LeaveCriticalSection(_pcs); } else { hres = E_OUTOFMEMORY; } return hres; } CNamedElemList::CNamedElemList() : _pcs(NULL), _pesHead(NULL) { #ifdef DEBUG _pszRCAddRefName = _szDebugName; _szDebugName[0] = 0; #endif } CNamedElemList::~CNamedElemList() { _EmptyList(); if (_pcs) { DeleteCriticalSection(_pcs); _pcs->RCRelease(); } } /////////////////////////////////////////////////////////////////////////////// // Private // All these fcts have to be called from within The critical section HRESULT CNamedElemList::_Add(LPCTSTR pszElemName, CNamedElem** ppelem) { CNamedElem* pelem; HRESULT hres = _createfct(&pelem); if (SUCCEEDED(hres)) { hres = pelem->Init(pszElemName); if (SUCCEEDED(hres)) { CElemSlot* pes = new CElemSlot(); if (pes) { // This takes an additionnal ref on pelem hres = pes->Init(pelem, NULL, _pesHead); if (SUCCEEDED(hres)) { if (_pesHead) { _pesHead->SetPrev(pes); } _pesHead = pes; } else { pes->RCRelease(); } } else { hres = E_OUTOFMEMORY; } } pelem->RCRelease(); if (FAILED(hres)) { pelem = NULL; } } if (ppelem) { if (pelem) { pelem->RCAddRef(); } *ppelem = pelem; } return hres; } HRESULT CNamedElemList::_GetTail(CElemSlot** ppes) { HRESULT hr; CElemSlot* pesLastValid = _GetValidHead(); if (pesLastValid) { CElemSlot* pesOld = pesLastValid; while (NULL != (pesLastValid = pesLastValid->GetNextValid())) { pesOld->RCRelease(); pesOld = pesLastValid; } pesLastValid = pesOld; hr = S_OK; } else { hr = S_FALSE; } // pesLastValid is already RCAddRef'ed *ppes = pesLastValid; return hr; } HRESULT CNamedElemList::_GetElemSlot(LPCTSTR pszElemName, CElemSlot** ppes) { HRESULT hres = S_FALSE; CElemSlot* pes = _GetValidHead(); CElemSlot* pesOld = pes; while (pes && SUCCEEDED(hres) && (S_FALSE == hres)) { CNamedElem* pelem; hres = pes->GetNamedElem(&pelem); if (SUCCEEDED(hres)) { TCHAR szElemName[MAX_PATH]; LPTSTR pszElemNameLocal = szElemName; DWORD cchReq; hres = pelem->GetName(szElemName, ARRAYSIZE(szElemName), &cchReq); if (E_BUFFERTOOSMALL == hres) { pszElemNameLocal = (LPTSTR)LocalAlloc(LPTR, cchReq * sizeof(TCHAR)); if (pszElemNameLocal) { hres = pelem->GetName(pszElemNameLocal, cchReq, &cchReq); } else { hres = E_OUTOFMEMORY; } } pelem->RCRelease(); if (SUCCEEDED(hres)) { if (!lstrcmpi(pszElemNameLocal, pszElemName)) { // Found it! pes->RCAddRef(); *ppes = pes; hres = S_OK; } else { pes = pes->GetNextValid(); hres = S_FALSE; } } if (pszElemNameLocal && (pszElemNameLocal != szElemName)) { LocalFree((HLOCAL)pszElemNameLocal); } } pesOld->RCRelease(); pesOld = pes; } return hres; } HRESULT CNamedElemList::_Remove(LPCTSTR pszElemName) { ASSERT(pszElemName); CElemSlot* pes; HRESULT hres = _GetElemSlot(pszElemName, &pes); if (SUCCEEDED(hres) && (S_FALSE != hres)) { if (pes == _pesHead) { // New head _pesHead = pes->GetNextValid(); if (_pesHead) { // We do not keep a ref on the head _pesHead->RCRelease(); } } hres = pes->Remove(); // 2: one to balance the _GetElemSlot and one to remove from list pes->RCRelease(); pes->RCRelease(); } return hres; } HRESULT CNamedElemList::_EmptyList() { HRESULT hres = S_FALSE; CElemSlot* pes = _GetValidHead(); while (SUCCEEDED(hres) && pes) { CElemSlot* pesOld = pes; hres = pes->Remove(); pes = pes->GetNextValid(); pesOld->RCRelease(); } _pesHead = NULL; return hres; } CElemSlot* CNamedElemList::_GetValidHead() { CElemSlot* pes = _pesHead; if (pes) { if (pes->IsValid()) { pes->RCAddRef(); } else { pes = pes->GetNextValid(); } } return pes; } #ifdef DEBUG HRESULT CNamedElemList::InitDebug(LPWSTR pszDebugName) { HRESULT hres = SafeStrCpyN(_szDebugName, pszDebugName, ARRAYSIZE(_szDebugName)); if (SUCCEEDED(hres)) { _RCCreate(__FILE__, __LINE__); } return hres; } void CNamedElemList::AssertNoDuplicate() { EnterCriticalSection(_pcs); CElemSlot* pes = _GetValidHead(); while (pes) { CElemSlot* pesOld = pes; CNamedElem* pelem; WCHAR szName[1024]; HRESULT hres = pes->GetNamedElem(&pelem); if (SUCCEEDED(hres)) { DWORD cchReq; hres = pelem->GetName(szName, ARRAYSIZE(szName), &cchReq); if (SUCCEEDED(hres)) { CElemSlot* pesIn = pes->GetNextValid(); while (pesIn) { CElemSlot* pesInOld = pesIn; CNamedElem* pelemIn; WCHAR szNameIn[1024]; hres = pesIn->GetNamedElem(&pelemIn); if (SUCCEEDED(hres)) { DWORD cchReqIn; hres = pelemIn->GetName(szNameIn, ARRAYSIZE(szNameIn), &cchReqIn); if (SUCCEEDED(hres)) { ASSERT(lstrcmp(szName, szNameIn)); } pelemIn->RCRelease(); } pesIn = pesIn->GetNextValid(); pesInOld->RCRelease(); } } pelem->RCRelease(); } pes = pes->GetNextValid(); pesOld->RCRelease(); } LeaveCriticalSection(_pcs); } void CNamedElemList::AssertAllElemsRefCount1() { EnterCriticalSection(_pcs); ASSERT(1 == _RCGetRefCount()); CElemSlot* pes = _GetValidHead(); while (pes) { CElemSlot* pesOld = pes; // Actually check for 2, since it was just AddRefed ASSERT(2 == pes->_RCGetRefCount()); CNamedElem* pelem; HRESULT hres = pes->GetNamedElem(&pelem); if (SUCCEEDED(hres)) { ASSERT(2 == pelem->_RCGetRefCount()); pelem->RCRelease(); } pes = pes->GetNextValid(); pesOld->RCRelease(); } LeaveCriticalSection(_pcs); } #endif //============================================================================= //============================================================================= //== CNamedElemEnum == //============================================================================= //============================================================================= /////////////////////////////////////////////////////////////////////////////// // Public HRESULT CNamedElemEnum::Next(CNamedElem** ppelem) { HRESULT hres = S_FALSE; EnterCriticalSection(_pcsList); if (_pesCurrent) { CElemSlot* pes = _pesCurrent; *ppelem = NULL; if (pes) { if (!_fFirst || !pes->IsValid()) { pes = pes->GetPrevValid(); } } if (!_fFirst && _pesCurrent) { _pesCurrent->RCRelease(); } if (pes) { hres = pes->GetNamedElem(ppelem); } _pesCurrent = pes; _fFirst = FALSE; } LeaveCriticalSection(_pcsList); return hres; } CNamedElemEnum::CNamedElemEnum() : _pesCurrent(NULL), _pcsList(NULL) { #ifdef DEBUG static TCHAR _szDebugName[] = TEXT("CNamedElemEnum"); _pszRCAddRefName = _szDebugName; #endif } CNamedElemEnum::~CNamedElemEnum() { if (_pcsList) { _pcsList->RCRelease(); } if (_pesCurrent) { _pesCurrent->RCRelease(); } } /////////////////////////////////////////////////////////////////////////////// // Private HRESULT CNamedElemEnum::_Init(CElemSlot* pesHead, CRefCountedCritSect* pcsList) { pcsList->RCAddRef(); _pcsList = pcsList; _fFirst = TRUE; _pesCurrent = pesHead; if (_pesCurrent) { _pesCurrent->RCAddRef(); } return S_OK; } //============================================================================= //============================================================================= //== CElemSlot == //============================================================================= //============================================================================= /////////////////////////////////////////////////////////////////////////////// // Public HRESULT CElemSlot::Init(CNamedElem* pelem, CElemSlot* pesPrev, CElemSlot* pesNext) { _pelem = pelem; pelem->RCAddRef(); _fValid = TRUE; _pesPrev = pesPrev; _pesNext = pesNext; return S_OK; } HRESULT CElemSlot::Remove() { _fValid = FALSE; _pelem->RCRelease(); _pelem = NULL; return S_OK; } HRESULT CElemSlot::GetNamedElem(CNamedElem** ppelem) { ASSERT(_fValid); _pelem->RCAddRef(); *ppelem = _pelem; return S_OK; } void CElemSlot::SetPrev(CElemSlot* pes) { _pesPrev = pes; } CElemSlot* CElemSlot::GetNextValid() { CElemSlot* pes = _pesNext; while (pes && !pes->IsValid()) { pes = pes->_pesNext; } if (pes) { pes->RCAddRef(); } return pes; } CElemSlot* CElemSlot::GetPrevValid() { CElemSlot* pes = _pesPrev; while (pes && !pes->IsValid()) { pes = pes->_pesPrev; } if (pes) { pes->RCAddRef(); } return pes; } BOOL CElemSlot::IsValid() { return _fValid; } CElemSlot::CElemSlot() : _fValid(FALSE), _pesPrev(NULL), _pesNext(NULL) { #ifdef DEBUG static TCHAR _szDebugName[] = TEXT("CElemSlot"); _pszRCAddRefName = _szDebugName; #endif } CElemSlot::~CElemSlot() { ASSERT(!_fValid); if (_pesPrev) { _pesPrev->_pesNext = _pesNext; } if (_pesNext) { _pesNext->_pesPrev = _pesPrev; } }