/*--------------------------------------------------------------------------* * * 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 #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(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(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 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 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(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)); }