Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

695 lines
20 KiB

  1. /*--------------------------------------------------------------------------*
  2. *
  3. * Microsoft Windows
  4. * Copyright (C) Microsoft Corporation, 1992 - 1999
  5. *
  6. * File: nodepath.h
  7. *
  8. * Contents: Dynamic node path generation helpers
  9. *
  10. * History: 31-Mar-98 JeffRo Created
  11. *
  12. *
  13. * The persisted format of a node path (aka bookmark) is as follows:
  14. *
  15. * DWORD idStatic; // the MTNODEID of the static root of the node
  16. * DWORD cDynamicBytes; // count of bytes in the dynamic portion of the
  17. * // bookmark
  18. * BYTE rgDynamicBytes[]; // array of bytes representing the dynamic
  19. * // portion of the bookmark
  20. *
  21. *
  22. * For MMC v1.0 consoles, rgDynamicBytes is a double-NULL terminated list
  23. * of Unicode strings representing the names of the dynamic nodes. For a
  24. * tree that looks like this:
  25. *
  26. * Static Node (id == 3)
  27. * Dynamic Node1
  28. * Dynamic Node2
  29. *
  30. * the full bookmark for Dynamic Node 2 would be:
  31. *
  32. * 00000000 03 00 00 00 00 00 00 00 44 00 79 00 6E 00 61 00 ........D.y.n.a.
  33. * 00000010 6D 00 69 00 63 00 20 00 4E 00 6F 00 64 00 65 00 m.i.c. .N.o.d.e.
  34. * 00000020 31 00 00 00 44 00 79 00 6E 00 61 00 6D 00 69 00 1...D.y.n.a.m.i.
  35. * 00000030 63 00 20 00 4E 00 6F 00 64 00 65 00 32 00 00 00 c. .N.o.d.e.2...
  36. * 00000040 00 00 ..
  37. *
  38. *
  39. * For MMC v1.1 and higher consoles, rgDynamic looks like this:
  40. *
  41. * BYTE rgSignature[16]; // "MMCCustomStream"
  42. * DWORD dwStreamVersion; // version number, currently 0x0100
  43. *
  44. * followed by 1 or more dynamic node IDs, each of which looks like:
  45. *
  46. * BYTE byNodeIDType; // NDTYP_STRING (0x01) means the snap-in
  47. * // did not support CCF_NODEID(2) and the
  48. * // ID is the NULL-terminated Unicode name
  49. * // of the node
  50. * // NDTYP_CUSTOM (0x02) means the snap-in
  51. * // supported CCF_NODEID(2) and what follows
  52. * // is the SNodeID structure
  53. *
  54. * BYTE rgNodeIDBytes[]; // bytes for the dynamic node ID
  55. *
  56. *
  57. * For a tree that looks like this:
  58. *
  59. * Static Node (id == 3)
  60. * Dynamic Node1 (doesn't support CCF_NODEID(2))
  61. * Dynamic Node2 (suports CCF_NODEID, returns DWORD 0x12345678)
  62. *
  63. * the full bookmark for Dynamic Node 2 would be:
  64. *
  65. * 00000000 03 00 00 00 3A 00 00 00 4D 4D 43 43 75 73 74 6F ....:...MMCCusto
  66. * 00000010 6D 53 74 72 65 61 6D 00 00 00 01 00 01 44 00 79 mStream......D.y
  67. * 00000020 00 6E 00 61 00 6D 00 69 00 63 00 20 00 4E 00 6F .n.a.m.i.c. .N.o
  68. * 00000030 00 64 00 65 00 31 00 00 00 02 04 00 00 00 78 56 .d.e.1........xV
  69. * 00000040 34 12 4.
  70. *--------------------------------------------------------------------------*/
  71. #include "stdafx.h"
  72. #include "nodepath.h"
  73. #include <comdef.h>
  74. #include "nmtempl.h"
  75. #include "conview.h"
  76. using namespace std;
  77. /*+-------------------------------------------------------------------------*
  78. * class CDynamicPathEntryEx
  79. *
  80. *
  81. * PURPOSE: Adds functionality (but NO MEMBER VARIABLES) to the
  82. * CDynamicPathEntry class, to initialize from an MTNode.
  83. *
  84. *+-------------------------------------------------------------------------*/
  85. class CDynamicPathEntryEx : public CDynamicPathEntry
  86. {
  87. typedef CDynamicPathEntry BC;
  88. public:
  89. // initialize from a node.
  90. SC ScInitialize(CMTNode *pMTNode, bool bFastRetrievalOnly);
  91. // assignment
  92. CDynamicPathEntryEx & operator = (const CDynamicPathEntryEx &rhs)
  93. {
  94. BC::operator =(rhs);
  95. return *this;
  96. }
  97. CDynamicPathEntryEx & operator = (const CDynamicPathEntry &rhs)
  98. {
  99. BC::operator =(rhs);
  100. return *this;
  101. }
  102. private:
  103. CLIPFORMAT GetCustomNodeIDCF ();
  104. CLIPFORMAT GetCustomNodeID2CF();
  105. };
  106. /*+-------------------------------------------------------------------------*
  107. *
  108. * CDynamicPathEntryEx::ScInitialize
  109. *
  110. * PURPOSE: Initializes the CDynamicPathEntryEx structure from the given MTNode.
  111. *
  112. * This handles all the backward compatibility cases. Refer to the
  113. * SDK docs to see how CCF_NODEID and CCF_NODEID2 are handled.
  114. *
  115. * PARAMETERS:
  116. * CMTNode* pMTNode :
  117. * DWORD dwFlags :
  118. *
  119. * RETURNS:
  120. * SC
  121. *
  122. *+-------------------------------------------------------------------------*/
  123. SC
  124. CDynamicPathEntryEx::ScInitialize(CMTNode* pMTNode, bool bFastRetrievalOnly)
  125. {
  126. DECLARE_SC(sc, TEXT("CDynamicPathEntryEx::ScInitialize"));
  127. USES_CONVERSION;
  128. bool bUseDisplayName = true;
  129. // get the data object for the node
  130. IDataObjectPtr spDataObject;
  131. sc = pMTNode->QueryDataObject (CCT_SCOPE, &spDataObject);
  132. if(sc)
  133. return sc;
  134. // extract the CCF_NODEID2 format from the data object
  135. HGLOBAL hGlobal = NULL;
  136. sc = DataObject_GetHGLOBALData (spDataObject, GetCustomNodeID2CF(), &hGlobal);
  137. if(!sc.IsError()) // succeeded
  138. {
  139. // build the DynamicNodeID from the custom node ID struct
  140. SNodeID2 *pNodeID2 = reinterpret_cast<SNodeID2*>(GlobalLock (hGlobal));
  141. sc = ScCheckPointers(pNodeID2);
  142. if(sc)
  143. return sc;
  144. // if the client needs a fast path ONLY but the snapin can't provide one,
  145. // return E_INVALIDARG;
  146. // Bug 175684: this is a "valid" error return, so we don't want to trace it
  147. if ( ( (pNodeID2->dwFlags & MMC_NODEID_SLOW_RETRIEVAL) &&
  148. bFastRetrievalOnly) ||
  149. ( pNodeID2->cBytes <= 0) )
  150. {
  151. SC scNoTrace = E_INVALIDARG;
  152. return (scNoTrace);
  153. }
  154. m_byteVector.insert (m_byteVector.end(),
  155. pNodeID2->id, pNodeID2->id + pNodeID2->cBytes);
  156. m_type = NDTYP_CUSTOM;
  157. bUseDisplayName = false;
  158. }
  159. else // the CCF_NODEID2 format was not supported. Try CCF_NODEID.
  160. {
  161. sc = DataObject_GetHGLOBALData (spDataObject, GetCustomNodeIDCF(), &hGlobal);
  162. if(!sc)
  163. {
  164. // build the DynamicNodeID from the custom node ID struct
  165. m_type = NDTYP_CUSTOM;
  166. SNodeID *pNodeID = reinterpret_cast<SNodeID*>(GlobalLock (hGlobal));
  167. sc = ScCheckPointers(pNodeID);
  168. if(sc)
  169. return sc;
  170. // if pNodeID->cBytes is zero, this is a legacy indication that the
  171. // node does not support fast retrieval. But, if the client is OK
  172. // with slow retrieval, we supply the display name instead.
  173. if(pNodeID->cBytes != 0)
  174. {
  175. m_byteVector.insert (m_byteVector.end(),
  176. pNodeID->id, pNodeID->id + pNodeID->cBytes);
  177. bUseDisplayName = false;
  178. }
  179. else
  180. {
  181. // cBytes == 0 here. If the client indicated fast retrieval, must return an error.
  182. // Bug 175684: this is a "valid" error return, so we don't want to trace it
  183. if(bFastRetrievalOnly)
  184. {
  185. SC scNoTrace = E_INVALIDARG;
  186. return (scNoTrace);
  187. }
  188. else
  189. bUseDisplayName = true; // must use the display name here.
  190. }
  191. }
  192. };
  193. // let go of the data
  194. if (hGlobal)
  195. {
  196. GlobalUnlock (hGlobal);
  197. GlobalFree (hGlobal);
  198. }
  199. // no custom ID; persist the node name, for compatibility
  200. // if CCF_NODEID was presented with a zero count, also use the node name.
  201. if (bUseDisplayName)
  202. {
  203. sc.Clear();
  204. m_type = NDTYP_STRING;
  205. tstring strName = pMTNode->GetDisplayName();
  206. if (!strName.empty())
  207. m_strEntry = T2CW(strName.data());
  208. else
  209. return (sc = E_INVALIDARG);
  210. }
  211. return sc;
  212. }
  213. /*--------------------------------------------------------------------------*
  214. * CDynamicPathEntryEx::GetCustomNodeIDCF
  215. *
  216. *
  217. *--------------------------------------------------------------------------*/
  218. CLIPFORMAT CDynamicPathEntryEx::GetCustomNodeIDCF()
  219. {
  220. static CLIPFORMAT cfCustomNodeID = 0;
  221. if (cfCustomNodeID == 0)
  222. {
  223. USES_CONVERSION;
  224. cfCustomNodeID = (CLIPFORMAT) RegisterClipboardFormat (W2T (CCF_NODEID));
  225. ASSERT (cfCustomNodeID != 0);
  226. }
  227. return (cfCustomNodeID);
  228. }
  229. /*--------------------------------------------------------------------------*
  230. * CDynamicPathEntryEx::GetCustomNodeID2CF
  231. *
  232. *
  233. *--------------------------------------------------------------------------*/
  234. CLIPFORMAT CDynamicPathEntryEx::GetCustomNodeID2CF()
  235. {
  236. static CLIPFORMAT cfCustomNodeID2 = 0;
  237. if (cfCustomNodeID2 == 0)
  238. {
  239. USES_CONVERSION;
  240. cfCustomNodeID2 = (CLIPFORMAT) RegisterClipboardFormat (W2T (CCF_NODEID2));
  241. ASSERT (cfCustomNodeID2 != 0);
  242. }
  243. return (cfCustomNodeID2);
  244. }
  245. //############################################################################
  246. //############################################################################
  247. //
  248. // Implementation of class CBookmarkEx
  249. //
  250. //############################################################################
  251. //############################################################################
  252. CBookmarkEx::CBookmarkEx(MTNODEID idStatic) : BC(idStatic)
  253. {
  254. }
  255. CBookmarkEx::CBookmarkEx(bool bIsFastBookmark) : BC(bIsFastBookmark)
  256. {
  257. }
  258. /*+-------------------------------------------------------------------------*
  259. * CBookmarkEx::~CBookmarkEx
  260. *
  261. * PURPOSE: Destructor
  262. *
  263. * PARAMETERS:
  264. *
  265. * RETURNS:
  266. *
  267. /*+-------------------------------------------------------------------------*/
  268. CBookmarkEx::~CBookmarkEx()
  269. {
  270. }
  271. /*+-------------------------------------------------------------------------*
  272. *
  273. * CBookmarkEx::ScRetarget
  274. *
  275. * PURPOSE:
  276. *
  277. * PARAMETERS:
  278. * CMTNode * pMTNode :
  279. * bool bFastRetrievalOnly :
  280. *
  281. * RETURNS:
  282. * SC
  283. *
  284. *+-------------------------------------------------------------------------*/
  285. SC
  286. CBookmarkEx::ScRetarget(CMTNode *pMTNode, bool bFastRetrievalOnly)
  287. {
  288. DECLARE_SC(sc, TEXT("CBookmarkEx::ScRetarget"));
  289. if(pMTNode)
  290. {
  291. sc = ScInitialize (pMTNode, NULL, bFastRetrievalOnly);
  292. if(sc)
  293. return sc;
  294. }
  295. else
  296. Reset();
  297. return sc;
  298. }
  299. /*+-------------------------------------------------------------------------*
  300. *
  301. * CBookmarkEx::ResetUI
  302. *
  303. * PURPOSE: Reset's the state of the bookmark. Will put up the Retry/Cancel
  304. * UI if the node is not available.
  305. *
  306. * RETURNS:
  307. * void
  308. *
  309. *+-------------------------------------------------------------------------*/
  310. void
  311. CBookmarkEx::ResetUI()
  312. {
  313. }
  314. /* CBookmarkEx::GetNode
  315. *
  316. * PURPOSE: Returns a CNode corresponding to the bookmark for a particular view.
  317. * NOTE: This will return a new instance every time. To reuse the same
  318. * instance, cache it.
  319. *
  320. * PARAMETERS:
  321. * CViewData * pViewData: The view for which a node is requested
  322. *
  323. * RETURNS:
  324. * CNode * : NULL if the MTNode could not be found.
  325. */
  326. std::auto_ptr<CNode>
  327. CBookmarkEx::GetNode(CViewData *pViewData)
  328. {
  329. DECLARE_SC(sc, TEXT("CBookmarkEx::GetNode"));
  330. CNode * pNode = NULL;
  331. CMTNode * pMTNode = NULL;
  332. // The NULL return value for failure conditions.
  333. std::auto_ptr<CNode> spNodeNull;
  334. // validate parameters
  335. if(NULL == pViewData)
  336. {
  337. sc = E_UNEXPECTED;
  338. return spNodeNull;
  339. }
  340. // Get the target node.
  341. bool bExactMatchFound = false; // out value from ScGetMTNode, unused
  342. sc = ScGetMTNode(true, &pMTNode, bExactMatchFound);
  343. if( sc.IsError() || !pMTNode) // could not find the node - abort.
  344. {
  345. return spNodeNull;
  346. }
  347. // make sure the node is expanded (invisibly) in the tree
  348. CConsoleView* pConsoleView = pViewData->GetConsoleView();
  349. if (pConsoleView == NULL)
  350. {
  351. sc = E_UNEXPECTED;
  352. return spNodeNull;
  353. }
  354. sc = pConsoleView->ScExpandNode (pMTNode->GetID(), true, false);
  355. if(sc)
  356. return spNodeNull;
  357. pNode = pMTNode->GetNode(pViewData, false);
  358. if (pNode == NULL)
  359. {
  360. sc = E_OUTOFMEMORY;
  361. return spNodeNull;
  362. }
  363. if (FAILED (pNode->InitComponents()))
  364. {
  365. delete pNode;
  366. sc = E_UNEXPECTED;
  367. return spNodeNull;
  368. }
  369. return (std::auto_ptr<CNode>(pNode));
  370. }
  371. /*+-------------------------------------------------------------------------*
  372. *
  373. * CBookmarkEx::ScInitialize
  374. *
  375. * PURPOSE:
  376. *
  377. * PARAMETERS:
  378. * CMTNode* pMTNode :
  379. * CMTNode* pMTViewRootNode :
  380. * bool bFastRetrievalOnly : true by default. If true, this
  381. * function returns E_INVALIDARG for
  382. * any node that cannot be quickly
  383. * retrieved.
  384. *
  385. * RETURNS:
  386. * SC
  387. *
  388. *+-------------------------------------------------------------------------*/
  389. SC
  390. CBookmarkEx::ScInitialize (CMTNode* pMTNode, CMTNode* pMTViewRootNode, bool bFastRetrievalOnly)
  391. {
  392. DECLARE_SC(sc, TEXT("CBookmarkEx::ScInitialize"));
  393. // check pointers
  394. sc = ScCheckPointers(pMTNode);
  395. if(sc)
  396. return sc;
  397. BC::Reset();
  398. // 1. Get the static node ID
  399. m_idStatic = pMTNode->GetID();
  400. // If this is a static node, we're done.
  401. if(pMTNode->IsStaticNode())
  402. return sc;
  403. bool fPassedViewRootNode = false;
  404. // get a descriptor for each dynamic node at the end of the branch
  405. // (from leaf to root)
  406. while ((pMTNode != NULL) && !pMTNode->IsStaticNode())
  407. {
  408. CDynamicPathEntryEx entry;
  409. sc = entry.ScInitialize(pMTNode, bFastRetrievalOnly);
  410. if(sc.IsError() && !(sc == E_INVALIDARG) ) // E_INVALIDARG means that fast retrieval was not available.
  411. return sc;
  412. if(sc) // must be E_INVALIDARG
  413. {
  414. // if the node's data object gave us an empty custom node ID,
  415. // we don't want to persist this node or any below it, so purge the list
  416. m_dynamicPath.clear(); // clear the path
  417. sc.Clear();
  418. }
  419. // otherwise, put it this node ID on the front of the list
  420. else
  421. m_dynamicPath.push_front (entry);
  422. /*
  423. * remember if we've passed the node at the root of our view
  424. * on our way up the tree to the first static node
  425. */
  426. if (pMTNode == pMTViewRootNode)
  427. fPassedViewRootNode = true;
  428. /*
  429. * If we've passed the view's root node and the list is empty, it means
  430. * that a node between the view's root node and the first static node
  431. * (specifically, this one) supports CCF_NODEID and has requested
  432. * that the node not be persisted. If a node isn't persisted,
  433. * nothing below it is persisted, either, so we can bail out.
  434. */
  435. if (fPassedViewRootNode && m_dynamicPath.empty())
  436. break;
  437. pMTNode = pMTNode->Parent();
  438. }
  439. // assume success
  440. sc.Clear();
  441. if(!pMTNode || !pMTNode->IsStaticNode())
  442. return (sc = E_UNEXPECTED);
  443. // Get the static node ID of the static parent
  444. m_idStatic = pMTNode->GetID();
  445. // if we don't have a dynamic node path, return so
  446. if (m_dynamicPath.empty ())
  447. return (sc = S_FALSE);
  448. // if we hit the root before we hit a static node, we have an error
  449. if (pMTNode == NULL)
  450. sc = E_FAIL;
  451. return sc;
  452. }
  453. /*+-------------------------------------------------------------------------*
  454. *
  455. * CBookmarkEx::ScGetMTNode
  456. *
  457. * PURPOSE: Returns the MTNode of the node with the given path relative to
  458. * the static node.
  459. *
  460. * PARAMETERS:
  461. * bool bExactMatchRequired: [IN] Do we need exact match?
  462. * CMTNode ** ppMTNode : [OUT]: The MTNode, if found.
  463. * bool bExactMatchFound : [OUT] Did we find exact match?
  464. *
  465. * RETURNS:
  466. * SC
  467. *
  468. *+-------------------------------------------------------------------------*/
  469. SC
  470. CBookmarkEx::ScGetMTNode(bool bExactMatchRequired, CMTNode **ppMTNode, bool& bExactMatchFound)
  471. {
  472. DECLARE_SC(sc, TEXT("CBookmarkEx::ScGetMTNode"));
  473. // check parameters
  474. sc = ScCheckPointers(ppMTNode);
  475. if(sc)
  476. return sc;
  477. // init out param
  478. *ppMTNode = NULL;
  479. bExactMatchFound = false;
  480. CScopeTree *pScopeTree = CScopeTree::GetScopeTree();
  481. if(!pScopeTree)
  482. {
  483. sc = E_POINTER;
  484. return sc;
  485. }
  486. if (m_idStatic == ID_ConsoleRoot)
  487. {
  488. sc = ScRetarget (pScopeTree->GetRoot(), true /*bFastRetrievalOnly*/);
  489. if(sc)
  490. return sc;
  491. }
  492. // find the MTNode of the static node closest to the required node.
  493. CMTNode* pMTNode = NULL;
  494. sc = pScopeTree->Find(m_idStatic, &pMTNode);
  495. if(sc)
  496. return sc;
  497. sc = ScCheckPointers(pMTNode);
  498. if(sc)
  499. return sc;
  500. *ppMTNode = pMTNode; // initialize
  501. CDynamicPath::iterator iter;
  502. for(iter = m_dynamicPath.begin(); iter != m_dynamicPath.end(); iter++)
  503. {
  504. CDynamicPathEntryEx entry;
  505. entry = *iter;
  506. // check the next segment of the path.
  507. sc = ScFindMatchingMTNode(pMTNode, entry, ppMTNode);
  508. // handle the special case of the node not being found but an exact match
  509. // not being needed. In this case, we use the closest ancestor node that
  510. // was available.
  511. if ( (sc == ScFromMMC(IDS_NODE_NOT_FOUND)) && !bExactMatchRequired )
  512. {
  513. // set the output.
  514. *ppMTNode = pMTNode;
  515. sc.Clear();
  516. return sc;
  517. }
  518. // bail on all other errors
  519. if(sc)
  520. return sc;
  521. sc = ScCheckPointers(*ppMTNode);
  522. if(sc)
  523. return sc;
  524. pMTNode = *ppMTNode; //prime the MTNode for the next round, if there is one.
  525. }
  526. // we've found a match if we ran out of entries.
  527. bExactMatchFound = (iter == m_dynamicPath.end());
  528. if(bExactMatchRequired && !bExactMatchFound) // could not find the exact node.
  529. {
  530. *ppMTNode = NULL;
  531. return (sc = ScFromMMC(IDS_NODE_NOT_FOUND));
  532. }
  533. // a NULL pMTNode is not an error, we just need to make sure that
  534. // nodes are initialized before use.
  535. if ((pMTNode != NULL) && !pMTNode->IsInitialized() )
  536. {
  537. sc = pMTNode->Init();
  538. if(sc)
  539. sc.TraceAndClear(); // does not invalidate the locating operation
  540. }
  541. return sc;
  542. }
  543. /*+-------------------------------------------------------------------------*
  544. *
  545. * CBookmarkEx::ScFindMatchingMTNode
  546. *
  547. * PURPOSE: Finds the first child node directly beneath the given parent node
  548. * whose node ID (ie one of CCF_NODEID2, CCF_NODEID, or the display
  549. * name) matches the specified CDynamicPathEntryEx object.
  550. *
  551. * PARAMETERS:
  552. * CMTNode * pMTNodeParent :
  553. * CDynamicPathEntryEx & entry :
  554. * CMTNode ** ppMatchingMTNode : [OUT]: The child node, if found.
  555. *
  556. * RETURNS:
  557. * SC
  558. *
  559. *+-------------------------------------------------------------------------*/
  560. SC
  561. CBookmarkEx::ScFindMatchingMTNode(CMTNode *pMTNodeParent, CDynamicPathEntryEx &entry,
  562. CMTNode **ppMatchingMTNode)
  563. {
  564. DECLARE_SC(sc, TEXT("CBookmarkEx::ScFindMatchingMTNode"));
  565. sc = ScCheckPointers(pMTNodeParent, ppMatchingMTNode);
  566. if(sc)
  567. return sc;
  568. *ppMatchingMTNode = NULL; // initialize
  569. // expand the parent node if not already done so.
  570. if (pMTNodeParent->WasExpandedAtLeastOnce() == FALSE)
  571. {
  572. sc = pMTNodeParent->Expand();
  573. if(sc)
  574. return sc;
  575. }
  576. // see if any of the children of this node match the next segment of the stored path
  577. for (CMTNode *pMTNode = pMTNodeParent->Child(); pMTNode != NULL; pMTNode = pMTNode->Next())
  578. {
  579. CDynamicPathEntryEx entryTemp;
  580. sc = entryTemp.ScInitialize(pMTNode, false /*bFastRetrievalOnly :
  581. at this point, we know the node is created, so we don't care about retrieval speed*/);
  582. if(sc)
  583. return sc;
  584. if(entryTemp == entry) // found it.
  585. {
  586. *ppMatchingMTNode = pMTNode;
  587. return sc;
  588. }
  589. }
  590. // could not find the node.
  591. return (sc = ScFromMMC(IDS_NODE_NOT_FOUND));
  592. }