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.

1976 lines
60 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1999 - 1999
  6. //
  7. // File: cmenu.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. // cmenu.cpp : Implementation of IContextMenuProvider and DLL registration.
  11. #include "stdafx.h"
  12. #include "oncmenu.h"
  13. #include "menuitem.h"
  14. #include "constatbar.h"
  15. #include "regutil.h"
  16. #include "moreutil.h"
  17. #include "multisel.h"
  18. #include "cmenuinfo.h"
  19. #include "conview.h"
  20. #include "scopndcb.h"
  21. #ifdef _DEBUG
  22. #undef THIS_FILE
  23. static char THIS_FILE[] = __FILE__;
  24. #endif
  25. /*+-------------------------------------------------------------------------*
  26. * class CNativeExtendContextMenu
  27. *
  28. *
  29. * PURPOSE: implements IExtendContextMenu by forwarding calls to CContextMenu
  30. * but does not affect lifetime of CContextMenu
  31. *
  32. *+-------------------------------------------------------------------------*/
  33. class CNativeExtendContextMenu :
  34. public CTiedComObject<CContextMenu>,
  35. public CComObjectRoot,
  36. public IExtendContextMenu // this is used so that menu items can be executed uniformly.
  37. {
  38. protected:
  39. typedef CNativeExtendContextMenu ThisClass;
  40. typedef CContextMenu CMyTiedObject;
  41. public:
  42. // com entry points
  43. BEGIN_COM_MAP(ThisClass)
  44. COM_INTERFACE_ENTRY(IExtendContextMenu)
  45. END_COM_MAP()
  46. DECLARE_NOT_AGGREGATABLE(ThisClass)
  47. // IExtendContexMenu methods
  48. MMC_METHOD3( AddMenuItems, LPDATAOBJECT, LPCONTEXTMENUCALLBACK, long * );
  49. MMC_METHOD2( Command, long, LPDATAOBJECT );
  50. };
  51. //############################################################################
  52. //############################################################################
  53. //
  54. // Implementation of methods on CNodeInitObject that
  55. // forward to CContextMenu
  56. //
  57. //############################################################################
  58. //############################################################################
  59. CContextMenu *
  60. CNodeInitObject::GetContextMenu()
  61. {
  62. DECLARE_SC(sc, TEXT("CNodeInitObject::GetContextMenu"));
  63. if(m_spContextMenu == NULL)
  64. {
  65. // check internal pointers
  66. sc = ScCheckPointers(m_spScopeTree, E_UNEXPECTED);
  67. if (sc)
  68. return NULL;
  69. // get scopetree and call back pointers
  70. CScopeTree* const pScopeTree =
  71. dynamic_cast<CScopeTree*>(m_spScopeTree.GetInterfacePtr());
  72. // if the menu is created by component data, it does not have the node.
  73. // in that case menu is created by passing NULL pointers to some parameters.
  74. // Menu should never need those pointers in the mentioned case
  75. CNodeCallback* pNodeCallback = NULL;
  76. if ( m_pNode != NULL )
  77. {
  78. // check other required pointers
  79. sc = ScCheckPointers(m_pNode->GetViewData(), E_UNEXPECTED);
  80. if (sc)
  81. return NULL;
  82. pNodeCallback =
  83. dynamic_cast<CNodeCallback *>(m_pNode->GetViewData()->GetNodeCallback());
  84. }
  85. // create context menu
  86. CContextMenu *pContextMenu = NULL;
  87. sc = CContextMenu::ScCreateContextMenuForScopeNode(m_pNode, pNodeCallback, pScopeTree,
  88. &m_spContextMenu, pContextMenu);
  89. if (sc)
  90. return NULL;
  91. sc = ScCheckPointers(pContextMenu, E_UNEXPECTED);
  92. if (sc)
  93. return NULL;
  94. return pContextMenu;
  95. }
  96. return dynamic_cast<CContextMenu *>(m_spContextMenu.GetInterfacePtr());
  97. }
  98. STDMETHODIMP
  99. CNodeInitObject::AddItem(CONTEXTMENUITEM * pItem)
  100. {
  101. DECLARE_SC(sc, TEXT("CNodeInitObject::AddItem"));
  102. CContextMenu *pContextMenu = GetContextMenu();
  103. sc = ScCheckPointers(pContextMenu, E_UNEXPECTED);
  104. if(sc)
  105. return sc.ToHr();
  106. sc = pContextMenu->ScAddItem(pItem, true/*bPassCommandBackToSnapin*/);
  107. return sc.ToHr();
  108. }
  109. STDMETHODIMP
  110. CNodeInitObject::EmptyMenuList ()
  111. {
  112. DECLARE_SC(sc, TEXT("CNodeInitObject::EmptyMenuList"));
  113. if (m_spContextMenu == NULL)
  114. return S_OK;
  115. CContextMenu *pContextMenu = GetContextMenu();
  116. sc = ScCheckPointers(pContextMenu, E_UNEXPECTED);
  117. if(sc)
  118. return sc.ToHr();
  119. sc = pContextMenu->EmptyMenuList();
  120. return sc.ToHr();
  121. }
  122. STDMETHODIMP
  123. CNodeInitObject::AddThirdPartyExtensionItems(IDataObject* piDataObject )
  124. {
  125. DECLARE_SC(sc, TEXT("CNodeInitObject::AddThirdPartyExtensionItems"));
  126. CContextMenu *pContextMenu = GetContextMenu();
  127. sc = ScCheckPointers(pContextMenu, E_UNEXPECTED);
  128. if(sc)
  129. return sc.ToHr();
  130. sc = pContextMenu->AddThirdPartyExtensionItems(piDataObject);
  131. return sc.ToHr();
  132. }
  133. STDMETHODIMP
  134. CNodeInitObject::AddPrimaryExtensionItems(IUnknown* piCallback, IDataObject* piDataObject )
  135. {
  136. DECLARE_SC(sc, TEXT("CNodeInitObject::AddPrimaryExtensionItems"));
  137. CContextMenu *pContextMenu = GetContextMenu();
  138. sc = ScCheckPointers(pContextMenu, E_UNEXPECTED);
  139. if(sc)
  140. return sc.ToHr();
  141. sc = pContextMenu->AddPrimaryExtensionItems(piCallback, piDataObject);
  142. return sc.ToHr();
  143. }
  144. STDMETHODIMP
  145. CNodeInitObject::ShowContextMenu(HWND hwndParent, LONG xPos, LONG yPos, LONG* plSelected)
  146. {
  147. DECLARE_SC(sc, TEXT("CNodeInitObject::ShowContextMenu"));
  148. CContextMenu *pContextMenu = GetContextMenu();
  149. sc = ScCheckPointers(pContextMenu, E_UNEXPECTED);
  150. if(sc)
  151. return sc.ToHr();
  152. pContextMenu->SetStatusBar(GetStatusBar()); // wire up the status bar.
  153. sc = pContextMenu->ShowContextMenu(hwndParent, xPos, yPos, plSelected);
  154. return sc.ToHr();
  155. }
  156. //############################################################################
  157. //############################################################################
  158. //
  159. // Implementation of CCommandSink
  160. //
  161. //############################################################################
  162. //############################################################################
  163. /*+-------------------------------------------------------------------------*
  164. * class CCommandSink
  165. *
  166. *
  167. * PURPOSE:
  168. *
  169. *+-------------------------------------------------------------------------*/
  170. class CCommandSink : public CWindowImpl<CCommandSink>
  171. {
  172. // Construction
  173. public:
  174. CCommandSink( CContextMenu& nodemgr, WTL::CMenu& menu, CConsoleStatusBar * pStatusbar);
  175. virtual ~CCommandSink();
  176. BOOL Init();
  177. LRESULT OnMenuSelect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
  178. BEGIN_MSG_MAP(CCommandSink)
  179. MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)
  180. END_MSG_MAP()
  181. private:
  182. CContextMenu& m_nodemgr;
  183. const WTL::CMenu& m_menu;
  184. CConsoleStatusBar * m_pStatusBar;
  185. };
  186. CCommandSink::CCommandSink( CContextMenu& nodemgr, WTL::CMenu& menu, CConsoleStatusBar * pStatusbar)
  187. : m_nodemgr( nodemgr ),
  188. m_menu( menu ),
  189. m_pStatusBar(pStatusbar)
  190. {
  191. }
  192. CCommandSink::~CCommandSink()
  193. {
  194. /*
  195. * clear the status bar text, if there's any there.
  196. */
  197. if (m_pStatusBar != NULL)
  198. m_pStatusBar->ScSetStatusText (NULL);
  199. }
  200. BOOL CCommandSink::Init()
  201. {
  202. RECT rcPos = {0,0,0,0};
  203. Create(NULL, rcPos, _T("ACFx:CxtMenuSink"), WS_POPUP);
  204. return TRUE;
  205. }
  206. LRESULT CCommandSink::OnMenuSelect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  207. {
  208. UINT nItemID = (UINT) LOWORD(wParam); // menu item or submenu index
  209. UINT nFlags = (UINT) HIWORD(wParam); // menu flags
  210. HMENU hSysMenu = (HMENU) lParam; // handle of menu clicked
  211. TRACE(_T("CCommandSink::OnMenuSelect: nItemID=%d, nFlags=0x%X, hSysMenu=0x%X\n"), nItemID, nFlags, hSysMenu);
  212. if ( 0xFFFF == nFlags && NULL == hSysMenu )
  213. return 0; // as per Win32 ProgRef
  214. if ( 0 == nItemID && !(nFlags & MF_POPUP) )
  215. return 0; // no item selected
  216. CMenuItem* pmenuitem = NULL;
  217. if (nFlags & MF_POPUP)
  218. {
  219. if ( hSysMenu == m_menu.m_hMenu )
  220. {
  221. // We assume menu's cannot be longer than 256 chars
  222. TCHAR szMenu[256];
  223. MENUITEMINFO menuItemInfo;
  224. menuItemInfo.cbSize = sizeof(MENUITEMINFO);
  225. menuItemInfo.fMask = MIIM_TYPE;
  226. menuItemInfo.fType = MFT_STRING;
  227. menuItemInfo.cch = 256;
  228. menuItemInfo.dwTypeData = szMenu;
  229. ::GetMenuItemInfo(hSysMenu, nItemID, TRUE, &menuItemInfo);
  230. ASSERT(256 >= (menuItemInfo.cch+1));
  231. pmenuitem = m_nodemgr.FindNthItemInSubmenu( NULL, nItemID, szMenu );
  232. }
  233. else
  234. pmenuitem = m_nodemgr.FindNthItemInSubmenu( hSysMenu, nItemID, NULL );
  235. }
  236. else
  237. pmenuitem = m_nodemgr.FindMenuItem( nItemID );
  238. if ( NULL == pmenuitem )
  239. {
  240. ASSERT( FALSE );
  241. return 0;
  242. }
  243. if(m_pStatusBar)
  244. m_pStatusBar->ScSetStatusText( pmenuitem->GetMenuItemStatusBarText() );
  245. return 0;
  246. }
  247. //############################################################################
  248. //############################################################################
  249. //
  250. // CContextMenu methods - continued from oncmenu.cpp
  251. // These methods were originally in this file and I dont want to move
  252. // them and break history - vivekj
  253. //
  254. //############################################################################
  255. //############################################################################
  256. //+-------------------------------------------------------------------
  257. //
  258. // Member: CContextMenu::EmptyMenuList
  259. //
  260. // Synopsis: Clear the context menu.
  261. //
  262. // Arguments:
  263. //
  264. // Returns: HRESULT
  265. //
  266. //--------------------------------------------------------------------
  267. STDMETHODIMP CContextMenu::EmptyMenuList ()
  268. {
  269. DECLARE_SC(sc, _T("IContextMenuProvider::EmptyMenuList"));
  270. START_CRITSEC_BOTH
  271. delete m_pmenuitemRoot;
  272. m_pmenuitemRoot = NULL;
  273. m_nNextMenuItemID = MENUITEM_BASE_ID;
  274. ReleaseSnapinList();
  275. m_fAddedThirdPartyExtensions = FALSE;
  276. m_MaxPrimaryOwnerID = OWNERID_PRIMARY_MIN;
  277. m_MaxThirdPartyOwnerID = OWNERID_THIRD_PARTY_MIN;
  278. m_CurrentExtensionOwnerID = OWNERID_NATIVE;
  279. m_fPrimaryInsertionFlags = 0;
  280. m_fThirdPartyInsertionFlags = 0;
  281. END_CRITSEC_BOTH
  282. return sc.ToHr();
  283. }
  284. /*+-------------------------------------------------------------------------*
  285. *
  286. * CContextMenu::RemoveAccelerators
  287. *
  288. * PURPOSE: Removes the accelerators from a context menu item name
  289. *
  290. * PARAMETERS:
  291. * CStr & str :
  292. *
  293. * RETURNS:
  294. * void
  295. *
  296. *+-------------------------------------------------------------------------*/
  297. void
  298. RemoveAccelerators(tstring &str)
  299. {
  300. // in some locales , the accelerators appear at the end eg: Start (&s). Therefore, remove anything after (&
  301. int i = str.find(TEXT( "(&" ));
  302. if (i != tstring::npos)
  303. str.erase (i); // remove the waste left over after and including the string "(&"
  304. tstring::iterator itToTrim = std::remove (str.begin(), str.end(), _T('&'));
  305. // remove the waste left over after removing accelerator markers
  306. str.erase (itToTrim, str.end());
  307. }
  308. //+-------------------------------------------------------------------
  309. //
  310. // Member: CContextMenu::AddItem
  311. //
  312. // Synopsis: Add a menu item to context menu.
  313. //
  314. // Arguments: CONTEXTMENUITEM*
  315. //
  316. // Returns: HRESULT
  317. //
  318. //--------------------------------------------------------------------
  319. STDMETHODIMP CContextMenu::AddItem( CONTEXTMENUITEM* pItem )
  320. {
  321. DECLARE_SC(sc, _T("IContextMenuCallback::AddItem"));
  322. return ( sc = ScAddItem( pItem ) ).ToHr();
  323. }
  324. //+-------------------------------------------------------------------
  325. //
  326. // Member: CContextMenu::ScAddItem
  327. //
  328. // Synopsis: Add a menu item to context menu.
  329. //
  330. // Arguments: CONTEXTMENUITEM*
  331. //
  332. // Returns: SC
  333. //
  334. //--------------------------------------------------------------------
  335. SC CContextMenu::ScAddItem( CONTEXTMENUITEM* pItem, bool bPassCommandBackToSnapin /*= false*/ )
  336. {
  337. DECLARE_SC(sc, _T("IContextMenuCallback::ScAddItem"));
  338. if (NULL == pItem)
  339. {
  340. sc = E_INVALIDARG;
  341. TraceSnapinError(_T("NULL CONTEXTMENUITEM ptr"), sc);
  342. return sc;
  343. }
  344. // added a non-langugage independent context menu item. Cook up a language independent ID.
  345. // get the menu text and strip out accelerator markers
  346. tstring strLanguageIndependentName;
  347. if(pItem->strName)
  348. {
  349. USES_CONVERSION;
  350. strLanguageIndependentName = OLE2CT(pItem->strName);
  351. RemoveAccelerators(strLanguageIndependentName);
  352. }
  353. #ifdef DBG
  354. TRACE(_T("CContextMenu::AddItem name \"%ls\" statusbartext \"%ls\" commandID %ld submenuID %ld flags %ld special %ld\n"),
  355. SAFEDBGBSTR(pItem->strName),
  356. SAFEDBGBSTR(pItem->strStatusBarText),
  357. pItem->lCommandID,
  358. pItem->lInsertionPointID,
  359. pItem->fFlags,
  360. pItem->fSpecialFlags);
  361. #endif
  362. // leaves critsec claim for DoAddMenuItem
  363. USES_CONVERSION;
  364. sc = DoAddMenuItem( OLE2CT(pItem->strName),
  365. OLE2CT(pItem->strStatusBarText),
  366. strLanguageIndependentName.data(),
  367. pItem->lCommandID,
  368. pItem->lInsertionPointID,
  369. pItem->fFlags,
  370. pItem->fSpecialFlags,
  371. m_CurrentExtensionOwnerID,
  372. NULL,
  373. bPassCommandBackToSnapin );
  374. return sc;
  375. }
  376. //+-------------------------------------------------------------------
  377. //
  378. // Member: CContextMenu::AddItem
  379. //
  380. // Synopsis: Add a menu item to context menu.
  381. //
  382. // Arguments: CONTEXTMENUITEM2* - includes a language independent name
  383. //
  384. // Returns: HRESULT
  385. //
  386. //--------------------------------------------------------------------
  387. STDMETHODIMP CContextMenu::AddItem( CONTEXTMENUITEM2* pItem )
  388. {
  389. DECLARE_SC(sc, _T("IContextMenuCallback::AddItem"));
  390. if (NULL == pItem)
  391. {
  392. sc = E_INVALIDARG;
  393. TraceSnapinError(_T("NULL CONTEXTMENUITEM ptr"), sc);
  394. return sc.ToHr();
  395. }
  396. // No language-independent-id ?
  397. if ( (pItem->strLanguageIndependentName == NULL) ||
  398. (wcscmp(pItem->strLanguageIndependentName, L"") == 0) )
  399. {
  400. // and it is neither a separator nor insertion point.
  401. if ( !(MF_SEPARATOR & pItem->fFlags) &&
  402. !(CCM_SPECIAL_INSERTION_POINT & pItem->fSpecialFlags) )
  403. {
  404. sc = E_INVALIDARG;
  405. TraceSnapinError(_T("NULL language-indexpendent-id passed"), sc);
  406. return sc.ToHr();
  407. }
  408. }
  409. #ifdef DBG
  410. TRACE(_T("CContextMenu::AddItem name \"%ls\" statusbartext \"%ls\" languageIndependentName \"%ls\" commandID %ld submenuID %ld flags %ld special %ld\n"),
  411. SAFEDBGBSTR(pItem->strName),
  412. SAFEDBGBSTR(pItem->strStatusBarText),
  413. SAFEDBGBSTR(pItem->strLanguageIndependentName),
  414. pItem->lCommandID,
  415. pItem->lInsertionPointID,
  416. pItem->fFlags,
  417. pItem->fSpecialFlags
  418. );
  419. #endif
  420. // leaves critsec claim for DoAddMenuItem
  421. USES_CONVERSION;
  422. sc = DoAddMenuItem( OLE2CT(pItem->strName),
  423. OLE2CT(pItem->strStatusBarText),
  424. OLE2CT(pItem->strLanguageIndependentName),
  425. pItem->lCommandID,
  426. pItem->lInsertionPointID,
  427. pItem->fFlags,
  428. pItem->fSpecialFlags,
  429. m_CurrentExtensionOwnerID );
  430. return sc.ToHr();
  431. }
  432. //+-------------------------------------------------------------------
  433. //
  434. // Member: CContextMenu::AddPrimaryExtensionItems
  435. //
  436. // Synopsis: Ask primary snapin to add menu items.
  437. //
  438. // Arguments: [piExtension]
  439. // [piDataobject]
  440. //
  441. // Note: claims critsec
  442. //
  443. // Returns: HRESULT
  444. //
  445. //--------------------------------------------------------------------
  446. STDMETHODIMP CContextMenu::AddPrimaryExtensionItems (
  447. IUnknown* piExtension,
  448. IDataObject* piDataObject )
  449. {
  450. DECLARE_SC(sc, _T("IContextMenuProvider::AddPrimaryExtensionItems"));
  451. if (NULL == piExtension)
  452. {
  453. sc = E_INVALIDARG;
  454. TraceSnapinError(_T("NULL IUnknown ptr"), sc);
  455. return sc.ToHr();
  456. }
  457. if (NULL == piDataObject)
  458. {
  459. sc = E_INVALIDARG;
  460. TraceSnapinError(_T("NULL IDataObject ptr"), sc);
  461. return sc.ToHr();
  462. }
  463. // control reentrant access to this
  464. if (!m_fAddingPrimaryExtensionItems)
  465. {
  466. m_fAddingPrimaryExtensionItems = true;
  467. //HRESULT hr = ExtractObjectTypeCStr( piDataObject, &m_strObjectGUID );
  468. //ASSERT( SUCCEEDED(hr) );
  469. START_CRITSEC_SNAPIN;
  470. sc = ScAddSnapinToList_IUnknown( piExtension, piDataObject, m_MaxPrimaryOwnerID++ );
  471. END_CRITSEC_SNAPIN;
  472. m_fAddingPrimaryExtensionItems = false;
  473. // Clear view menu allowed flag
  474. // A second call may be made to AddPrimaryExtensionItems to handle the other item
  475. // types only, so the view items must be disabled after the first call.
  476. m_fPrimaryInsertionFlags &= ~CCM_INSERTIONALLOWED_VIEW;
  477. if (sc)
  478. return sc.ToHr();
  479. }
  480. return sc.ToHr();
  481. }
  482. //+-------------------------------------------------------------------
  483. //
  484. // Member: CContextMenu::AddThirdPartyExtensionItems
  485. //
  486. // Synopsis: Ask extensions to add comtext menu items.
  487. //
  488. // Arguments: IDataObject*
  489. //
  490. // Note: claims critsec, potentially for a considerable period of time
  491. //
  492. // Returns: HRESULT
  493. //
  494. //--------------------------------------------------------------------
  495. STDMETHODIMP CContextMenu::AddThirdPartyExtensionItems (
  496. IDataObject* piDataObject )
  497. {
  498. DECLARE_SC(sc, _T("IContextMenuProvider::AddThirdPartyExtensionItems"));
  499. if (NULL == piDataObject)
  500. {
  501. sc = E_INVALIDARG;
  502. TraceSnapinError(_T("NULL piDataObject"), sc);
  503. return sc.ToHr();
  504. }
  505. START_CRITSEC_SNAPIN;
  506. // Extensions may only be added once, otherwise return S_FALSE
  507. if (m_fAddedThirdPartyExtensions == TRUE)
  508. {
  509. sc = S_FALSE;
  510. TraceNodeMgrLegacy(_T("CContextMenu::AddThirdPartyExtensionItems>> Menu already extended"), sc);
  511. return sc.ToHr();
  512. }
  513. m_fAddedThirdPartyExtensions = TRUE;
  514. do // not a loop
  515. {
  516. CExtensionsIterator it;
  517. sc = it.ScInitialize(piDataObject, g_szContextMenu);
  518. if (sc)
  519. {
  520. sc = S_FALSE;
  521. break;
  522. }
  523. BOOL fProblem = FALSE;
  524. for (; it.IsEnd() == FALSE; it.Advance())
  525. {
  526. sc = ScAddSnapinToList_GUID(it.GetCLSID(), piDataObject,
  527. m_MaxThirdPartyOwnerID++);
  528. if (sc)
  529. fProblem = TRUE; // Continue even on error.
  530. }
  531. if (fProblem == TRUE)
  532. sc = S_FALSE;
  533. } while (0);
  534. END_CRITSEC_SNAPIN;
  535. return sc.ToHr();
  536. }
  537. // claims critsec, potentially for a considerable period of time
  538. STDMETHODIMP CContextMenu::AddMultiSelectExtensionItems (
  539. LONG_PTR lMultiSelection)
  540. {
  541. MMC_TRY
  542. if (lMultiSelection == 0)
  543. return E_INVALIDARG;
  544. CMultiSelection* pMS = reinterpret_cast<CMultiSelection*>(lMultiSelection);
  545. ASSERT(pMS != NULL);
  546. TRACE_METHOD(CContextMenu,AddThirdPartyExtensionItems);
  547. TRACE(_T("CContextMenu::AddThirdPartyExtensionItems"));
  548. START_CRITSEC_SNAPIN;
  549. // Extensions may only be added once, otherwise return S_FALSE
  550. if (m_fAddedThirdPartyExtensions == TRUE)
  551. {
  552. TRACE(_T("CContextMenu::AddThirdPartyExtensionItems>> Menu already extended"));
  553. return S_FALSE;
  554. }
  555. m_fAddedThirdPartyExtensions = TRUE;
  556. do // not a loop
  557. {
  558. CList<CLSID, CLSID&> snapinClsidList;
  559. HRESULT hr = pMS->GetExtensionSnapins(g_szContextMenu, snapinClsidList);
  560. BREAK_ON_FAIL(hr);
  561. POSITION pos = snapinClsidList.GetHeadPosition();
  562. if (pos == NULL)
  563. break;
  564. CLSID clsid;
  565. IDataObjectPtr spDataObject;
  566. hr = pMS->GetMultiSelDataObject(&spDataObject);
  567. ASSERT(SUCCEEDED(hr));
  568. BREAK_ON_FAIL(hr);
  569. BOOL fProblem = FALSE;
  570. while (pos)
  571. {
  572. clsid = snapinClsidList.GetNext(pos);
  573. hr = ScAddSnapinToList_GUID(clsid, spDataObject,
  574. m_MaxThirdPartyOwnerID++).ToHr();
  575. CHECK_HRESULT(hr);
  576. if (FAILED(hr))
  577. fProblem = TRUE; // Continue even on error.
  578. }
  579. if (fProblem == TRUE)
  580. hr = S_FALSE;
  581. } while (0);
  582. END_CRITSEC_SNAPIN;
  583. return S_OK;
  584. MMC_CATCH
  585. }
  586. // Worker function, called recursively by FindMenuItem
  587. // critsec should already be claimed
  588. // If fFindSubmenu, then nMenuItemID is actually an HMENU
  589. CMenuItem* FindWorker( MenuItemList& list, LONG_PTR nMenuItemID, BOOL fFindSubmenu )
  590. {
  591. POSITION pos = list.GetHeadPosition();
  592. while(pos)
  593. {
  594. CMenuItem* pItem = list.GetNext(pos);
  595. if ( !fFindSubmenu && pItem->GetMenuItemID() == nMenuItemID )
  596. {
  597. // Found a match
  598. return pItem;
  599. } else
  600. if ( pItem->HasChildList() )
  601. {
  602. if ( fFindSubmenu &&
  603. pItem->GetPopupMenuHandle() == (HMENU)nMenuItemID &&
  604. !pItem->IsSpecialInsertionPoint() ) // "insertion point" is not real menu
  605. return pItem;
  606. pItem = FindWorker( pItem->GetMenuItemSubmenu(), nMenuItemID, fFindSubmenu );
  607. if (NULL != pItem)
  608. return pItem;
  609. }
  610. }
  611. return NULL;
  612. }
  613. MenuItemList* CContextMenu::GetMenuItemList()
  614. {
  615. if (NULL == m_pmenuitemRoot)
  616. m_pmenuitemRoot = new CRootMenuItem;
  617. if (m_pmenuitemRoot == NULL)
  618. {
  619. return NULL;
  620. }
  621. return &m_pmenuitemRoot->GetMenuItemSubmenu();
  622. }
  623. // critsec should already be claimed
  624. CMenuItem* CContextMenu::FindMenuItem( LONG_PTR nMenuItemID, BOOL fFindSubmenu )
  625. {
  626. DECLARE_SC(sc, TEXT("CContextMenu::FindMenuItem"));
  627. if (0 == nMenuItemID || CCM_INSERTIONPOINTID_ROOT_MENU == nMenuItemID)
  628. return m_pmenuitemRoot;
  629. else
  630. {
  631. MenuItemList* plist = GetMenuItemList();
  632. sc = ScCheckPointers( plist );
  633. if (sc)
  634. return NULL;
  635. return FindWorker( *plist, nMenuItemID, fFindSubmenu );
  636. }
  637. }
  638. /*+-------------------------------------------------------------------------*
  639. *
  640. * ReverseFindWorker
  641. *
  642. * PURPOSE: Worker function, called recursively by ReverseFindMenuItem
  643. * critsec should already be claimed
  644. *
  645. * PARAMETERS:
  646. * MenuItemList& list :
  647. * long nCommandID :
  648. * MENU_OWNER_ID ownerID :
  649. * CStr & strPath :
  650. *
  651. * RETURNS:
  652. * CMenuItem*
  653. *
  654. *+-------------------------------------------------------------------------*/
  655. CMenuItem*
  656. ReverseFindWorker( MenuItemList& list, long nCommandID, MENU_OWNER_ID ownerID, CStr &strPath, CStr &strLanguageIndependentPath )
  657. {
  658. POSITION pos = list.GetHeadPosition();
  659. while(pos)
  660. {
  661. CMenuItem* pItem = list.GetNext(pos);
  662. if ( pItem->GetCommandID() == nCommandID
  663. && ( (pItem->GetMenuItemOwner() == ownerID)
  664. || IsSharedInsertionPointID(nCommandID)
  665. )
  666. )
  667. {
  668. // Found a match - add it to the path and return
  669. strPath = pItem->GetPath();
  670. strLanguageIndependentPath = pItem->GetLanguageIndependentPath();
  671. return pItem;
  672. }
  673. else if ( pItem->HasChildList() )
  674. {
  675. pItem = ReverseFindWorker( pItem->GetMenuItemSubmenu(), nCommandID, ownerID, strPath, strLanguageIndependentPath );
  676. if (NULL != pItem)
  677. {
  678. return pItem;
  679. }
  680. }
  681. }
  682. return NULL;
  683. }
  684. /*+-------------------------------------------------------------------------*
  685. *
  686. * CContextMenu::ReverseFindMenuItem
  687. *
  688. * PURPOSE: Searches for the specified menu item. Also builds up the
  689. * path to the menu item in strPath.
  690. *
  691. * NOTE: critsec should already be claimed
  692. *
  693. * PARAMETERS:
  694. * long nCommandID :
  695. * MENU_OWNER_ID ownerID :
  696. * CStr & strPath :
  697. *
  698. * RETURNS:
  699. * CMenuItem*
  700. *
  701. *+-------------------------------------------------------------------------*/
  702. CMenuItem*
  703. CContextMenu::ReverseFindMenuItem( long nCommandID, MENU_OWNER_ID ownerID, CStr &strPath, CStr &strLanguageIndependentPath)
  704. {
  705. DECLARE_SC(sc, TEXT("CContextMenu::ReverseFindMenuItem"));
  706. strPath = TEXT(""); // initialize
  707. if (CCM_INSERTIONPOINTID_ROOT_MENU == nCommandID)
  708. return m_pmenuitemRoot;
  709. else
  710. {
  711. MenuItemList* plist = GetMenuItemList();
  712. sc = ScCheckPointers( plist );
  713. if (sc)
  714. return NULL;
  715. return ReverseFindWorker( *plist, nCommandID, ownerID, strPath, strLanguageIndependentPath);
  716. }
  717. }
  718. //
  719. // Find Nth item in specified menu/submenu
  720. //
  721. CMenuItem* CContextMenu::FindNthItemInSubmenu( HMENU hmenuParent, UINT iPosition, LPTSTR lpszMenuName )
  722. {
  723. // locate menu/submenu
  724. MenuItemList* plist = GetMenuItemList();
  725. if ( NULL != hmenuParent )
  726. {
  727. CMenuItem* pParent = FindMenuItem( (LONG_PTR)hmenuParent, TRUE );
  728. if ( NULL == pParent )
  729. {
  730. ASSERT( FALSE );
  731. return NULL;
  732. }
  733. plist = &pParent->GetMenuItemSubmenu();
  734. }
  735. if ( NULL == plist )
  736. {
  737. ASSERT( FALSE );
  738. return NULL;
  739. }
  740. // find the Nth item
  741. POSITION pos = plist->GetHeadPosition();
  742. if (NULL != lpszMenuName)
  743. {
  744. while(pos)
  745. {
  746. CMenuItem* pItem = plist->GetNext(pos);
  747. if (! _tcscmp(lpszMenuName, pItem->GetMenuItemName() ))
  748. {
  749. // Found the match
  750. return pItem;
  751. }
  752. }
  753. }
  754. else
  755. {
  756. while(pos)
  757. {
  758. CMenuItem* pItem = plist->GetNext(pos);
  759. if ( 0 == iPosition-- )
  760. {
  761. // Found a match
  762. return pItem;
  763. }
  764. }
  765. }
  766. ASSERT( FALSE );
  767. return NULL;
  768. }
  769. // claims critsec
  770. STDMETHODIMP CContextMenu::DoAddMenuItem(LPCTSTR lpszName,
  771. LPCTSTR lpszStatusBarText,
  772. LPCTSTR lpszLanguageIndependentName,
  773. LONG lCommandID,
  774. LONG lInsertionPointID,
  775. LONG fFlags,
  776. LONG fSpecialFlags,
  777. MENU_OWNER_ID ownerID,
  778. CMenuItem** ppMenuItem /* = NULL */,
  779. bool bPassCommandBackToSnapin /*= false*/ )
  780. {
  781. DECLARE_SC(sc, TEXT("CContextMenu::DoAddMenuItem"));
  782. MMC_TRY
  783. // init out param
  784. if (ppMenuItem)
  785. *ppMenuItem = NULL;
  786. // Save test flag now because special flags are modified below
  787. BOOL bTestOnly = fSpecialFlags & CCM_SPECIAL_TESTONLY;
  788. if ( OWNERID_INVALID == ownerID )
  789. {
  790. TRACE(_T("CContextMenu::DoAddMenuItem(): invalid ownerid"));
  791. ASSERT(FALSE);
  792. return E_INVALIDARG;
  793. }
  794. if ( (CCM_SPECIAL_SEPARATOR & fSpecialFlags)?0:1
  795. + ((CCM_SPECIAL_SUBMENU|CCM_SPECIAL_DEFAULT_ITEM) & fSpecialFlags)?0:1
  796. + (CCM_SPECIAL_INSERTION_POINT & fSpecialFlags)?0:1
  797. > 1 )
  798. {
  799. TRACE(_T("CContextMenu::DoAddMenuItem(): invalid combination of special flags"));
  800. ASSERT(FALSE);
  801. return E_INVALIDARG;
  802. }
  803. if (CCM_SPECIAL_SEPARATOR & fSpecialFlags)
  804. {
  805. lpszName = NULL;
  806. lpszStatusBarText = NULL;
  807. lCommandID = 0;
  808. fFlags = MF_SEPARATOR | MF_GRAYED | MF_DISABLED;
  809. }
  810. if ( CCM_SPECIAL_INSERTION_POINT & fSpecialFlags )
  811. {
  812. fFlags = NULL; // be sure to clear MF_POPUP
  813. fSpecialFlags = CCM_SPECIAL_INSERTION_POINT;
  814. }
  815. if ( (CCM_SPECIAL_SUBMENU & fSpecialFlags) && !(MF_POPUP & fFlags) )
  816. {
  817. TRACE(_T("CContextMenu::DoAddMenuItem(): CCM_SPECIAL_SUBMENU requires MF_POPUP"));
  818. ASSERT(FALSE);
  819. return E_INVALIDARG;
  820. }
  821. if ( (MF_OWNERDRAW|MF_BITMAP) & fFlags )
  822. {
  823. TRACE(_T("CContextMenu::DoAddMenuItem(): MF_OWNERDRAW and MF_BITMAP are invalid"));
  824. ASSERT(FALSE);
  825. return E_INVALIDARG;
  826. }
  827. else if ( !(MF_SEPARATOR & fFlags) &&
  828. !(CCM_SPECIAL_INSERTION_POINT & fSpecialFlags) &&
  829. NULL == lpszName )
  830. {
  831. TRACE(_T("CContextMenu::DoAddMenuItem(): invalid menuitem text\n"));
  832. ASSERT(FALSE);
  833. return E_INVALIDARG;
  834. }
  835. // note that NULL==lpszStatusBarText is permitted
  836. START_CRITSEC_MENU;
  837. //
  838. // An insertion point of 0 is interpreted the same as CCM_INSERTIONPOINTID_ROOT_MENU
  839. //
  840. if (0 == lInsertionPointID)
  841. lInsertionPointID = CCM_INSERTIONPOINTID_ROOT_MENU;
  842. //
  843. // Check that the insertion point ID specified is legal for this customer
  844. //
  845. do // false loop
  846. {
  847. if ( !IsSpecialInsertionPointID(lInsertionPointID) )
  848. break;
  849. if ( IsReservedInsertionPointID(lInsertionPointID) )
  850. {
  851. TRACE(_T("CContextMenu::DoAddMenuItem(): using reserved insertion point ID\n"));
  852. return E_INVALIDARG;
  853. }
  854. if ( !IsSharedInsertionPointID(lInsertionPointID) )
  855. break;
  856. if ( !IsAddPrimaryInsertionPointID(lInsertionPointID) )
  857. {
  858. if ( IsPrimaryOwnerID(ownerID) )
  859. {
  860. TRACE(_T("CContextMenu::DoAddMenuItem(): not addprimary insertion point ID\n"));
  861. return E_INVALIDARG;
  862. }
  863. }
  864. if ( !IsAdd3rdPartyInsertionPointID(lInsertionPointID) )
  865. {
  866. if ( IsThirdPartyOwnerID(ownerID) )
  867. {
  868. TRACE(_T("CContextMenu::DoAddMenuItem(): not add3rdpartyinsertion point ID\n"));
  869. return E_INVALIDARG;
  870. }
  871. }
  872. } while (FALSE); // false loop
  873. //
  874. // Check that the command ID specified is legal for this customer
  875. //
  876. if ( (MF_POPUP & fFlags) || (CCM_SPECIAL_INSERTION_POINT & fSpecialFlags) )
  877. {
  878. do // false loop
  879. {
  880. if ( !IsSpecialInsertionPointID(lCommandID) )
  881. break;
  882. if ( IsReservedInsertionPointID(lCommandID) )
  883. {
  884. TRACE(_T("CContextMenu::DoAddMenuItem(): adding reserved insertion point ID\n"));
  885. ASSERT(FALSE);
  886. return E_INVALIDARG;
  887. }
  888. if ( !IsSharedInsertionPointID(lCommandID) )
  889. break;
  890. if ( IsThirdPartyOwnerID(ownerID) )
  891. {
  892. TRACE(_T("CContextMenu::DoAddMenuItem(): 3rdparty cannot add shared insertion point"));
  893. ASSERT(FALSE);
  894. return E_INVALIDARG;
  895. }
  896. else if ( IsPrimaryOwnerID(ownerID) )
  897. {
  898. if ( !IsCreatePrimaryInsertionPointID(lCommandID) )
  899. {
  900. TRACE(_T("CContextMenu::DoAddMenuItem(): only system for new !PRIMARYCREATE submenu"));
  901. ASSERT(FALSE);
  902. return E_INVALIDARG;
  903. }
  904. }
  905. else if ( IsSystemOwnerID(ownerID) )
  906. {
  907. if ( IsCreatePrimaryInsertionPointID(lCommandID) )
  908. {
  909. TRACE(_T("CContextMenu::DoAddMenuItem(): only primary extension for new PRIMARYCREATE submenu"));
  910. ASSERT(FALSE);
  911. return E_INVALIDARG;
  912. }
  913. }
  914. } while (FALSE); // false loop
  915. }
  916. else if ( !(CCM_SPECIAL_SEPARATOR & fSpecialFlags) )
  917. {
  918. if ( IsReservedCommandID(lCommandID) )
  919. {
  920. TRACE(_T("CContextMenu::DoAddMenuItem(): no new RESERVED menu items"));
  921. ASSERT(FALSE);
  922. return E_INVALIDARG;
  923. }
  924. }
  925. if (NULL == m_pmenuitemRoot)
  926. m_pmenuitemRoot = new CRootMenuItem;
  927. CStr strPath, strLanguageIndependentPath; // this builds up the path of the menu item.
  928. CMenuItem* pParent = ReverseFindMenuItem( lInsertionPointID, ownerID, strPath, strLanguageIndependentPath);
  929. if (NULL == pParent)
  930. {
  931. TRACE(_T("CContextMenu::DoAddMenuItem(): submenu with command ID %ld owner %ld does not exist"), lInsertionPointID, ownerID );
  932. ASSERT(FALSE);
  933. return E_INVALIDARG;
  934. }
  935. MenuItemList& rMenuList = pParent->GetMenuItemSubmenu();
  936. // If this is only a test add, return with success now
  937. if (bTestOnly)
  938. return S_OK;
  939. // get the data object and IExtendContextMenu pointer to set in the item.
  940. IExtendContextMenuPtr spExtendContextMenu;
  941. IDataObject* pDataObject = NULL; // This is used JUST to hold on to the object until Command completes.
  942. // locate the IExtendContextMenu of the snapin.
  943. {
  944. // The selected item was added by an extension
  945. SnapinStruct* psnapin = FindSnapin( ownerID );
  946. if(psnapin != NULL)
  947. {
  948. pDataObject = psnapin->m_pIDataObject;
  949. spExtendContextMenu = psnapin->pIExtendContextMenu;
  950. }
  951. else
  952. {
  953. CTiedComObjectCreator<CNativeExtendContextMenu>::
  954. ScCreateAndConnect(*this, spExtendContextMenu);
  955. // built in items are handled by CContextMenu itself.
  956. }
  957. }
  958. // compute the language independent and language dependent paths for the context menu item.
  959. CStr strLanguageIndependentName = lpszLanguageIndependentName;
  960. tstring tstrName = lpszName ? lpszName : TEXT("");
  961. RemoveAccelerators(tstrName);
  962. CStr strName;
  963. strName = tstrName.data(); // got to standardise on either tstring or CStr
  964. // add a "->" separator to the path if needed
  965. if(!strPath.IsEmpty() && !strName.IsEmpty())
  966. strPath += _T("->");
  967. strPath += strName;
  968. // add a "->" separator to the language independent path if needed
  969. if(!strLanguageIndependentPath.IsEmpty() && !strLanguageIndependentName.IsEmpty())
  970. strLanguageIndependentPath += _T("->");
  971. strLanguageIndependentPath += strLanguageIndependentName;
  972. CMenuItem* pItem = new CMenuItem(
  973. lpszName,
  974. lpszStatusBarText,
  975. lpszLanguageIndependentName,
  976. (LPCTSTR)strPath,
  977. (LPCTSTR)strLanguageIndependentPath,
  978. lCommandID,
  979. m_nNextMenuItemID++,
  980. fFlags,
  981. ownerID,
  982. spExtendContextMenu,
  983. pDataObject,
  984. fSpecialFlags,
  985. bPassCommandBackToSnapin);
  986. ASSERT( pItem );
  987. if (pItem == NULL)
  988. return E_OUTOFMEMORY;
  989. rMenuList.AddTail(pItem);
  990. // If this is a system defined insertion point, update the insertion flags
  991. if (IsSharedInsertionPointID(lCommandID) && !IsCreatePrimaryInsertionPointID(lCommandID))
  992. {
  993. long fFlag = ( 1L << (lCommandID & CCM_INSERTIONPOINTID_MASK_FLAGINDEX));
  994. if (IsAddPrimaryInsertionPointID(lCommandID))
  995. m_fPrimaryInsertionFlags |= fFlag;
  996. if (IsAdd3rdPartyInsertionPointID(lCommandID))
  997. m_fThirdPartyInsertionFlags |= fFlag;
  998. }
  999. // return the item if required
  1000. if (ppMenuItem)
  1001. *ppMenuItem = pItem;
  1002. END_CRITSEC_MENU;
  1003. return S_OK;
  1004. MMC_CATCH
  1005. }
  1006. // APP HACK. Workarounding dependency on older FP where they were QI'ing for IConsole from
  1007. // IContextMenuCallback, which was working in MMC 1.2, but cannot work in mmc 2.0
  1008. // See bug 200621 (Windows bugs (ntbug9) 11/15/2000)
  1009. #define WORKAROUND_FOR_FP_REQUIRED
  1010. #if defined (WORKAROUND_FOR_FP_REQUIRED)
  1011. /***************************************************************************\
  1012. *
  1013. * CLASS: CWorkaroundWrapperForFrontPageMenu
  1014. *
  1015. * PURPOSE: Used from subclassed MMC's IExtendContextMenu interface for FrontPage.
  1016. * Contains (in com sense) IContextMenuCallback2 and IContextMenuCallback by forwarding
  1017. * them to original interface, but in addition supports QI for IConsole.
  1018. * This is a requirement for older FrontPage to work
  1019. *
  1020. \***************************************************************************/
  1021. class CWorkaroundWrapperForFrontPageMenu :
  1022. public IContextMenuCallback,
  1023. public IContextMenuCallback2,
  1024. public IConsole2, // workaround for bug 200621. This is a dummy implementation of IConsole2
  1025. public CComObjectRoot
  1026. {
  1027. friend class CWorkaroundMMCWrapperForFrontPageMenu;
  1028. // pointer to context menu object
  1029. IContextMenuCallbackPtr m_spIContextMenuCallback;
  1030. IContextMenuCallback2Ptr m_spIContextMenuCallback2;
  1031. public:
  1032. typedef CWorkaroundWrapperForFrontPageMenu ThisClass;
  1033. // com entry points
  1034. BEGIN_COM_MAP(ThisClass)
  1035. COM_INTERFACE_ENTRY(IContextMenuCallback) // the IContextMenuProvider and IContextMenu
  1036. COM_INTERFACE_ENTRY(IContextMenuCallback2)
  1037. COM_INTERFACE_ENTRY(IConsole)
  1038. COM_INTERFACE_ENTRY(IConsole2)
  1039. END_COM_MAP()
  1040. // just forward...
  1041. STDMETHOD(AddItem) ( CONTEXTMENUITEM* pItem )
  1042. {
  1043. if ( m_spIContextMenuCallback == NULL )
  1044. return E_UNEXPECTED;
  1045. return m_spIContextMenuCallback->AddItem( pItem );
  1046. }
  1047. // just forward...
  1048. STDMETHOD(AddItem) ( CONTEXTMENUITEM2* pItem )
  1049. {
  1050. if ( m_spIContextMenuCallback2 == NULL )
  1051. return E_UNEXPECTED;
  1052. return m_spIContextMenuCallback2->AddItem( pItem );
  1053. }
  1054. // IConsole2 methods - DUMMY - workaround for bug 200621
  1055. STDMETHOD(SetHeader)( LPHEADERCTRL pHeader) {return E_NOTIMPL;}
  1056. STDMETHOD(SetToolbar)( LPTOOLBAR pToolbar) {return E_NOTIMPL;}
  1057. STDMETHOD(QueryResultView)( LPUNKNOWN* pUnknown) {return E_NOTIMPL;}
  1058. STDMETHOD(QueryScopeImageList)( LPIMAGELIST* ppImageList) {return E_NOTIMPL;}
  1059. STDMETHOD(QueryResultImageList)( LPIMAGELIST* ppImageList) {return E_NOTIMPL;}
  1060. STDMETHOD(UpdateAllViews)( LPDATAOBJECT lpDataObject,LPARAM data,LONG_PTR hint) {return E_NOTIMPL;}
  1061. STDMETHOD(MessageBox)( LPCWSTR lpszText, LPCWSTR lpszTitle,UINT fuStyle, int* piRetval) {return E_NOTIMPL;}
  1062. STDMETHOD(QueryConsoleVerb)( LPCONSOLEVERB * ppConsoleVerb) {return E_NOTIMPL;}
  1063. STDMETHOD(SelectScopeItem)( HSCOPEITEM hScopeItem) {return E_NOTIMPL;}
  1064. STDMETHOD(GetMainWindow)( HWND* phwnd)
  1065. {
  1066. if (!phwnd)
  1067. return E_INVALIDARG;
  1068. *phwnd = (CScopeTree::GetScopeTree() ? CScopeTree::GetScopeTree()->GetMainWindow() : NULL);
  1069. return S_OK;
  1070. }
  1071. STDMETHOD(NewWindow)( HSCOPEITEM hScopeItem, unsigned long lOptions) {return E_NOTIMPL;}
  1072. STDMETHOD(Expand)( HSCOPEITEM hItem, BOOL bExpand) {return E_NOTIMPL;}
  1073. STDMETHOD(IsTaskpadViewPreferred)() {return E_NOTIMPL;}
  1074. STDMETHOD(SetStatusText )( LPOLESTR pszStatusText) {return E_NOTIMPL;}
  1075. };
  1076. /***************************************************************************\
  1077. *
  1078. * CLASS: CWorkaroundMMCWrapperForFrontPageMenu
  1079. *
  1080. * PURPOSE: Subclasses MMC's IExtendContextMenu interface for FrontPage.
  1081. * Contains ( in com sense) IExtendContextMenu; Forwards calls to default MMC implementation,
  1082. * but for AddMenuItems gives itself as a callback interface.
  1083. * [ main purpose to have this object is to avoid changing main MMC functions ]
  1084. * [ to implement this workaround ]
  1085. *
  1086. \***************************************************************************/
  1087. class CWorkaroundMMCWrapperForFrontPageMenu :
  1088. public IExtendContextMenu,
  1089. public CComObjectRoot
  1090. {
  1091. // pointer to context menu object
  1092. IExtendContextMenuPtr m_spExtendContextMenu;
  1093. CNode *m_pNode;
  1094. public:
  1095. typedef CWorkaroundMMCWrapperForFrontPageMenu ThisClass;
  1096. // this method is null for all snapins, but FrontPage
  1097. // for FrontPage it wraps and replaces spIUnknown paramter
  1098. static SC ScSubclassFP(const CLSID& clsid,IUnknownPtr &spIUnknown)
  1099. {
  1100. DECLARE_SC(sc, TEXT("CWorkaroundMMCWrapperForFrontPageMenu::ScSubclassFP"));
  1101. static const CLSID CLSID_Fpsrvmmc = { 0xFF5903A8, 0x78D6, 0x11D1,
  1102. { 0x92, 0xF6, 0x00, 0x60, 0x97, 0xB0, 0x10, 0x56 } };
  1103. // only required intercept one clsid
  1104. if ( clsid != CLSID_Fpsrvmmc )
  1105. return sc;
  1106. // create self
  1107. typedef CComObject<CWorkaroundMMCWrapperForFrontPageMenu> ThisComObj_t;
  1108. ThisComObj_t *pObj = NULL;
  1109. sc = ThisComObj_t::CreateInstance(&pObj);
  1110. if (sc)
  1111. return sc;
  1112. // cast to avoid member access problems (workarounding compiler)
  1113. ThisClass *pThis = pObj;
  1114. sc = ScCheckPointers( pThis, E_UNEXPECTED );
  1115. if (sc)
  1116. return sc;
  1117. // maintain the lifetime in case of accident
  1118. IUnknownPtr spThis = pThis->GetUnknown();
  1119. // grab on snapin's interface
  1120. pThis->m_spExtendContextMenu = spIUnknown;
  1121. sc = ScCheckPointers( pThis->m_spExtendContextMenu, E_UNEXPECTED );
  1122. if (sc)
  1123. return sc;
  1124. // substitute the snapin (in-out parameter)
  1125. spIUnknown = spThis;
  1126. return sc;
  1127. }
  1128. // com entry points
  1129. BEGIN_COM_MAP(ThisClass)
  1130. COM_INTERFACE_ENTRY(IExtendContextMenu)
  1131. END_COM_MAP()
  1132. // AddMenuItems is the method this object exists for.
  1133. // If we got here, mmc is about to ask FrontPage to add its items to context menu.
  1134. // We'll wrap the callback interface given by MMC with the object implementing
  1135. // phony IConsole - this is required for older FP to work
  1136. STDMETHOD(AddMenuItems)( LPDATAOBJECT piDataObject, LPCONTEXTMENUCALLBACK piCallback, long * pInsertionAllowed )
  1137. {
  1138. DECLARE_SC(sc, TEXT("CWorkaroundMMCWrapperForFrontPageMenu::AddMenuItems"));
  1139. IContextMenuCallbackPtr spIContextMenuCallback = piCallback;
  1140. IContextMenuCallback2Ptr spIContextMenuCallback2 = piCallback;
  1141. if ( m_spExtendContextMenu == NULL || spIContextMenuCallback == NULL || spIContextMenuCallback2 == NULL )
  1142. return E_UNEXPECTED;
  1143. // create a wrapper for FP
  1144. typedef CComObject<CWorkaroundWrapperForFrontPageMenu> WrapperComObj_t;
  1145. WrapperComObj_t *pObj = NULL;
  1146. sc = WrapperComObj_t::CreateInstance(&pObj);
  1147. if (sc)
  1148. return sc.ToHr();
  1149. // cast to avoid member access problems (workarounding compiler)
  1150. CWorkaroundWrapperForFrontPageMenu *pWrapper = pObj;
  1151. sc = ScCheckPointers( pWrapper, E_UNEXPECTED );
  1152. if (sc)
  1153. return sc.ToHr();
  1154. // maintain the lifetime in case of accident
  1155. IUnknownPtr spWrapper = pWrapper->GetUnknown();
  1156. // grab on snapin's interface
  1157. pWrapper->m_spIContextMenuCallback = spIContextMenuCallback;
  1158. pWrapper->m_spIContextMenuCallback2 = spIContextMenuCallback2;
  1159. // call snapin on behave on mmc, but pass itself as callback
  1160. sc = m_spExtendContextMenu->AddMenuItems( piDataObject, pWrapper, pInsertionAllowed );
  1161. // fall thru even on error - need to release interfaces
  1162. // reset callback interfaces - not valid after the call anyway...
  1163. // this will let context menu go, and prevent FP from suicide (AV);
  1164. // Following this all calls to IContextMenuCallback would fail,
  1165. // but that's ok, since it is not legal to call them after AddMenuItems.
  1166. pWrapper->m_spIContextMenuCallback = NULL;
  1167. pWrapper->m_spIContextMenuCallback2 = NULL;
  1168. return sc.ToHr();
  1169. }
  1170. // simply forward....
  1171. STDMETHOD(Command)(long lCommandID, LPDATAOBJECT piDataObject)
  1172. {
  1173. ASSERT( m_spExtendContextMenu != NULL );
  1174. if ( m_spExtendContextMenu == NULL )
  1175. return E_UNEXPECTED;
  1176. return m_spExtendContextMenu->Command(lCommandID, piDataObject);
  1177. }
  1178. };
  1179. #endif // defined (WORKAROUND_FOR_FP_REQUIRED)
  1180. // critsec should already be claimed
  1181. SC CContextMenu::ScAddSnapinToList_GUID(
  1182. const CLSID& clsid,
  1183. IDataObject* piDataObject,
  1184. MENU_OWNER_ID ownerID )
  1185. {
  1186. DECLARE_SC(sc, TEXT("CContextMenu::ScAddSnapinToList_GUID"));
  1187. // cocreate extension
  1188. IUnknownPtr spIUnknown;
  1189. sc = ::CoCreateInstance(clsid, NULL, MMC_CLSCTX_INPROC,
  1190. IID_IUnknown, (LPVOID*)&spIUnknown);
  1191. if (sc)
  1192. return sc;
  1193. #if defined (WORKAROUND_FOR_FP_REQUIRED)
  1194. sc = CWorkaroundMMCWrapperForFrontPageMenu::ScSubclassFP(clsid, spIUnknown);
  1195. #endif // defined (WORKAROUND_FOR_FP_REQUIRED)
  1196. // get IExtendContextMenu interface
  1197. IExtendContextMenuPtr spIExtendContextMenu = spIUnknown;
  1198. sc = ScCheckPointers(spIExtendContextMenu, E_NOINTERFACE);
  1199. if (sc)
  1200. return sc;
  1201. // add menu items
  1202. sc = ScAddSnapinToList_IExtendContextMenu(spIExtendContextMenu,
  1203. piDataObject, ownerID );
  1204. if (sc)
  1205. return sc;
  1206. return sc;
  1207. }
  1208. // does not AddRef() or Release() interface pointer
  1209. // critsec should already be claimed
  1210. SC CContextMenu::ScAddSnapinToList_IUnknown(
  1211. IUnknown* piExtension,
  1212. IDataObject* piDataObject,
  1213. MENU_OWNER_ID ownerID )
  1214. {
  1215. DECLARE_SC(sc, TEXT("CContextMenu::AddSnapinToList_IUnknown"));
  1216. // parameter check
  1217. sc = ScCheckPointers(piExtension);
  1218. if (sc)
  1219. return sc;
  1220. IExtendContextMenuPtr spIExtendContextMenu = piExtension;
  1221. if (spIExtendContextMenu == NULL)
  1222. return sc; // snapin does not extend context menus
  1223. // add menu items
  1224. sc = ScAddSnapinToList_IExtendContextMenu( spIExtendContextMenu, piDataObject, ownerID );
  1225. if (sc)
  1226. return sc;
  1227. return sc;
  1228. }
  1229. // Interface pointer is Release()d when menu list is emptied
  1230. // critsec should already be claimed
  1231. SC CContextMenu::ScAddSnapinToList_IExtendContextMenu(
  1232. IExtendContextMenu* pIExtendContextMenu,
  1233. IDataObject* piDataObject,
  1234. MENU_OWNER_ID ownerID )
  1235. {
  1236. DECLARE_SC(sc, TEXT("CContextMenu::ScAddSnapinToList_IExtendContextMenu"));
  1237. // parameter check
  1238. sc = ScCheckPointers(pIExtendContextMenu);
  1239. if (sc)
  1240. return sc;
  1241. SnapinStruct* psnapstruct = new SnapinStruct( pIExtendContextMenu, piDataObject, ownerID );
  1242. sc = ScCheckPointers(psnapstruct, E_OUTOFMEMORY);
  1243. if (sc)
  1244. return sc;
  1245. m_SnapinList->AddTail(psnapstruct);
  1246. m_CurrentExtensionOwnerID = ownerID;
  1247. long fInsertionFlags = IsPrimaryOwnerID(ownerID) ? m_fPrimaryInsertionFlags : m_fThirdPartyInsertionFlags;
  1248. // if view items are requested, then allow only view items
  1249. // view item requests go to the IComponent. If other item types are allowed there
  1250. // will be a second pass through this code directed to the IComponentData.
  1251. long lTempFlags = fInsertionFlags;
  1252. if ( fInsertionFlags & CCM_INSERTIONALLOWED_VIEW )
  1253. lTempFlags = CCM_INSERTIONALLOWED_VIEW;
  1254. try
  1255. {
  1256. sc = pIExtendContextMenu->AddMenuItems( piDataObject, this, &lTempFlags );
  1257. #ifdef DBG
  1258. if (sc)
  1259. TraceSnapinError(_T("IExtendContextMenu::AddMenuItems failed"), sc);
  1260. #endif
  1261. }
  1262. catch (...)
  1263. {
  1264. if (DOBJ_CUSTOMOCX == piDataObject)
  1265. {
  1266. ASSERT( FALSE && "IExtendContextMenu::AddMenuItem of IComponent is called with DOBJ_CUSTOMOCX and snapin derefed this custom data object. Please handle special dataobjects in your snapin.");
  1267. sc = E_UNEXPECTED;
  1268. }
  1269. else if (DOBJ_CUSTOMWEB == piDataObject)
  1270. {
  1271. ASSERT( FALSE && "IExtendContextMenu::AddMenuItem of IComponent is called with DOBJ_CUSTOMWEB and snapin derefed this custom data object. Please handle special dataobjects in your snapin.");
  1272. sc = E_UNEXPECTED;
  1273. }
  1274. else
  1275. {
  1276. ASSERT( FALSE && "IExtendContextMenu::AddMenuItem implemented by snapin has thrown an exception.");
  1277. sc = E_UNEXPECTED;
  1278. }
  1279. }
  1280. m_CurrentExtensionOwnerID = OWNERID_NATIVE;
  1281. if (sc)
  1282. return sc;
  1283. // Primary snapin is allowed to clear extension snapin insertion flags
  1284. if ( IsPrimaryOwnerID(ownerID) )
  1285. m_fThirdPartyInsertionFlags &= fInsertionFlags;
  1286. return sc;
  1287. }
  1288. // All snapin interface pointers are Release()d
  1289. // critsec should already be claimed
  1290. void CContextMenu::ReleaseSnapinList()
  1291. {
  1292. ASSERT(m_SnapinList != NULL);
  1293. if (m_SnapinList != NULL && m_SnapinList->GetCount() != 0)
  1294. {
  1295. POSITION pos = m_SnapinList->GetHeadPosition();
  1296. while(pos)
  1297. {
  1298. SnapinStruct* pItem = (SnapinStruct*)m_SnapinList->GetNext(pos);
  1299. ASSERT_OBJECTPTR( pItem );
  1300. delete pItem;
  1301. }
  1302. m_SnapinList->RemoveAll();
  1303. }
  1304. }
  1305. // critsec should already be claimed
  1306. SnapinStruct* CContextMenu::FindSnapin( MENU_OWNER_ID ownerID )
  1307. {
  1308. ASSERT(m_SnapinList != NULL);
  1309. if (m_SnapinList != NULL && m_SnapinList->GetCount() != 0)
  1310. {
  1311. POSITION pos = m_SnapinList->GetHeadPosition();
  1312. while(pos)
  1313. {
  1314. SnapinStruct* pItem = (SnapinStruct*)m_SnapinList->GetNext(pos);
  1315. ASSERT( NULL != pItem );
  1316. if ( ownerID == pItem->m_OwnerID )
  1317. return pItem;
  1318. }
  1319. }
  1320. return NULL;
  1321. }
  1322. // Worker function, called recursively by ShowContextMenu
  1323. // critsec should already be claimed
  1324. HRESULT CollapseInsertionPoints( CMenuItem* pmenuitemParent )
  1325. {
  1326. ASSERT( NULL != pmenuitemParent && !pmenuitemParent->IsSpecialInsertionPoint() );
  1327. MenuItemList& rMenuList = pmenuitemParent->GetMenuItemSubmenu();
  1328. POSITION pos = rMenuList.GetHeadPosition();
  1329. while(pos)
  1330. {
  1331. POSITION posThisItem = pos;
  1332. CMenuItem* pItem = (rMenuList.GetNext(pos));
  1333. ASSERT( pItem != NULL );
  1334. if ( pItem->IsPopupMenu() )
  1335. {
  1336. ASSERT( !pItem->IsSpecialInsertionPoint() );
  1337. HRESULT hr = CollapseInsertionPoints( pItem );
  1338. if ( FAILED(hr) )
  1339. {
  1340. ASSERT( FALSE );
  1341. return hr;
  1342. }
  1343. continue;
  1344. }
  1345. if ( !pItem->IsSpecialInsertionPoint() )
  1346. continue;
  1347. // we found an insertion point, move its items into this list
  1348. MenuItemList& rInsertedList = pItem->GetMenuItemSubmenu();
  1349. POSITION posInsertAfterThis = posThisItem;
  1350. while ( !rInsertedList.IsEmpty() )
  1351. {
  1352. CMenuItem* pInsertedItem = rInsertedList.RemoveHead();
  1353. posInsertAfterThis = rMenuList.InsertAfter( posInsertAfterThis, pInsertedItem );
  1354. }
  1355. // delete the insertion point item
  1356. rMenuList.RemoveAt(posThisItem);
  1357. delete pItem;
  1358. // restart at head of list, in case of recursive insertion points
  1359. pos = rMenuList.GetHeadPosition();
  1360. }
  1361. return S_OK;
  1362. }
  1363. // Worker function, called recursively by ShowContextMenu
  1364. // critsec should already be claimed and CollapseInsertionPoints should have been called
  1365. HRESULT CollapseSpecialSeparators( CMenuItem* pmenuitemParent )
  1366. {
  1367. ASSERT( NULL != pmenuitemParent && !pmenuitemParent->IsSpecialInsertionPoint() );
  1368. MenuItemList& rMenuList = pmenuitemParent->GetMenuItemSubmenu();
  1369. CMenuItem* pItem = NULL;
  1370. BOOL fLastItemWasReal = FALSE;
  1371. POSITION pos = rMenuList.GetHeadPosition();
  1372. POSITION posThisItem = pos;
  1373. while(pos)
  1374. {
  1375. posThisItem = pos;
  1376. pItem = (rMenuList.GetNext(pos));
  1377. ASSERT( pItem != NULL );
  1378. ASSERT( !pItem->IsSpecialInsertionPoint() );
  1379. if ( pItem->IsPopupMenu() )
  1380. {
  1381. ASSERT( !pItem->IsSpecialSeparator() );
  1382. HRESULT hr = CollapseSpecialSeparators( pItem );
  1383. if ( FAILED(hr) )
  1384. {
  1385. ASSERT( FALSE );
  1386. return hr;
  1387. }
  1388. fLastItemWasReal = TRUE;
  1389. continue;
  1390. }
  1391. if ( !pItem->IsSpecialSeparator() )
  1392. {
  1393. fLastItemWasReal = TRUE;
  1394. continue;
  1395. }
  1396. if ( fLastItemWasReal )
  1397. {
  1398. fLastItemWasReal = FALSE;
  1399. continue;
  1400. }
  1401. // Found two consecutive special separators, or special seperator as first item
  1402. // delete the insertion point item
  1403. rMenuList.RemoveAt(posThisItem);
  1404. delete pItem;
  1405. }
  1406. if ( !fLastItemWasReal && !rMenuList.IsEmpty() )
  1407. {
  1408. // Found special separator as last item
  1409. delete rMenuList.RemoveTail();
  1410. }
  1411. return S_OK;
  1412. }
  1413. // Worker function, called recursively by ShowContextMenu
  1414. // critsec should already be claimed
  1415. HRESULT BuildContextMenu( WTL::CMenu& menu,
  1416. CMenuItem* pmenuitemParent )
  1417. {
  1418. MenuItemList& rMenuList = pmenuitemParent->GetMenuItemSubmenu();
  1419. int nCount = 0;
  1420. bool fInsertedItemSinceLastSeparator = false;
  1421. POSITION pos = rMenuList.GetHeadPosition();
  1422. while(pos)
  1423. {
  1424. CMenuItem* pItem = (rMenuList.GetNext(pos));
  1425. ASSERT( pItem != NULL );
  1426. ASSERT( !pItem->IsSpecialInsertionPoint() );
  1427. UINT_PTR nCommandID = pItem->GetMenuItemID();
  1428. long nFlags = pItem->GetMenuItemFlags();
  1429. /*
  1430. * special processing for submenus
  1431. */
  1432. if ( pItem->IsPopupMenu() )
  1433. {
  1434. // add items to a submenu
  1435. WTL::CMenu submenu;
  1436. VERIFY( submenu.CreatePopupMenu() );
  1437. HRESULT hr = BuildContextMenu( submenu, pItem );
  1438. if ( FAILED(hr) )
  1439. return hr;
  1440. HMENU hSubmenu = submenu.Detach();
  1441. ASSERT( NULL != hSubmenu );
  1442. nCommandID = (UINT_PTR)hSubmenu;
  1443. pItem->SetPopupMenuHandle( hSubmenu );
  1444. if ( pItem->IsSpecialSubmenu() )
  1445. {
  1446. MenuItemList& rChildMenuList = pItem->GetMenuItemSubmenu();
  1447. if ( rChildMenuList.IsEmpty() )
  1448. {
  1449. // Bug 151435: remove instead of disabling unused submenus
  1450. // pItem->SetMenuItemFlags(nFlags | (MF_GRAYED|MF_DISABLED));
  1451. ::DestroyMenu(hSubmenu);
  1452. continue;
  1453. }
  1454. }
  1455. fInsertedItemSinceLastSeparator = true;
  1456. }
  1457. /*
  1458. * special processing for separators
  1459. */
  1460. else if (nFlags & MF_SEPARATOR)
  1461. {
  1462. /*
  1463. * if we haven't inserted an item since the last separator,
  1464. * we don't want to insert this one or we'll have consecutive
  1465. * separators, or an unnecessary separator at the top of the menu
  1466. */
  1467. if (!fInsertedItemSinceLastSeparator)
  1468. continue;
  1469. /*
  1470. * if there aren't any more items after this separator,
  1471. * we don't want to insert this one or we'll have an
  1472. * unnecessary separator at the bottom of the menu
  1473. */
  1474. if (pos == NULL)
  1475. continue;
  1476. fInsertedItemSinceLastSeparator = false;
  1477. }
  1478. /*
  1479. * just a normal menu item
  1480. */
  1481. else
  1482. {
  1483. fInsertedItemSinceLastSeparator = true;
  1484. }
  1485. if (!menu.AppendMenu(nFlags, nCommandID, pItem->GetMenuItemName()))
  1486. {
  1487. #ifdef DBG
  1488. TRACE(_T("BuildContextMenu: AppendMenu(%ld, %ld, \"%s\") reports error\n"),
  1489. nFlags,
  1490. nCommandID,
  1491. SAFEDBGTCHAR(pItem->GetMenuItemName()) );
  1492. #endif
  1493. ASSERT( FALSE );
  1494. return E_UNEXPECTED;
  1495. }
  1496. if (pItem->IsSpecialItemDefault())
  1497. {
  1498. VERIFY( ::SetMenuDefaultItem(menu, nCount, TRUE) );
  1499. }
  1500. ++nCount;
  1501. }
  1502. return S_OK;
  1503. }
  1504. /*+-------------------------------------------------------------------------*
  1505. * CContextMenu::BuildContextMenu
  1506. *
  1507. * PURPOSE:
  1508. *
  1509. * PARAMETERS:
  1510. * WTL::CMenu & menu:
  1511. *
  1512. * RETURNS:
  1513. * HRESULT
  1514. /*+-------------------------------------------------------------------------*/
  1515. HRESULT
  1516. CContextMenu::BuildContextMenu(WTL::CMenu &menu)
  1517. {
  1518. HRESULT hr = S_OK;
  1519. hr = ::CollapseInsertionPoints( m_pmenuitemRoot );
  1520. if ( FAILED(hr) )
  1521. return hr;
  1522. hr = ::CollapseSpecialSeparators( m_pmenuitemRoot );
  1523. if ( FAILED(hr) )
  1524. return hr;
  1525. hr = ::BuildContextMenu( menu, m_pmenuitemRoot );
  1526. if ( FAILED(hr) )
  1527. return hr;
  1528. UINT iItems = menu.GetMenuItemCount();
  1529. if ((UINT)-1 == iItems)
  1530. {
  1531. TRACE(_T("CContextMenu::BuildContextMenu(): itemcount error"));
  1532. ASSERT( FALSE );
  1533. return E_UNEXPECTED;
  1534. }
  1535. else if (0 >= iItems)
  1536. {
  1537. TRACE(_T("CContextMenu::BuildContextMenu(): no items added"));
  1538. return S_OK;
  1539. }
  1540. return hr;
  1541. }
  1542. /*+-------------------------------------------------------------------------*
  1543. * CContextMenu::ShowContextMenu
  1544. *
  1545. * PURPOSE:
  1546. *
  1547. * PARAMETERS:
  1548. * WND hwndParent:
  1549. * LONG xPos:
  1550. * LONG yPos:
  1551. * LONG* plSelected:
  1552. *
  1553. * RETURNS:
  1554. * HRESULT
  1555. /*+-------------------------------------------------------------------------*/
  1556. STDMETHODIMP
  1557. CContextMenu::ShowContextMenu( HWND hwndParent, LONG xPos,
  1558. LONG yPos, LONG* plSelected)
  1559. {
  1560. return (ShowContextMenuEx (hwndParent, xPos, yPos, NULL/*prcExclude*/,
  1561. true/*bAllowDefaultMenuItem*/, plSelected));
  1562. }
  1563. STDMETHODIMP
  1564. CContextMenu::ShowContextMenuEx(HWND hwndParent, LONG xPos,
  1565. LONG yPos, LPCRECT prcExclude,
  1566. bool bAllowDefaultMenuItem, LONG* plSelected)
  1567. {
  1568. DECLARE_SC(sc, _T("IContextMenuProvider::ShowContextMenuEx"));
  1569. if (NULL == plSelected)
  1570. {
  1571. sc = E_INVALIDARG;
  1572. TraceSnapinError(_T("NULL selected ptr"), sc);
  1573. return sc.ToHr();
  1574. }
  1575. *plSelected = 0;
  1576. WTL::CMenu menu;
  1577. VERIFY( menu.CreatePopupMenu() );
  1578. START_CRITSEC_BOTH;
  1579. if (NULL == m_pmenuitemRoot)
  1580. return sc.ToHr();
  1581. sc = BuildContextMenu(menu); // build the context menu
  1582. if (sc)
  1583. return sc.ToHr();
  1584. CMenuItem* pItem = NULL;
  1585. LONG lSelected = 0;
  1586. CConsoleStatusBar *pStatusBar = GetStatusBar();
  1587. // At this point, pStatusBar should be non-NULL, either because
  1588. // 1) This function was called by CNodeInitObject, which calls SetStatusBar() first,
  1589. // or 2) by the object model, where m_pNode is always non-NULL.
  1590. ASSERT(pStatusBar);
  1591. // set up the menu command sink and hook up the status bar.
  1592. CCommandSink comsink( *this, menu, pStatusBar);
  1593. if ( !comsink.Init() )
  1594. {
  1595. sc = E_UNEXPECTED;
  1596. TraceNodeMgrLegacy(_T("CContextMenu::ShowContextMenuEx(): comsink error\n"), sc);
  1597. return sc.ToHr();
  1598. }
  1599. /*
  1600. * if we got an exclusion rectangle, set up a TPMPARAMS to specify it
  1601. */
  1602. TPMPARAMS* ptpm = NULL;
  1603. TPMPARAMS tpm;
  1604. if (prcExclude != NULL)
  1605. {
  1606. tpm.cbSize = sizeof(tpm);
  1607. tpm.rcExclude = *prcExclude;
  1608. ptpm = &tpm;
  1609. }
  1610. /*
  1611. * Bug 139708: menu bar popups shouldn't have default menu items. If
  1612. * we can't have one on this popup, remove any default item now.
  1613. */
  1614. if (!bAllowDefaultMenuItem)
  1615. SetMenuDefaultItem (menu, -1, false);
  1616. lSelected = menu.TrackPopupMenuEx(
  1617. TPM_RETURNCMD | TPM_NONOTIFY | TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_VERTICAL,
  1618. xPos,
  1619. yPos,
  1620. comsink.m_hWnd, // CODEWORK can we eliminate this?
  1621. ptpm );
  1622. comsink.DestroyWindow();
  1623. pItem = (0 == lSelected) ? NULL : FindMenuItem( lSelected );
  1624. if ( pItem != NULL )
  1625. {
  1626. // execute the menu item
  1627. sc = ExecuteMenuItem(pItem);
  1628. if(sc)
  1629. return sc.ToHr();
  1630. // in some cases we'll need to pass command to the sanpin
  1631. if ( pItem->NeedsToPassCommandBackToSnapin() )
  1632. *plSelected = pItem->GetCommandID();
  1633. }
  1634. else
  1635. ASSERT( 0 == lSelected ); // no items selected.
  1636. END_CRITSEC_BOTH;
  1637. return sc.ToHr();
  1638. }
  1639. HRESULT
  1640. CContextMenu::ExecuteMenuItem(CMenuItem *pItem)
  1641. {
  1642. DECLARE_SC(sc, TEXT("CContextMenu::ExecuteMenuItem"));
  1643. sc = ScCheckPointers(pItem);
  1644. if(sc)
  1645. return sc.ToHr();
  1646. // execute it;
  1647. sc = pItem->ScExecute();
  1648. if(sc)
  1649. return sc.ToHr();
  1650. return sc.ToHr();
  1651. }