|
|
/*--------------------------------------------------------------------------*
* * Microsoft Windows * Copyright (C) Microsoft Corporation, 1992 - 1999 * * File: nodepath.h * * Contents: Dynamic node path generation helpers * * History: 31-Mar-98 JeffRo Created * * * The persisted format of a node path (aka bookmark) is as follows: * * DWORD idStatic; // the MTNODEID of the static root of the node
* DWORD cDynamicBytes; // count of bytes in the dynamic portion of the
* // bookmark
* BYTE rgDynamicBytes[]; // array of bytes representing the dynamic
* // portion of the bookmark
* * * For MMC v1.0 consoles, rgDynamicBytes is a double-NULL terminated list * of Unicode strings representing the names of the dynamic nodes. For a * tree that looks like this: * * Static Node (id == 3) * Dynamic Node1 * Dynamic Node2 * * the full bookmark for Dynamic Node 2 would be: * * 00000000 03 00 00 00 00 00 00 00 44 00 79 00 6E 00 61 00 ........D.y.n.a. * 00000010 6D 00 69 00 63 00 20 00 4E 00 6F 00 64 00 65 00 m.i.c. .N.o.d.e. * 00000020 31 00 00 00 44 00 79 00 6E 00 61 00 6D 00 69 00 1...D.y.n.a.m.i. * 00000030 63 00 20 00 4E 00 6F 00 64 00 65 00 32 00 00 00 c. .N.o.d.e.2... * 00000040 00 00 .. * * * For MMC v1.1 and higher consoles, rgDynamic looks like this: * * BYTE rgSignature[16]; // "MMCCustomStream"
* DWORD dwStreamVersion; // version number, currently 0x0100
* * followed by 1 or more dynamic node IDs, each of which looks like: * * BYTE byNodeIDType; // NDTYP_STRING (0x01) means the snap-in
* // did not support CCF_NODEID(2) and the
* // ID is the NULL-terminated Unicode name
* // of the node
* // NDTYP_CUSTOM (0x02) means the snap-in
* // supported CCF_NODEID(2) and what follows
* // is the SNodeID structure
* * BYTE rgNodeIDBytes[]; // bytes for the dynamic node ID
* * * For a tree that looks like this: * * Static Node (id == 3) * Dynamic Node1 (doesn't support CCF_NODEID(2)) * Dynamic Node2 (suports CCF_NODEID, returns DWORD 0x12345678) * * the full bookmark for Dynamic Node 2 would be: * * 00000000 03 00 00 00 3A 00 00 00 4D 4D 43 43 75 73 74 6F ....:...MMCCusto * 00000010 6D 53 74 72 65 61 6D 00 00 00 01 00 01 44 00 79 mStream......D.y * 00000020 00 6E 00 61 00 6D 00 69 00 63 00 20 00 4E 00 6F .n.a.m.i.c. .N.o * 00000030 00 64 00 65 00 31 00 00 00 02 04 00 00 00 78 56 .d.e.1........xV * 00000040 34 12 4. *--------------------------------------------------------------------------*/
#include "stdafx.h"
#include "nodepath.h"
#include <comdef.h>
#include "nmtempl.h"
#include "conview.h"
using namespace std;
/*+-------------------------------------------------------------------------*
* class CDynamicPathEntryEx * * * PURPOSE: Adds functionality (but NO MEMBER VARIABLES) to the * CDynamicPathEntry class, to initialize from an MTNode. * *+-------------------------------------------------------------------------*/ class CDynamicPathEntryEx : public CDynamicPathEntry { typedef CDynamicPathEntry BC; public: // initialize from a node.
SC ScInitialize(CMTNode *pMTNode, bool bFastRetrievalOnly);
// assignment
CDynamicPathEntryEx & operator = (const CDynamicPathEntryEx &rhs) { BC::operator =(rhs); return *this; }
CDynamicPathEntryEx & operator = (const CDynamicPathEntry &rhs) { BC::operator =(rhs); return *this; }
private: CLIPFORMAT GetCustomNodeIDCF (); CLIPFORMAT GetCustomNodeID2CF(); };
/*+-------------------------------------------------------------------------*
* * CDynamicPathEntryEx::ScInitialize * * PURPOSE: Initializes the CDynamicPathEntryEx structure from the given MTNode. * * This handles all the backward compatibility cases. Refer to the * SDK docs to see how CCF_NODEID and CCF_NODEID2 are handled. * * PARAMETERS: * CMTNode* pMTNode : * DWORD dwFlags : * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC CDynamicPathEntryEx::ScInitialize(CMTNode* pMTNode, bool bFastRetrievalOnly) { DECLARE_SC(sc, TEXT("CDynamicPathEntryEx::ScInitialize")); USES_CONVERSION;
bool bUseDisplayName = true;
// get the data object for the node
IDataObjectPtr spDataObject; sc = pMTNode->QueryDataObject (CCT_SCOPE, &spDataObject); if(sc) return sc;
// extract the CCF_NODEID2 format from the data object
HGLOBAL hGlobal = NULL; sc = DataObject_GetHGLOBALData (spDataObject, GetCustomNodeID2CF(), &hGlobal); if(!sc.IsError()) // succeeded
{ // build the DynamicNodeID from the custom node ID struct
SNodeID2 *pNodeID2 = reinterpret_cast<SNodeID2*>(GlobalLock (hGlobal)); sc = ScCheckPointers(pNodeID2); if(sc) return sc;
// if the client needs a fast path ONLY but the snapin can't provide one,
// return E_INVALIDARG;
// Bug 175684: this is a "valid" error return, so we don't want to trace it
if ( ( (pNodeID2->dwFlags & MMC_NODEID_SLOW_RETRIEVAL) && bFastRetrievalOnly) || ( pNodeID2->cBytes <= 0) ) { SC scNoTrace = E_INVALIDARG; return (scNoTrace); }
m_byteVector.insert (m_byteVector.end(), pNodeID2->id, pNodeID2->id + pNodeID2->cBytes);
m_type = NDTYP_CUSTOM; bUseDisplayName = false; } else // the CCF_NODEID2 format was not supported. Try CCF_NODEID.
{ sc = DataObject_GetHGLOBALData (spDataObject, GetCustomNodeIDCF(), &hGlobal); if(!sc) { // build the DynamicNodeID from the custom node ID struct
m_type = NDTYP_CUSTOM; SNodeID *pNodeID = reinterpret_cast<SNodeID*>(GlobalLock (hGlobal));
sc = ScCheckPointers(pNodeID); if(sc) return sc;
// if pNodeID->cBytes is zero, this is a legacy indication that the
// node does not support fast retrieval. But, if the client is OK
// with slow retrieval, we supply the display name instead.
if(pNodeID->cBytes != 0) { m_byteVector.insert (m_byteVector.end(), pNodeID->id, pNodeID->id + pNodeID->cBytes); bUseDisplayName = false; } else { // cBytes == 0 here. If the client indicated fast retrieval, must return an error.
// Bug 175684: this is a "valid" error return, so we don't want to trace it
if(bFastRetrievalOnly) { SC scNoTrace = E_INVALIDARG; return (scNoTrace); } else bUseDisplayName = true; // must use the display name here.
} } };
// let go of the data
if (hGlobal) { GlobalUnlock (hGlobal); GlobalFree (hGlobal); } // no custom ID; persist the node name, for compatibility
// if CCF_NODEID was presented with a zero count, also use the node name.
if (bUseDisplayName) { sc.Clear(); m_type = NDTYP_STRING;
tstring strName = pMTNode->GetDisplayName(); if (!strName.empty()) m_strEntry = T2CW(strName.data()); else return (sc = E_INVALIDARG);
}
return sc; }
/*--------------------------------------------------------------------------*
* CDynamicPathEntryEx::GetCustomNodeIDCF * * *--------------------------------------------------------------------------*/
CLIPFORMAT CDynamicPathEntryEx::GetCustomNodeIDCF() { static CLIPFORMAT cfCustomNodeID = 0;
if (cfCustomNodeID == 0) { USES_CONVERSION; cfCustomNodeID = (CLIPFORMAT) RegisterClipboardFormat (W2T (CCF_NODEID)); ASSERT (cfCustomNodeID != 0); }
return (cfCustomNodeID); }
/*--------------------------------------------------------------------------*
* CDynamicPathEntryEx::GetCustomNodeID2CF * * *--------------------------------------------------------------------------*/
CLIPFORMAT CDynamicPathEntryEx::GetCustomNodeID2CF() { static CLIPFORMAT cfCustomNodeID2 = 0;
if (cfCustomNodeID2 == 0) { USES_CONVERSION; cfCustomNodeID2 = (CLIPFORMAT) RegisterClipboardFormat (W2T (CCF_NODEID2)); ASSERT (cfCustomNodeID2 != 0); }
return (cfCustomNodeID2); }
//############################################################################
//############################################################################
//
// Implementation of class CBookmarkEx
//
//############################################################################
//############################################################################
CBookmarkEx::CBookmarkEx(MTNODEID idStatic) : BC(idStatic) { }
CBookmarkEx::CBookmarkEx(bool bIsFastBookmark) : BC(bIsFastBookmark) { }
/*+-------------------------------------------------------------------------*
* CBookmarkEx::~CBookmarkEx * * PURPOSE: Destructor * * PARAMETERS: * * RETURNS: * /*+-------------------------------------------------------------------------*/ CBookmarkEx::~CBookmarkEx() { }
/*+-------------------------------------------------------------------------*
* * CBookmarkEx::ScRetarget * * PURPOSE: * * PARAMETERS: * CMTNode * pMTNode : * bool bFastRetrievalOnly : * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC CBookmarkEx::ScRetarget(CMTNode *pMTNode, bool bFastRetrievalOnly) { DECLARE_SC(sc, TEXT("CBookmarkEx::ScRetarget"));
if(pMTNode) { sc = ScInitialize (pMTNode, NULL, bFastRetrievalOnly); if(sc) return sc; } else Reset();
return sc; }
/*+-------------------------------------------------------------------------*
* * CBookmarkEx::ResetUI * * PURPOSE: Reset's the state of the bookmark. Will put up the Retry/Cancel * UI if the node is not available. * * RETURNS: * void * *+-------------------------------------------------------------------------*/ void CBookmarkEx::ResetUI() { }
/* CBookmarkEx::GetNode
* * PURPOSE: Returns a CNode corresponding to the bookmark for a particular view. * NOTE: This will return a new instance every time. To reuse the same * instance, cache it. * * PARAMETERS: * CViewData * pViewData: The view for which a node is requested * * RETURNS: * CNode * : NULL if the MTNode could not be found. */ std::auto_ptr<CNode> CBookmarkEx::GetNode(CViewData *pViewData) { DECLARE_SC(sc, TEXT("CBookmarkEx::GetNode"));
CNode * pNode = NULL; CMTNode * pMTNode = NULL;
// The NULL return value for failure conditions.
std::auto_ptr<CNode> spNodeNull;
// validate parameters
if(NULL == pViewData) { sc = E_UNEXPECTED; return spNodeNull; }
// Get the target node.
bool bExactMatchFound = false; // out value from ScGetMTNode, unused
sc = ScGetMTNode(true, &pMTNode, bExactMatchFound); if( sc.IsError() || !pMTNode) // could not find the node - abort.
{ return spNodeNull; }
// make sure the node is expanded (invisibly) in the tree
CConsoleView* pConsoleView = pViewData->GetConsoleView(); if (pConsoleView == NULL) { sc = E_UNEXPECTED; return spNodeNull; }
sc = pConsoleView->ScExpandNode (pMTNode->GetID(), true, false); if(sc) return spNodeNull;
pNode = pMTNode->GetNode(pViewData, false); if (pNode == NULL) { sc = E_OUTOFMEMORY; return spNodeNull; }
if (FAILED (pNode->InitComponents())) { delete pNode; sc = E_UNEXPECTED; return spNodeNull; }
return (std::auto_ptr<CNode>(pNode)); }
/*+-------------------------------------------------------------------------*
* * CBookmarkEx::ScInitialize * * PURPOSE: * * PARAMETERS: * CMTNode* pMTNode : * CMTNode* pMTViewRootNode : * bool bFastRetrievalOnly : true by default. If true, this * function returns E_INVALIDARG for * any node that cannot be quickly * retrieved. * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC CBookmarkEx::ScInitialize (CMTNode* pMTNode, CMTNode* pMTViewRootNode, bool bFastRetrievalOnly) { DECLARE_SC(sc, TEXT("CBookmarkEx::ScInitialize"));
// check pointers
sc = ScCheckPointers(pMTNode); if(sc) return sc;
BC::Reset();
// 1. Get the static node ID
m_idStatic = pMTNode->GetID();
// If this is a static node, we're done.
if(pMTNode->IsStaticNode()) return sc;
bool fPassedViewRootNode = false;
// get a descriptor for each dynamic node at the end of the branch
// (from leaf to root)
while ((pMTNode != NULL) && !pMTNode->IsStaticNode()) { CDynamicPathEntryEx entry;
sc = entry.ScInitialize(pMTNode, bFastRetrievalOnly); if(sc.IsError() && !(sc == E_INVALIDARG) ) // E_INVALIDARG means that fast retrieval was not available.
return sc;
if(sc) // must be E_INVALIDARG
{ // if the node's data object gave us an empty custom node ID,
// we don't want to persist this node or any below it, so purge the list
m_dynamicPath.clear(); // clear the path
sc.Clear(); } // otherwise, put it this node ID on the front of the list
else m_dynamicPath.push_front (entry);
/*
* remember if we've passed the node at the root of our view * on our way up the tree to the first static node */ if (pMTNode == pMTViewRootNode) fPassedViewRootNode = true;
/*
* If we've passed the view's root node and the list is empty, it means * that a node between the view's root node and the first static node * (specifically, this one) supports CCF_NODEID and has requested * that the node not be persisted. If a node isn't persisted, * nothing below it is persisted, either, so we can bail out. */ if (fPassedViewRootNode && m_dynamicPath.empty()) break;
pMTNode = pMTNode->Parent(); }
// assume success
sc.Clear(); if(!pMTNode || !pMTNode->IsStaticNode()) return (sc = E_UNEXPECTED);
// Get the static node ID of the static parent
m_idStatic = pMTNode->GetID();
// if we don't have a dynamic node path, return so
if (m_dynamicPath.empty ()) return (sc = S_FALSE);
// if we hit the root before we hit a static node, we have an error
if (pMTNode == NULL) sc = E_FAIL;
return sc;
}
/*+-------------------------------------------------------------------------*
* * CBookmarkEx::ScGetMTNode * * PURPOSE: Returns the MTNode of the node with the given path relative to * the static node. * * PARAMETERS: * bool bExactMatchRequired: [IN] Do we need exact match? * CMTNode ** ppMTNode : [OUT]: The MTNode, if found. * bool bExactMatchFound : [OUT] Did we find exact match? * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC CBookmarkEx::ScGetMTNode(bool bExactMatchRequired, CMTNode **ppMTNode, bool& bExactMatchFound) { DECLARE_SC(sc, TEXT("CBookmarkEx::ScGetMTNode"));
// check parameters
sc = ScCheckPointers(ppMTNode); if(sc) return sc;
// init out param
*ppMTNode = NULL; bExactMatchFound = false;
CScopeTree *pScopeTree = CScopeTree::GetScopeTree(); if(!pScopeTree) { sc = E_POINTER; return sc; }
if (m_idStatic == ID_ConsoleRoot) { sc = ScRetarget (pScopeTree->GetRoot(), true /*bFastRetrievalOnly*/); if(sc) return sc; }
// find the MTNode of the static node closest to the required node.
CMTNode* pMTNode = NULL; sc = pScopeTree->Find(m_idStatic, &pMTNode); if(sc) return sc;
sc = ScCheckPointers(pMTNode); if(sc) return sc;
*ppMTNode = pMTNode; // initialize
CDynamicPath::iterator iter;
for(iter = m_dynamicPath.begin(); iter != m_dynamicPath.end(); iter++) { CDynamicPathEntryEx entry;
entry = *iter;
// check the next segment of the path.
sc = ScFindMatchingMTNode(pMTNode, entry, ppMTNode);
// handle the special case of the node not being found but an exact match
// not being needed. In this case, we use the closest ancestor node that
// was available.
if ( (sc == ScFromMMC(IDS_NODE_NOT_FOUND)) && !bExactMatchRequired ) { // set the output.
*ppMTNode = pMTNode; sc.Clear(); return sc; }
// bail on all other errors
if(sc) return sc;
sc = ScCheckPointers(*ppMTNode); if(sc) return sc;
pMTNode = *ppMTNode; //prime the MTNode for the next round, if there is one.
}
// we've found a match if we ran out of entries.
bExactMatchFound = (iter == m_dynamicPath.end());
if(bExactMatchRequired && !bExactMatchFound) // could not find the exact node.
{ *ppMTNode = NULL; return (sc = ScFromMMC(IDS_NODE_NOT_FOUND)); }
// a NULL pMTNode is not an error, we just need to make sure that
// nodes are initialized before use.
if ((pMTNode != NULL) && !pMTNode->IsInitialized() ) { sc = pMTNode->Init(); if(sc) sc.TraceAndClear(); // does not invalidate the locating operation
}
return sc; }
/*+-------------------------------------------------------------------------*
* * CBookmarkEx::ScFindMatchingMTNode * * PURPOSE: Finds the first child node directly beneath the given parent node * whose node ID (ie one of CCF_NODEID2, CCF_NODEID, or the display * name) matches the specified CDynamicPathEntryEx object. * * PARAMETERS: * CMTNode * pMTNodeParent : * CDynamicPathEntryEx & entry : * CMTNode ** ppMatchingMTNode : [OUT]: The child node, if found. * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ SC CBookmarkEx::ScFindMatchingMTNode(CMTNode *pMTNodeParent, CDynamicPathEntryEx &entry, CMTNode **ppMatchingMTNode) { DECLARE_SC(sc, TEXT("CBookmarkEx::ScFindMatchingMTNode"));
sc = ScCheckPointers(pMTNodeParent, ppMatchingMTNode); if(sc) return sc;
*ppMatchingMTNode = NULL; // initialize
// expand the parent node if not already done so.
if (pMTNodeParent->WasExpandedAtLeastOnce() == FALSE) { sc = pMTNodeParent->Expand(); if(sc) return sc; }
// see if any of the children of this node match the next segment of the stored path
for (CMTNode *pMTNode = pMTNodeParent->Child(); pMTNode != NULL; pMTNode = pMTNode->Next()) { CDynamicPathEntryEx entryTemp; sc = entryTemp.ScInitialize(pMTNode, false /*bFastRetrievalOnly :
at this point, we know the node is created, so we don't care about retrieval speed*/); if(sc) return sc;
if(entryTemp == entry) // found it.
{ *ppMatchingMTNode = pMTNode; return sc; } }
// could not find the node.
return (sc = ScFromMMC(IDS_NODE_NOT_FOUND)); }
|