/*=================================================================== Microsoft Denali Microsoft Confidential. Copyright 1997 Microsoft Corporation. All Rights Reserved. Component: MetaUtil object File: KeyCol.cpp Owner: t-BrianM This file contains implementation of the key collections. ===================================================================*/ #include "stdafx.h" #include "MetaUtil.h" #include "MUtilObj.h" #include "keycol.h" /*------------------------------------------------------------------ * C F l a t K e y C o l l e c t i o n */ /*=================================================================== CFlatKeyCollection::CFlatKeyCollection Constructor Parameters: None Returns: Nothing ===================================================================*/ CFlatKeyCollection::CFlatKeyCollection() : m_tszBaseKey(NULL) { } /*=================================================================== CFlatKeyCollection::Init Constructor Parameters: pIMeta ATL Smart pointer to the metabase admin base object tszBaseKey Name of key to enumerate from Returns: E_OUTOFMEMORY if allocation fails S_OK on success ===================================================================*/ HRESULT CFlatKeyCollection::Init(const CComPtr &pIMeta, LPCTSTR tszBaseKey) { ASSERT(pIMeta.p != NULL); ASSERT_NULL_OR_STRING(tszBaseKey); m_pIMeta = pIMeta; // Copy tszBaseKey to m_tszBaseKey if (tszBaseKey == NULL) { // BaseKey is root m_tszBaseKey = NULL; } else { // Allocate and copy the passed string to the member string m_tszBaseKey = new TCHAR[_tcslen(tszBaseKey) + 1]; if (m_tszBaseKey == NULL) { return ::ReportError(E_OUTOFMEMORY); } _tcscpy(m_tszBaseKey, tszBaseKey); CannonizeKey(m_tszBaseKey); } return S_OK; } /*=================================================================== CFlatKeyCollection::~CFlatKeyCollection Destructor Parameters: None Returns: Nothing ===================================================================*/ CFlatKeyCollection::~CFlatKeyCollection() { if (m_tszBaseKey != NULL) delete m_tszBaseKey; } /*=================================================================== CFlatKeyCollection::InterfaceSupportsErrorInfo Standard ATL implementation ===================================================================*/ STDMETHODIMP CFlatKeyCollection::InterfaceSupportsErrorInfo(REFIID riid) { static const IID* arr[] = { &IID_IKeyCollection, }; for (int i=0;iEnumKeys(METADATA_MASTER_ROOT_HANDLE, T2W(m_tszBaseKey), wszSubKey, iIndex); if (FAILED(hr)) { if (HRESULT_CODE(hr) == ERROR_NO_MORE_ITEMS) { // Ran out of items, return the number we counted *plReturn = iIndex; return S_OK; } else { return ::ReportError(hr); } } iIndex++; } } /*=================================================================== CFlatKeyCollection::get_Item Get method for Item property. Returns a key given its index. Parameters: lIndex [in] 1 based index of the key to get pbstrRetKey [out, retval] Retrived key Returns: E_INVALIDARG if pbstrRetKey == NULL or lIndex <= 0 S_OK on success ===================================================================*/ STDMETHODIMP CFlatKeyCollection::get_Item(long lIndex, BSTR *pbstrRetKey) { TRACE0("MetaUtil: CFlatKeyCollection::get_Item\n"); ASSERT_NULL_OR_POINTER(pbstrRetKey, BSTR); if ((pbstrRetKey == NULL) || (lIndex <= 0)) { return ::ReportError(E_INVALIDARG); } *pbstrRetKey = NULL; USES_CONVERSION; HRESULT hr; wchar_t wszSubKey[ADMINDATA_MAX_NAME_LEN]; hr = m_pIMeta->EnumKeys(METADATA_MASTER_ROOT_HANDLE, T2W(m_tszBaseKey), wszSubKey, lIndex - 1); if (FAILED(hr)) { return ::ReportError(hr); } *pbstrRetKey = W2BSTR(wszSubKey); return S_OK; } /*=================================================================== CFlatKeyCollection::get__NewEnum Get method for _NewEnum property. Returns an enumeration object for the subkeys. Parameters: ppIReturn [out, retval] Interface for the enumberation object Returns: E_OUTOFMEMORY if allocation fails. E_INVALIDARG if ppIReturn == NULL S_OK on success ===================================================================*/ STDMETHODIMP CFlatKeyCollection::get__NewEnum(LPUNKNOWN * ppIReturn) { TRACE0("MetaUtil: CFlatKeyCollection::get__NewEnum\n"); ASSERT_NULL_OR_POINTER(ppIReturn, LPUNKNOWN); if (ppIReturn == NULL) { return ::ReportError(E_INVALIDARG); } HRESULT hr; // Create the flat key enumeration CComObject *pObj = NULL; ATLTRY(pObj = new CComObject); if (pObj == NULL) { return ::ReportError(E_OUTOFMEMORY); } hr = pObj->Init(m_pIMeta, m_tszBaseKey, 0); if (FAILED(hr)) { return ::ReportError(hr); } // Set the interface to IUnknown hr = pObj->QueryInterface(IID_IUnknown, (void **) ppIReturn); if (FAILED(hr)) { return ::ReportError(hr); } ASSERT(*ppIReturn != NULL); return S_OK; } /*=================================================================== CFlatKeyCollection::Add Adds a key to the metabase relative to the collection's base key Parameters: bstrRelKey [in] Relative key to add Returns: E_INVALIDARG if bstrRelKey == NULL S_OK on success ===================================================================*/ STDMETHODIMP CFlatKeyCollection::Add(BSTR bstrRelKey) { TRACE0("MetaUtil: CFlatKeyCollection::Add\n"); ASSERT_NULL_OR_POINTER(bstrRelKey, OLECHAR); if (bstrRelKey == NULL) { return ::ReportError(E_INVALIDARG); } // Build the full key USES_CONVERSION; TCHAR tszFullKey[ADMINDATA_MAX_NAME_LEN]; if (m_tszBaseKey == NULL) { _tcscpy(tszFullKey, OLE2T(bstrRelKey)); } else { _tcscpy(tszFullKey, m_tszBaseKey); _tcscat(tszFullKey, _T("/")); _tcscat(tszFullKey, OLE2T(bstrRelKey)); } CannonizeKey(tszFullKey); return ::CreateKey(m_pIMeta, tszFullKey); } /*=================================================================== CFlatKeyCollection::Remove Removes a key from the metabase relative to the collection's base key Parameters: bstrRelKey [in] Relative key to remove Returns: E_INVALIDARG if bstrRelKey == NULL S_OK on success ===================================================================*/ STDMETHODIMP CFlatKeyCollection::Remove(BSTR bstrRelKey) { TRACE0("MetaUtil: CFlatKeyCollection::Remove\n"); ASSERT_NULL_OR_POINTER(bstrRelKey, OLECHAR); if (bstrRelKey == NULL) { return ::ReportError(E_INVALIDARG); } // Build the full key USES_CONVERSION; TCHAR tszFullKey[ADMINDATA_MAX_NAME_LEN]; if (m_tszBaseKey == NULL) { _tcscpy(tszFullKey, OLE2T(bstrRelKey)); } else { _tcscpy(tszFullKey, m_tszBaseKey); _tcscat(tszFullKey, _T("/")); _tcscat(tszFullKey, OLE2T(bstrRelKey)); } CannonizeKey(tszFullKey); return ::DeleteKey(m_pIMeta, tszFullKey); } /*------------------------------------------------------------------ * C F l a t K e y E n u m */ /*=================================================================== CFlatKeyEnum::CFlatKeyEnum Constructor Parameters: None Returns: Nothing ===================================================================*/ CFlatKeyEnum::CFlatKeyEnum() : m_tszBaseKey(NULL), m_iIndex(0) { } /*=================================================================== CFlatKeyEnum::Init Constructor Parameters: pIMeta ATL Smart pointer to the metabase tszBaseKey Name of key to enumerate from iIndex Index of next element in enumeration Returns: E_OUTOFMEMORY if allocation fails S_OK on success ===================================================================*/ HRESULT CFlatKeyEnum::Init(const CComPtr &pIMeta, LPCTSTR tszBaseKey, int iIndex) { ASSERT(pIMeta.p != NULL); ASSERT_NULL_OR_STRING(tszBaseKey); ASSERT(iIndex >= 0); m_pIMeta = pIMeta; // Copy tszBaseKey to m_tszBaseKey if (tszBaseKey == NULL) { // BaseKey is root m_tszBaseKey = NULL; } else { // Allocate and copy the passed string to the member string m_tszBaseKey = new TCHAR[_tcslen(tszBaseKey) + 1]; if (m_tszBaseKey == NULL) { return ::ReportError(E_OUTOFMEMORY); } _tcscpy(m_tszBaseKey, tszBaseKey); CannonizeKey(m_tszBaseKey); } m_iIndex = iIndex; return S_OK; } /*=================================================================== CFlatKeyEnum::~CFlatKeyEnum Destructor Parameters: None Returns: Nothing ===================================================================*/ CFlatKeyEnum::~CFlatKeyEnum() { if (m_tszBaseKey != NULL) { delete m_tszBaseKey; } } /*=================================================================== CFlatKeyEnum::Next Gets the next n items from the enumberation. Parameters: ulNumToGet [in] Number of elements to get rgvarDest [out] Array to put them in pulNumGot [out] If not NULL, number of elements rgvarDest got Returns: E_INVALIDARG if rgvarDest == NULL S_OK on success ===================================================================*/ STDMETHODIMP CFlatKeyEnum::Next(unsigned long ulNumToGet, VARIANT FAR* rgvarDest, unsigned long FAR* pulNumGot) { TRACE0("MetaUtil: CFlatKeyEnum::Next\n"); ASSERT_NULL_OR_POINTER(pulNumGot, unsigned long); // Make sure the array is big enough and we can write to it ASSERT((rgvarDest == NULL) || IsValidAddress(rgvarDest, ulNumToGet * sizeof(VARIANT), TRUE)); if (rgvarDest == NULL) { return ::ReportError(E_INVALIDARG); } USES_CONVERSION; HRESULT hr; wchar_t wszSubKey[ADMINDATA_MAX_NAME_LEN]; unsigned int uiDestIndex; // Clear the output array for(uiDestIndex = 0; uiDestIndex < ulNumToGet; uiDestIndex++) { VariantInit(&(rgvarDest[uiDestIndex])); } // For each subkey to get uiDestIndex = 0; while (uiDestIndex < ulNumToGet) { // Get a subkey hr = m_pIMeta->EnumKeys(METADATA_MASTER_ROOT_HANDLE, T2W(m_tszBaseKey), wszSubKey, m_iIndex); if (FAILED(hr)) { if (HRESULT_CODE(hr) == ERROR_NO_MORE_ITEMS) { if (pulNumGot != NULL) { *pulNumGot = uiDestIndex; } return S_FALSE; } else { return ::ReportError(hr); } } // Output the subkey rgvarDest[uiDestIndex].vt = VT_BSTR; rgvarDest[uiDestIndex].bstrVal = W2BSTR(wszSubKey); // Setup next iteration m_iIndex++; uiDestIndex++; } if (pulNumGot != NULL) { *pulNumGot = uiDestIndex; } return S_OK; } /*=================================================================== CFlatKeyEnum::Skip Skips the next n items in an enumeration Parameters: ulNumToSkip [in] Number of elements to skip Returns: S_OK always ===================================================================*/ STDMETHODIMP CFlatKeyEnum::Skip(unsigned long ulNumToSkip) { TRACE0("MetaUtil: CFlatKeyEnum::Skip\n"); m_iIndex += ulNumToSkip; return S_OK; } /*=================================================================== CFlatKeyEnum::Reset Rests the enumeration to the first item Parameters: None Returns: S_OK always ===================================================================*/ STDMETHODIMP CFlatKeyEnum::Reset() { TRACE0("MetaUtil: CFlatKeyEnum::Reset\n"); m_iIndex = 0; return S_OK; } /*=================================================================== CFlatKeyEnum::Clone Gets an interface pointer to a copy of the enumeration at its current state. Parameters: ppIReturn [out] Pointer to interface for copy Returns: E_OUTOFMEMORY if allocation fails. E_INVALIDARG if ppIReturn == NULL S_OK on success ===================================================================*/ STDMETHODIMP CFlatKeyEnum::Clone(IEnumVARIANT FAR* FAR* ppIReturn) { TRACE0("MetaUtil: CFlatKeyEnum::Clone\n"); ASSERT_NULL_OR_POINTER(ppIReturn, LPUNKNOWN); if (ppIReturn == NULL) { return ::ReportError(E_INVALIDARG); } HRESULT hr; // Create a copy of the enumeration CComObject *pObj = NULL; ATLTRY(pObj = new CComObject); if (pObj == NULL) { return ::ReportError(E_OUTOFMEMORY); } hr = pObj->Init(m_pIMeta, m_tszBaseKey, m_iIndex); if (FAILED(hr)) { return ::ReportError(hr); } // Set the interface to IEnumVARIANT hr = pObj->QueryInterface(IID_IEnumVARIANT, (void **) ppIReturn); if (FAILED(hr)) { return ::ReportError(hr); } ASSERT(*ppIReturn != NULL); return S_OK; } /*------------------------------------------------------------------ * C K e y S t a c k N o d e */ /*=================================================================== CKeyStackNode::Init Constructor Parameters: tszRelKey Relative key for the enumeration level, NULL for root iIndex 0-based index for the next element Returns: E_OUTOFMEMORY if allocation fails. E_INVALIDARG if iIndex < 0 S_OK on success ===================================================================*/ HRESULT CKeyStackNode::Init(LPCTSTR tszRelKey, int iIndex) { ASSERT_NULL_OR_STRING(tszRelKey); ASSERT(iIndex >= 0); // Copy the relative key string if (tszRelKey == NULL) { // RelKey is empty m_tszRelKey = NULL; } else { // Allocate and copy the passed string to the member string m_tszRelKey = new TCHAR[_tcslen(tszRelKey) + 1]; if (m_tszRelKey == NULL) { return E_OUTOFMEMORY; } _tcscpy(m_tszRelKey, tszRelKey); } m_iIndex = iIndex; return S_OK; } /*=================================================================== CKeyStackNode::~CKeyStackNode Destructor Parameters: None Returns: Nothing ===================================================================*/ CKeyStackNode::~CKeyStackNode() { if (m_tszRelKey != NULL) { delete m_tszRelKey; } } /*=================================================================== CKeyStackNode::Clone Copies the node, except for the next pointer, which is NULL. Parameters: None Returns: NULL on failure Pointer to copy of node on success ===================================================================*/ CKeyStackNode *CKeyStackNode::Clone() { HRESULT hr; CKeyStackNode *pCRet; pCRet = new CKeyStackNode(); if (pCRet == NULL) { return NULL; } hr = pCRet->Init(m_tszRelKey, m_iIndex); if (FAILED(hr)) { delete pCRet; return NULL; } return pCRet; } /*------------------------------------------------------------------ * C K e y S t a c k */ /*=================================================================== CKeyStack::~CKeyStack Destructor Parameters: None Returns: Nothing ===================================================================*/ CKeyStack::~CKeyStack() { // Delete the remaining nodes CKeyStackNode *pCDelete; while(m_pCTop != NULL) { ASSERT_POINTER(m_pCTop, CKeyStackNode); pCDelete = m_pCTop; m_pCTop = m_pCTop->m_pCNext; delete pCDelete; } } /*=================================================================== CKeyStack::Push Pushes a CKeyStackNode onto the stack Parameters: pNew Pointer to CKeyStackNode to push on the stack Returns: Nothing, never fails Notes: CKeyStack "owns" the memory pointed to by pNew after call. CKeyStack or a later caller will delete it when done with it. ===================================================================*/ void CKeyStack::Push(CKeyStackNode *pCNew) { ASSERT_POINTER(pCNew, CKeyStackNode); pCNew->m_pCNext = m_pCTop; m_pCTop = pCNew; } /*=================================================================== CKeyStack::Pop Pops a CKeyStackNode from the stack Parameters: None Returns: Pointer to the top element or NULL if the stack is empty Notes: Caller "owns" the memory pointed to by pNew after call. Caller is expected to delete it when it is done with it. ===================================================================*/ CKeyStackNode *CKeyStack::Pop() { CKeyStackNode *pCRet; pCRet = m_pCTop; if (m_pCTop != NULL) { m_pCTop = m_pCTop->m_pCNext; ASSERT_NULL_OR_POINTER(m_pCTop, CKeyStackNode); } return pCRet; } /*=================================================================== CKeyStack::Clone Copies the stack, including all of the nodes. Parameters: Sheep Returns: NULL on failure Pointer to copy of stack on success ===================================================================*/ CKeyStack *CKeyStack::Clone() { CKeyStack *pCRet; // Build the container pCRet = new CKeyStack(); if (pCRet == NULL) { return NULL; } // Copy the nodes CKeyStackNode *pCSource; CKeyStackNode **ppCDest; pCSource = m_pCTop; ppCDest = &(pCRet->m_pCTop); while(pCSource != NULL) { ASSERT_POINTER(pCSource, CKeyStackNode); *ppCDest = pCSource->Clone(); if ((*ppCDest) == NULL) { delete pCRet; return NULL; } ppCDest = &((*ppCDest)->m_pCNext); pCSource = pCSource->m_pCNext; } *ppCDest = NULL; return pCRet; } /*------------------------------------------------------------------ * C D e e p K e y C o l l e c t i o n */ /*=================================================================== CDeepKeyCollection::CDeepKeyCollection Constructor Parameters: None Returns: Nothing ===================================================================*/ CDeepKeyCollection::CDeepKeyCollection() : m_tszBaseKey(NULL) { } /*=================================================================== CDeepKeyCollection::Init Constructor Parameters: pIMeta ATL Smart pointer to the metabase tszBaseKey Name of key to enumerate from Returns: E_OUTOFMEMORY if allocation fails S_OK on success ===================================================================*/ HRESULT CDeepKeyCollection::Init(const CComPtr &pIMeta, LPCTSTR tszBaseKey) { ASSERT(pIMeta.p != NULL); ASSERT_NULL_OR_STRING(tszBaseKey); m_pIMeta = pIMeta; // Copy tszBaseKey to m_tszBaseKey if (tszBaseKey == NULL) { // BaseKey is root m_tszBaseKey = NULL; } else { // Allocate and copy the passed string to the member string m_tszBaseKey = new TCHAR[_tcslen(tszBaseKey) + 1]; if (m_tszBaseKey == NULL) { return ::ReportError(E_OUTOFMEMORY); } _tcscpy(m_tszBaseKey, tszBaseKey); CannonizeKey(m_tszBaseKey); } return S_OK; } /*=================================================================== CDeepKeyCollection::~CDeepKeyCollection Destructor Parameters: None Returns: Nothing ===================================================================*/ CDeepKeyCollection::~CDeepKeyCollection() { if (m_tszBaseKey != NULL) delete m_tszBaseKey; } /*=================================================================== CDeepKeyCollection::InterfaceSupportsErrorInfo Standard ATL implementation ===================================================================*/ STDMETHODIMP CDeepKeyCollection::InterfaceSupportsErrorInfo(REFIID riid) { static const IID* arr[] = { &IID_IKeyCollection, }; for (int i=0;i count S_OK on success Notes: This method is slow. Deep enumerations are much faster. Might be able to do some hacking with a stack object and cached location to speed up sequential calls. ===================================================================*/ STDMETHODIMP CDeepKeyCollection::get_Item(long lIndex, BSTR *pbstrRetKey) { TRACE0("MetaUtil: CDeepKeyCollection::get_Item\n"); ASSERT_NULL_OR_POINTER(pbstrRetKey, BSTR); if ((lIndex <= 0) || (pbstrRetKey == NULL)) { return ::ReportError(E_INVALIDARG); } HRESULT hr; TCHAR tszRetKey[ADMINDATA_MAX_NAME_LEN]; long lCurIndex; lCurIndex = 1; tszRetKey[0] = _T('\0'); hr = IndexItem(NULL, lIndex, &lCurIndex, tszRetKey); if (hr == S_FALSE) { // Ran out of items before we found it return ::ReportError(ERROR_NO_MORE_ITEMS); } else if (hr == S_OK) { // Found it *pbstrRetKey = T2BSTR(tszRetKey); } else { return ::ReportError(hr); } return hr; } /*=================================================================== CDeepKeyCollection::get__NewEnum Get method for _NewEnum property. Returns an enumeration object for the subkeys. Parameters: ppIReturn [out, retval] Interface for the enumberation object Returns: E_INVALIDARG if ppIReturn == NULL S_OK on success ===================================================================*/ STDMETHODIMP CDeepKeyCollection::get__NewEnum(LPUNKNOWN * ppIReturn) { TRACE0("MetaUtil: CDeepKeyCollection::get__NewEnum\n"); ASSERT_NULL_OR_POINTER(ppIReturn, LPUNKNOWN); if (ppIReturn == NULL) { return ::ReportError(E_INVALIDARG); } HRESULT hr; // Create the deep key enumeration CComObject *pObj = NULL; ATLTRY(pObj = new CComObject); if (pObj == NULL) { return ::ReportError(E_OUTOFMEMORY); } hr = pObj->Init(m_pIMeta, m_tszBaseKey, NULL); if (FAILED(hr)) { return ::ReportError(hr); } // Set the interface to IUnknown hr = pObj->QueryInterface(IID_IUnknown, (void **) ppIReturn); if (FAILED(hr)) { return ::ReportError(hr); } ASSERT(*ppIReturn != NULL); return S_OK; } /*=================================================================== CDeepKeyCollection::Add Adds a key to the metabase relative to the collection's base key Parameters: bstrRelKey [in] Relative key to add Returns: E_INVALIDARG if bstrRelKey == NULL S_OK on success ===================================================================*/ STDMETHODIMP CDeepKeyCollection::Add(BSTR bstrRelKey) { TRACE0("MetaUtil: CDeepKeyCollection::Add\n"); ASSERT_NULL_OR_POINTER(bstrRelKey, OLECHAR); if (bstrRelKey == NULL) { return ::ReportError(E_INVALIDARG); } // Build the full key USES_CONVERSION; TCHAR tszFullKey[ADMINDATA_MAX_NAME_LEN]; if (m_tszBaseKey == NULL) { _tcscpy(tszFullKey, OLE2T(bstrRelKey)); } else { _tcscpy(tszFullKey, m_tszBaseKey); _tcscat(tszFullKey, _T("/")); _tcscat(tszFullKey, OLE2T(bstrRelKey)); } CannonizeKey(tszFullKey); return ::CreateKey(m_pIMeta, tszFullKey); } /*=================================================================== CDeepKeyCollection::Remove Removes a key from the metabase relative to the collection's base key Parameters: bstrRelKey [in] Relative key to remove Returns: E_INVALIDARG if bstrRelKey == NULL S_OK on success ===================================================================*/ STDMETHODIMP CDeepKeyCollection::Remove(BSTR bstrRelKey) { TRACE0("MetaUtil: CDeepKeyCollection::Remove\n"); ASSERT_NULL_OR_POINTER(bstrRelKey, OLECHAR); if (bstrRelKey == NULL) { return ::ReportError(E_INVALIDARG); } // Build the full key USES_CONVERSION; TCHAR tszFullKey[ADMINDATA_MAX_NAME_LEN]; if (m_tszBaseKey == NULL) { _tcscpy(tszFullKey, OLE2T(bstrRelKey)); } else { _tcscpy(tszFullKey, m_tszBaseKey); _tcscat(tszFullKey, _T("/")); _tcscat(tszFullKey, OLE2T(bstrRelKey)); } CannonizeKey(tszFullKey); return ::DeleteKey(m_pIMeta, tszFullKey); } /*=================================================================== CDeepKeyCollection::CountKeys Private, recursive method for counting keys Parameters: tszBaseKey [in] Key to begin counting with (but not to count) NULL can represent the root key. plNumKeys [out] Number of keys counter, not including the base Returns: S_OK on success ===================================================================*/ HRESULT CDeepKeyCollection::CountKeys(LPTSTR tszBaseKey, long *plNumKeys) { ASSERT_NULL_OR_STRING(tszBaseKey); ASSERT_POINTER(plNumKeys, long); *plNumKeys = 0; USES_CONVERSION; HRESULT hr; wchar_t wszSubKey[ADMINDATA_MAX_NAME_LEN]; wchar_t wszFullSubKey[ADMINDATA_MAX_NAME_LEN]; int iIndex; iIndex = 0; for (;;) { // FOREVER, will return from loop hr = m_pIMeta->EnumKeys(METADATA_MASTER_ROOT_HANDLE, T2W(tszBaseKey), wszSubKey, iIndex); if (FAILED(hr)) { if ((HRESULT_CODE(hr) == ERROR_NO_MORE_ITEMS) || (HRESULT_CODE(hr) == ERROR_PATH_NOT_FOUND)) { // Ran out of items, break return S_OK; } else { return ::ReportError(hr); } } else { // SUCCEEDED(hr) // Build the full subkey if ((tszBaseKey == NULL) || (tszBaseKey[0] == _T('\0')) ) { wcscpy(wszFullSubKey, wszSubKey); } else { wcscpy(wszFullSubKey, T2W(tszBaseKey)); wcscat(wszFullSubKey, L"/"); wcscat(wszFullSubKey, wszSubKey); } // Count this key (*plNumKeys)++; // Count the subkeys long lNumSubKeys; hr = CountKeys(W2T(wszFullSubKey), &lNumSubKeys); if (FAILED(hr)) { return hr; } (*plNumKeys) += lNumSubKeys; } iIndex++; } } /*=================================================================== CDeepKeyCollection::IndexItem Private, recursive method for indexing keys Parameters: tszRelKey Relative key to index from lDestIndex Destination index plCurIndex Current (working) index tszRet Result from search Returns: S_OK if the destination index was reached S_FALSE if the destination index was not reached ===================================================================*/ HRESULT CDeepKeyCollection::IndexItem(LPTSTR tszRelKey, long lDestIndex, long *plCurIndex, LPTSTR tszRet) { ASSERT_NULL_OR_STRING(tszRelKey); ASSERT_POINTER(plCurIndex, long); ASSERT_STRING(tszRet); USES_CONVERSION; HRESULT hr; wchar_t wszSubKey[ADMINDATA_MAX_NAME_LEN]; wchar_t wszRelSubKey[ADMINDATA_MAX_NAME_LEN]; int iIndex; // Open the base key METADATA_HANDLE hMDBaseKey; hr = m_pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE, T2W(m_tszBaseKey), METADATA_PERMISSION_READ, MUTIL_OPEN_KEY_TIMEOUT, &hMDBaseKey); if (FAILED(hr)) { return ::ReportError(hr); } iIndex = 0; for (;;) { // FOREVER, will return from loop hr = m_pIMeta->EnumKeys(hMDBaseKey, T2W(tszRelKey), wszSubKey, iIndex); if (FAILED(hr)) { m_pIMeta->CloseKey(hMDBaseKey); if ((HRESULT_CODE(hr) == ERROR_NO_MORE_ITEMS) || (HRESULT_CODE(hr) == ERROR_PATH_NOT_FOUND)) { // Ran out of items, break return S_FALSE; } else { return ::ReportError(hr); } } else { // Build the full subkey if ((tszRelKey == NULL) || (tszRelKey[0] == _T('\0')) ) { wcscpy(wszRelSubKey, wszSubKey); } else { wcscpy(wszRelSubKey, T2W(tszRelKey)); wcscat(wszRelSubKey, L"/"); wcscat(wszRelSubKey, wszSubKey); } // Is this the destination? if ((*plCurIndex) == lDestIndex) { //Found it, copy it to the return buffer _tcscpy(tszRet, W2T(wszRelSubKey)); m_pIMeta->CloseKey(hMDBaseKey); return S_OK; } // Count this key (*plCurIndex)++; // Check the subkeys hr = IndexItem(W2T(wszRelSubKey), lDestIndex, plCurIndex, tszRet); if (hr == S_OK) { //Found it m_pIMeta->CloseKey(hMDBaseKey); return S_OK; } else if (FAILED(hr)) { m_pIMeta->CloseKey(hMDBaseKey); return hr; } } iIndex++; } // Close the base key m_pIMeta->CloseKey(hMDBaseKey); return S_OK; } /*------------------------------------------------------------------ * C D e e p K e y E n u m */ /*=================================================================== CDeepKeyEnum::CDeepKeyEnum Constructor Parameters: None Returns: Nothing ===================================================================*/ CDeepKeyEnum::CDeepKeyEnum() : m_tszBaseKey(NULL), m_pCKeyStack(NULL) { } /*=================================================================== CDeepKeyEnum::Init Constructor Parameters: pIMeta ATL Smart pointer to the metabase tszBaseKey Name of key to enumerate from pKeyStack pointer to a stack containing the state to copy or NULL to start from the begining Returns: E_OUTOFMEMORY if allocation fails S_OK on success ===================================================================*/ HRESULT CDeepKeyEnum::Init(const CComPtr &pIMeta, LPCTSTR tszBaseKey, CKeyStack *pCKeyStack) { ASSERT(pIMeta.p != NULL); ASSERT_NULL_OR_STRING(tszBaseKey); ASSERT_NULL_OR_POINTER(pCKeyStack, CKeyStack); HRESULT hr; m_pIMeta = pIMeta; // Copy the base string if (tszBaseKey == NULL) { // BaseKey is root m_tszBaseKey = NULL; } else { // Allocate and copy the passed string to the member string m_tszBaseKey = new TCHAR[_tcslen(tszBaseKey) + 1]; if (m_tszBaseKey == NULL) { return ::ReportError(E_OUTOFMEMORY); } _tcscpy(m_tszBaseKey, tszBaseKey); CannonizeKey(m_tszBaseKey); } // Setup the stack if (pCKeyStack == NULL) { // Build a new stack CKeyStackNode *pCNew; m_pCKeyStack = new CKeyStack(); if (m_pCKeyStack == NULL) { return ::ReportError(E_OUTOFMEMORY); } // Create the first node pCNew = new CKeyStackNode(); if (pCNew == NULL) { delete m_pCKeyStack; m_pCKeyStack = NULL; return ::ReportError(E_OUTOFMEMORY); } hr = pCNew->Init(NULL, 0); if (FAILED(hr)) { delete m_pCKeyStack; m_pCKeyStack = NULL; return ::ReportError(E_OUTOFMEMORY); } // Put the first node onto the stack m_pCKeyStack->Push(pCNew); } else { // Clone the stack we were passed m_pCKeyStack = pCKeyStack->Clone(); if (m_pCKeyStack == NULL) { return ::ReportError(E_OUTOFMEMORY); } } return S_OK; } /*=================================================================== CDeepKeyEnum::~CDeepKeyEnum Destructor Parameters: None Returns: Nothing ===================================================================*/ CDeepKeyEnum::~CDeepKeyEnum() { if (m_tszBaseKey != NULL) { delete m_tszBaseKey; } if (m_pCKeyStack != NULL) { delete m_pCKeyStack; } } /*=================================================================== CDeepKeyEnum::Next Gets the next n items from the enumberation. Parameters: ulNumToGet [in] Number of elements to get rgvarDest [out] Array to put them in pulNumGot [out] If not NULL, number of elements rgvarDest got Returns: S_OK if outputs ulNumToGet items S_FALSE if outputs less than ulNumToGet items E_OUTOFMEMORY if allocation failed ===================================================================*/ STDMETHODIMP CDeepKeyEnum::Next(unsigned long ulNumToGet, VARIANT FAR* rgvarDest, unsigned long FAR* pulNumGot) { TRACE0("MetaUtil: CDeepKeyEnum::Next\n"); ASSERT_NULL_OR_POINTER(pulNumGot, unsigned long); // Make sure the array is big enough and we can write to it ASSERT((rgvarDest == NULL) || IsValidAddress(rgvarDest, ulNumToGet * sizeof(VARIANT), TRUE)); if (pulNumGot != NULL) { pulNumGot = 0; } USES_CONVERSION; HRESULT hr; unsigned int i; CKeyStackNode *pCKeyNode; CKeyStackNode *pCSubKeyNode; wchar_t wszSubKey[ADMINDATA_MAX_NAME_LEN]; wchar_t wszRelSubKey[ADMINDATA_MAX_NAME_LEN]; // Open the base key METADATA_HANDLE hMDBaseKey; hr = m_pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE, T2W(m_tszBaseKey), METADATA_PERMISSION_READ, MUTIL_OPEN_KEY_TIMEOUT, &hMDBaseKey); if (FAILED(hr)) { return ::ReportError(hr); } // For each element to retrive for (i=0; i < ulNumToGet; i++) { // Get a subkey do { // Pop a key off the stack pCKeyNode = m_pCKeyStack->Pop(); // if the stack is empty, we're done return S_FALSE if (pCKeyNode == NULL) { m_pIMeta->CloseKey(hMDBaseKey); if (pulNumGot != NULL) { *pulNumGot = i; } return S_FALSE; } // Attempt to Enum the next key hr = m_pIMeta->EnumKeys(hMDBaseKey, T2W(pCKeyNode->GetBaseKey()), wszSubKey, pCKeyNode->GetIndex()); // If failed delete the stack entry if (FAILED(hr)) { delete pCKeyNode; if ((HRESULT_CODE(hr) != ERROR_NO_MORE_ITEMS) && (HRESULT_CODE(hr) != ERROR_PATH_NOT_FOUND)) { // Got an unexpected Error m_pIMeta->CloseKey(hMDBaseKey); return ::ReportError(hr); } } } while (FAILED(hr)); // Build the relative subkey if ((pCKeyNode->GetBaseKey() == NULL) || ((pCKeyNode->GetBaseKey())[0] == _T('\0')) ) { wcscpy(wszRelSubKey, wszSubKey); } else { wcscpy(wszRelSubKey, T2W(pCKeyNode->GetBaseKey())); wcscat(wszRelSubKey, L"/"); wcscat(wszRelSubKey, wszSubKey); } // Output the relative subkey VariantInit(&(rgvarDest[i])); rgvarDest[i].vt = VT_BSTR; rgvarDest[i].bstrVal = W2BSTR(wszRelSubKey); // Increment the key index pCKeyNode->SetIndex(pCKeyNode->GetIndex() + 1); // Push the key back onto the stack m_pCKeyStack->Push(pCKeyNode); // Create a stack node for the subkey pCSubKeyNode = new CKeyStackNode(); if (pCSubKeyNode == NULL) { m_pIMeta->CloseKey(hMDBaseKey); return ::ReportError(E_OUTOFMEMORY); } hr = pCSubKeyNode->Init(W2T(wszRelSubKey), 0); if (FAILED(hr)) { m_pIMeta->CloseKey(hMDBaseKey); return ::ReportError(hr); } // Push the subkey onto the stack m_pCKeyStack->Push(pCSubKeyNode); } // Close the base key m_pIMeta->CloseKey(hMDBaseKey); if (pulNumGot != NULL) { *pulNumGot = i; } return S_OK; } /*=================================================================== CDeepKeyEnum::Skip Skips the next n items in an enumeration Parameters: ulNumToSkip [in] Number of elements to skip Returns: S_OK if outputs ulNumToGet items E_OUTOFMEMORY if allocation failed ===================================================================*/ STDMETHODIMP CDeepKeyEnum::Skip(unsigned long ulNumToSkip) { TRACE0("MetaUtil: CDeepKeyEnum::Skip\n"); USES_CONVERSION; HRESULT hr; unsigned long i; CKeyStackNode *pCKeyNode; CKeyStackNode *pCSubKeyNode; wchar_t wszSubKey[ADMINDATA_MAX_NAME_LEN]; wchar_t wszRelSubKey[ADMINDATA_MAX_NAME_LEN]; // Open the base key METADATA_HANDLE hMDBaseKey; hr = m_pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE, T2W(m_tszBaseKey), METADATA_PERMISSION_READ, MUTIL_OPEN_KEY_TIMEOUT, &hMDBaseKey); if (FAILED(hr)) { return ::ReportError(hr); } // For each element to stip for (i=0; i < ulNumToSkip; i++) { // Get a subkey do { // Pop a key off the stack pCKeyNode = m_pCKeyStack->Pop(); // if the stack is empty, we're done return S_OK if (pCKeyNode == NULL) { m_pIMeta->CloseKey(hMDBaseKey); return S_OK; } // Attempt to Enum the next key hr = m_pIMeta->EnumKeys(METADATA_MASTER_ROOT_HANDLE, T2W(pCKeyNode->GetBaseKey()), wszSubKey, pCKeyNode->GetIndex()); // If failed delete the stack entry if (FAILED(hr)) { delete pCKeyNode; if ((HRESULT_CODE(hr) != ERROR_NO_MORE_ITEMS) && (HRESULT_CODE(hr) != ERROR_PATH_NOT_FOUND)) { // Got an unexpected Error m_pIMeta->CloseKey(hMDBaseKey); return ::ReportError(hr); } } } while (FAILED(hr)); // Build the relative subkey if ((pCKeyNode->GetBaseKey() == NULL) || ((pCKeyNode->GetBaseKey())[0] == _T('\0')) ) { wcscpy(wszRelSubKey, wszSubKey); } else { wcscpy(wszRelSubKey, T2W(pCKeyNode->GetBaseKey())); wcscat(wszRelSubKey, L"/"); wcscat(wszRelSubKey, wszSubKey); } // Increment the key index pCKeyNode->SetIndex(pCKeyNode->GetIndex() + 1); // Push the key back on the stack m_pCKeyStack->Push(pCKeyNode); // Create a stack node for the subkey pCSubKeyNode = new CKeyStackNode(); if (pCSubKeyNode == NULL) { m_pIMeta->CloseKey(hMDBaseKey); return ::ReportError(E_OUTOFMEMORY); } hr = pCSubKeyNode->Init(W2T(wszRelSubKey), 0); if (FAILED(hr)) { m_pIMeta->CloseKey(hMDBaseKey); return ::ReportError(hr); } // Push the subkey onto the stack m_pCKeyStack->Push(pCSubKeyNode); } // Close the base key m_pIMeta->CloseKey(hMDBaseKey); return S_OK; } /*=================================================================== CDeepKeyEnum::Reset Rests the enumeration to the first item Parameters: None Returns: E_OUTOFMEMORY if not enough memory to build a new stack S_OK on success ===================================================================*/ STDMETHODIMP CDeepKeyEnum::Reset() { TRACE0("MetaUtil: CDeepKeyEnum::Reset\n"); HRESULT hr; // Build a new stack (if this fails we still have the old stack) CKeyStack *pCNewStack; CKeyStackNode *pCNewNode; pCNewStack = new CKeyStack(); if (pCNewStack == NULL) { return ::ReportError(E_OUTOFMEMORY); } // Create the first node pCNewNode = new CKeyStackNode(); if (pCNewNode == NULL) { delete pCNewStack; return ::ReportError(E_OUTOFMEMORY); } hr = pCNewNode->Init(NULL, 0); if (FAILED(hr)) { delete pCNewStack; return ::ReportError(E_OUTOFMEMORY); } // Put the first node onto the new stack pCNewStack->Push(pCNewNode); // Replace the old stack delete m_pCKeyStack; m_pCKeyStack = pCNewStack; return S_OK; } /*=================================================================== CDeepKeyEnum::Clone Gets an interface pointer to a copy of the enumeration at its current state. Parameters: ppIReturn [out] Pointer to interface for copy Returns: E_INVALIDARG if ppIReturn == NULL E_OUTOFMEMORY if not enough memory to create clone S_OK on success ===================================================================*/ STDMETHODIMP CDeepKeyEnum::Clone(IEnumVARIANT FAR* FAR* ppIReturn) { TRACE0("MetaUtil: CDeepKeyEnum::Clone\n"); ASSERT_NULL_OR_POINTER(ppIReturn, LPUNKNOWN); if (ppIReturn == NULL) { return ::ReportError(E_INVALIDARG); } HRESULT hr; // Create a copy of the enumeration CComObject *pObj = NULL; ATLTRY(pObj = new CComObject); if (pObj == NULL) { return ::ReportError(E_OUTOFMEMORY); } hr = pObj->Init(m_pIMeta, m_tszBaseKey, m_pCKeyStack); if (FAILED(hr)) { return ::ReportError(hr); } // Set the interface to IEnumVARIANT hr = pObj->QueryInterface(IID_IEnumVARIANT, (void **) ppIReturn); if (FAILED(hr)) { return ::ReportError(hr); } ASSERT(*ppIReturn != NULL); return S_OK; }