windows-nt/Source/XPSP1/NT/inetsrv/iis/utils/metautil/keycol.cpp

1730 lines
39 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*===================================================================
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<IMSAdminBase> &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;i<sizeof(arr)/sizeof(arr[0]);i++)
{
if (InlineIsEqualGUID(*arr[i],riid))
return S_OK;
}
return S_FALSE;
}
/*===================================================================
CFlatKeyCollection::get_Count
Get method for Count property. Counts the number of subkeys
Parameters:
plReturn [out, retval] Value to return to client.
Returns:
E_INVALIDARG if plReturn == NULL
S_OK on success
Notes:
Actually counts all of the subkeys. Do not call in a loop!
===================================================================*/
STDMETHODIMP CFlatKeyCollection::get_Count(long * plReturn)
{
TRACE0("MetaUtil: CFlatKeyCollection::get_Count\n");
ASSERT_NULL_OR_POINTER(plReturn, long);
if (plReturn == NULL) {
return ::ReportError(E_INVALIDARG);
}
USES_CONVERSION;
HRESULT hr;
*plReturn = 0;
// Count the subkeys
wchar_t wszSubKey[ADMINDATA_MAX_NAME_LEN];
int iIndex;
iIndex = 0;
for (;;) { // FOREVER, will return from loop
hr = m_pIMeta->EnumKeys(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<CFlatKeyEnum> *pObj = NULL;
ATLTRY(pObj = new CComObject<CFlatKeyEnum>);
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<IMSAdminBase> &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<CFlatKeyEnum> *pObj = NULL;
ATLTRY(pObj = new CComObject<CFlatKeyEnum>);
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<IMSAdminBase> &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<sizeof(arr)/sizeof(arr[0]);i++)
{
if (InlineIsEqualGUID(*arr[i],riid))
return S_OK;
}
return S_FALSE;
}
/*===================================================================
CDeepKeyCollection::get_Count
Get method for Count property. Counts the number of subkeys
Parameters:
plReturn [out, retval] Value to return to client.
Returns:
E_INVALIDARG if pVal == NULL
S_OK on success
Notes:
Actually counts all of the subkeys recursivly. Very slow, do
not put in a loop!
===================================================================*/
STDMETHODIMP CDeepKeyCollection::get_Count(long * pVal)
{
TRACE0("MetaUtil: CDeepKeyCollection::get_Count\n");
ASSERT_NULL_OR_POINTER(pVal, long);
if (pVal == NULL) {
return ::ReportError(E_INVALIDARG);
}
HRESULT hr;
hr = CountKeys(m_tszBaseKey, pVal);
return hr;
}
/*===================================================================
CDeepKeyCollection::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] Interface for the enumberation object
Returns:
E_INVALIDARG if lIndex <= 0 or pbstrRetKey == NULL
ERROR_NO_MORE_ITEMS if index is > 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<CDeepKeyEnum> *pObj = NULL;
ATLTRY(pObj = new CComObject<CDeepKeyEnum>);
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<IMSAdminBase> &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<CDeepKeyEnum> *pObj = NULL;
ATLTRY(pObj = new CComObject<CDeepKeyEnum>);
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;
}