Leaked source code of windows server 2003
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.

666 lines
19 KiB

  1. #include "priv.h"
  2. #include "tbmenu.h"
  3. #include "isfband.h"
  4. #include "isfmenu.h"
  5. #include "mluisupp.h"
  6. #define SMFORWARD(x) if (!_psm) { return E_FAIL; } else return _psm->x
  7. typedef struct
  8. {
  9. WNDPROC pfnOriginal;
  10. IMenuBand* pmb;
  11. } MENUHOOK;
  12. #define SZ_MENUHOOKPROP TEXT("MenuHookProp")
  13. LRESULT CALLBACK MenuHookWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  14. {
  15. MENUHOOK* pmh = (MENUHOOK*)GetProp(hwnd, SZ_MENUHOOKPROP);
  16. if (pmh)
  17. {
  18. MSG msg;
  19. LRESULT lres;
  20. msg.hwnd = hwnd;
  21. msg.message = uMsg;
  22. msg.wParam = wParam;
  23. msg.lParam = lParam;
  24. if (pmh->pmb->TranslateMenuMessage(&msg, &lres) == S_OK)
  25. return lres;
  26. wParam = msg.wParam;
  27. lParam = msg.lParam;
  28. return CallWindowProc(pmh->pfnOriginal, hwnd, uMsg, wParam, lParam);
  29. }
  30. return 0;
  31. }
  32. HRESULT HookMenuWindow(HWND hwnd, IMenuBand* pmb)
  33. {
  34. HRESULT hres = E_FAIL;
  35. ASSERT(IsWindow(hwnd));
  36. // make sure we haven't already hooked this window
  37. if (GetProp(hwnd, SZ_MENUHOOKPROP) == NULL)
  38. {
  39. MENUHOOK* pmh = new MENUHOOK;
  40. if (pmh)
  41. {
  42. pmh->pfnOriginal = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
  43. pmh->pmb = pmb;
  44. SetProp(hwnd, SZ_MENUHOOKPROP, pmh);
  45. SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)MenuHookWndProc);
  46. hres = S_OK;
  47. }
  48. }
  49. return hres;
  50. }
  51. void UnHookMenuWindow(HWND hwnd)
  52. {
  53. MENUHOOK* pmh = (MENUHOOK*)GetProp(hwnd, SZ_MENUHOOKPROP);
  54. if (pmh)
  55. {
  56. SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) pmh->pfnOriginal);
  57. SetProp(hwnd, SZ_MENUHOOKPROP, NULL);
  58. delete pmh;
  59. }
  60. }
  61. // This class is here to implement a "Menu Filter". We need this because the old style of
  62. // implementing obscured Menus does not work because user munges the WM_INITMENUPOPUP information
  63. // based on the relative position within the HMENU. So here we keep that information, we just hide the item.
  64. class CShellMenuCallbackWrapper : public IShellMenuCallback,
  65. public CObjectWithSite
  66. {
  67. int _cRef;
  68. IShellMenuCallback* _psmc;
  69. HWND _hwnd;
  70. RECT _rcTB;
  71. ~CShellMenuCallbackWrapper()
  72. {
  73. ATOMICRELEASE(_psmc);
  74. }
  75. public:
  76. CShellMenuCallbackWrapper(HWND hwnd, IShellMenuCallback* psmc) : _cRef(1)
  77. {
  78. _psmc = psmc;
  79. if (_psmc)
  80. _psmc->AddRef();
  81. _hwnd = hwnd;
  82. GetClientRect(_hwnd, &_rcTB);
  83. }
  84. // *** IUnknown methods ***
  85. STDMETHODIMP QueryInterface (REFIID riid, LPVOID * ppvObj)
  86. {
  87. static const QITAB qit[] =
  88. {
  89. QITABENT(CShellMenuCallbackWrapper, IShellMenuCallback),
  90. QITABENT(CShellMenuCallbackWrapper, IObjectWithSite),
  91. { 0 },
  92. };
  93. return QISearch(this, qit, riid, ppvObj);
  94. }
  95. STDMETHODIMP_(ULONG) AddRef()
  96. {
  97. _cRef++;
  98. return _cRef;
  99. }
  100. STDMETHODIMP_(ULONG) Release()
  101. {
  102. ASSERT(_cRef > 0);
  103. _cRef--;
  104. if (_cRef > 0)
  105. return _cRef;
  106. delete this;
  107. return 0;
  108. }
  109. // *** CObjectWithSite methods (override)***
  110. STDMETHODIMP SetSite(IUnknown* punk) { return IUnknown_SetSite(_psmc, punk); }
  111. STDMETHODIMP GetSite(REFIID riid, void** ppObj) { return IUnknown_GetSite(_psmc, riid, ppObj); }
  112. // *** IShellMenuCallback methods ***
  113. STDMETHODIMP CallbackSM(LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  114. {
  115. HRESULT hres = S_FALSE;
  116. if (_psmc)
  117. hres = _psmc->CallbackSM(psmd, uMsg, wParam, lParam);
  118. if (uMsg == SMC_GETINFO)
  119. {
  120. SMINFO* psminfo = (SMINFO*)lParam;
  121. int iPos = (int)SendMessage(_hwnd, TB_COMMANDTOINDEX, psmd->uId, 0);
  122. if (psminfo->dwMask & SMIM_FLAGS &&
  123. iPos >= 0 &&
  124. !SHIsButtonObscured(_hwnd, &_rcTB, iPos))
  125. {
  126. psminfo->dwFlags |= SMIF_HIDDEN;
  127. hres = S_OK;
  128. }
  129. }
  130. return hres;
  131. }
  132. };
  133. //
  134. // CTrackShellMenu implementation
  135. //
  136. STDAPI CTrackShellMenu_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  137. {
  138. HRESULT hres = E_OUTOFMEMORY;
  139. CTrackShellMenu* pObj = new CTrackShellMenu();
  140. if (pObj)
  141. {
  142. hres = pObj->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
  143. pObj->Release();
  144. }
  145. return hres;
  146. }
  147. CTrackShellMenu::CTrackShellMenu() : _cRef(1)
  148. {
  149. if (SUCCEEDED(CoCreateInstance(CLSID_MenuBand, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellMenu, &_psm))))
  150. {
  151. _psm->QueryInterface(IID_PPV_ARG(IShellMenu2, &_psm2));
  152. }
  153. }
  154. CTrackShellMenu::~CTrackShellMenu()
  155. {
  156. ATOMICRELEASE(_psm2);
  157. ATOMICRELEASE(_psm);
  158. ATOMICRELEASE(_psmClient);
  159. ASSERT(!_punkSite); // else someone neglected to call matching SetSite(NULL)
  160. }
  161. ULONG CTrackShellMenu::AddRef()
  162. {
  163. _cRef++;
  164. return _cRef;
  165. }
  166. ULONG CTrackShellMenu::Release()
  167. {
  168. ASSERT(_cRef > 0);
  169. _cRef--;
  170. if (_cRef > 0)
  171. return _cRef;
  172. delete this;
  173. return 0;
  174. }
  175. HRESULT CTrackShellMenu::QueryInterface(REFIID riid, void **ppvObj)
  176. {
  177. static const QITAB qit[] = {
  178. QITABENTMULTI(CTrackShellMenu, IShellMenu, ITrackShellMenu),
  179. QITABENT(CTrackShellMenu, ITrackShellMenu),
  180. QITABENT(CTrackShellMenu, IShellMenu2),
  181. QITABENT(CTrackShellMenu, IObjectWithSite),
  182. QITABENT(CTrackShellMenu, IServiceProvider),
  183. { 0 },
  184. };
  185. HRESULT hres = QISearch(this, qit, riid, ppvObj);
  186. return hres;
  187. }
  188. // *** IServiceProvider methods ***
  189. HRESULT CTrackShellMenu::QueryService(REFGUID guidService,
  190. REFIID riid, void **ppvObj)
  191. {
  192. return IUnknown_QueryService(_psm, guidService, riid, ppvObj);
  193. }
  194. // *** IShellMenu methods ***
  195. STDMETHODIMP CTrackShellMenu::Initialize(IShellMenuCallback* psmc, UINT uId, UINT uIdAncestor, DWORD dwFlags)
  196. { SMFORWARD(Initialize(psmc, uId, uIdAncestor, dwFlags)); }
  197. STDMETHODIMP CTrackShellMenu::GetMenuInfo(IShellMenuCallback** ppsmc, UINT* puId, UINT* puIdAncestor, DWORD* pdwFlags)
  198. { SMFORWARD(GetMenuInfo(ppsmc, puId, puIdAncestor, pdwFlags)); }
  199. STDMETHODIMP CTrackShellMenu::SetShellFolder(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HKEY hkey, DWORD dwFlags)
  200. { SMFORWARD(SetShellFolder(psf, pidlFolder, hkey, dwFlags)); }
  201. STDMETHODIMP CTrackShellMenu::GetShellFolder(DWORD* pdwFlags, LPITEMIDLIST* ppidl, REFIID riid, void** ppvObj)
  202. { SMFORWARD(GetShellFolder(pdwFlags, ppidl, riid, ppvObj)); }
  203. STDMETHODIMP CTrackShellMenu::SetMenu(HMENU hmenu, HWND hwnd, DWORD dwFlags)
  204. { SMFORWARD(SetMenu(hmenu, hwnd, dwFlags)); }
  205. STDMETHODIMP CTrackShellMenu::GetMenu(HMENU* phmenu, HWND* phwnd, DWORD* pdwFlags)
  206. { SMFORWARD(GetMenu(phmenu, phwnd, pdwFlags)); }
  207. STDMETHODIMP CTrackShellMenu::InvalidateItem(LPSMDATA psmd, DWORD dwFlags)
  208. { SMFORWARD(InvalidateItem(psmd, dwFlags)); }
  209. STDMETHODIMP CTrackShellMenu::GetState(LPSMDATA psmd)
  210. { SMFORWARD(GetState(psmd)); }
  211. STDMETHODIMP CTrackShellMenu::SetMenuToolbar(IUnknown* punk, DWORD dwFlags)
  212. { SMFORWARD(SetMenuToolbar(punk, dwFlags)); }
  213. STDMETHODIMP CTrackShellMenu::GetSubMenu(UINT idCmd, REFIID riid, void **ppvObj)
  214. {
  215. if (_psm2)
  216. {
  217. return _psm2->GetSubMenu(idCmd, riid, ppvObj);
  218. }
  219. else
  220. {
  221. return E_NOTIMPL;
  222. }
  223. }
  224. STDMETHODIMP CTrackShellMenu::SetToolbar(HWND hwnd, DWORD dwFlags)
  225. {
  226. if (_psm2)
  227. {
  228. return _psm2->SetToolbar(hwnd, dwFlags);
  229. }
  230. else
  231. {
  232. return E_NOTIMPL;
  233. }
  234. }
  235. STDMETHODIMP CTrackShellMenu::SetMinWidth(int cxMenu)
  236. {
  237. if (_psm2)
  238. {
  239. return _psm2->SetMinWidth(cxMenu);
  240. }
  241. else
  242. {
  243. return E_NOTIMPL;
  244. }
  245. }
  246. STDMETHODIMP CTrackShellMenu::SetNoBorder(BOOL fNoBorder)
  247. {
  248. if (_psm2)
  249. {
  250. return _psm2->SetNoBorder(fNoBorder);
  251. }
  252. else
  253. {
  254. return E_NOTIMPL;
  255. }
  256. }
  257. STDMETHODIMP CTrackShellMenu::SetTheme(LPCWSTR pszTheme)
  258. {
  259. if (_psm2)
  260. {
  261. return _psm2->SetTheme(pszTheme);
  262. }
  263. else
  264. {
  265. return E_NOTIMPL;
  266. }
  267. }
  268. // *** ITrackShellMenu methods ***
  269. HRESULT CTrackShellMenu::SetObscured(HWND hwndTB, IUnknown* punkBand, DWORD dwSMSetFlags)
  270. {
  271. HRESULT hr = E_OUTOFMEMORY;
  272. // Make sure we created the Inner Shell Menu
  273. if (!_psm)
  274. return hr;
  275. if (punkBand &&
  276. SUCCEEDED(punkBand->QueryInterface(IID_PPV_ARG(IShellMenu, &_psmClient))))
  277. {
  278. UINT uId, uIdAncestor;
  279. DWORD dwFlags;
  280. IShellMenuCallback* psmcb;
  281. hr = _psmClient->GetMenuInfo(&psmcb, &uId, &uIdAncestor, &dwFlags);
  282. if (SUCCEEDED(hr))
  283. {
  284. IShellMenuCallback* psmcbClone = NULL;
  285. if (psmcb)
  286. {
  287. if (S_FALSE == psmcb->CallbackSM(NULL, SMC_GETOBJECT,
  288. (WPARAM)&IID_IShellMenuCallback,
  289. (LPARAM)(LPVOID*)&psmcbClone))
  290. {
  291. psmcbClone = psmcb;
  292. psmcbClone->AddRef();
  293. }
  294. }
  295. dwFlags &= ~SMINIT_HORIZONTAL;
  296. CShellMenuCallbackWrapper* psmcw = new CShellMenuCallbackWrapper(hwndTB, psmcbClone);
  297. // We want the bands to think it is:
  298. // Top level - because it has no menuband parent
  299. // Vertical - because it's not a menubar
  300. dwFlags |= SMINIT_TOPLEVEL | SMINIT_VERTICAL;
  301. hr = _psm->Initialize(psmcw, uId, ANCESTORDEFAULT, dwFlags);
  302. if (SUCCEEDED(hr))
  303. {
  304. HWND hwndOwner;
  305. HMENU hmenuObscured;
  306. hr = _psmClient->GetMenu(&hmenuObscured, &hwndOwner, NULL);
  307. if (SUCCEEDED(hr))
  308. {
  309. hr = _psm->SetMenu(hmenuObscured, hwndOwner, dwSMSetFlags | SMSET_DONTOWN); // Menuband takes ownership;
  310. }
  311. }
  312. if (psmcb)
  313. psmcb->Release();
  314. if (psmcbClone)
  315. psmcbClone->Release();
  316. if (psmcw)
  317. psmcw->Release();
  318. }
  319. }
  320. else
  321. {
  322. IShellMenu2 *psm2;
  323. hr = _psm->QueryInterface(IID_PPV_ARG(IShellMenu2, &psm2));
  324. if (SUCCEEDED(hr))
  325. {
  326. hr = psm2->SetToolbar(hwndTB, dwSMSetFlags);
  327. psm2->Release();
  328. }
  329. }
  330. return hr;
  331. }
  332. HRESULT CTrackShellMenu::Popup(HWND hwnd, POINTL *ppt, RECTL *prcExclude, DWORD dwFlags)
  333. {
  334. IMenuBand* pmb;
  335. HRESULT hres = E_INVALIDARG;
  336. if (!_psm)
  337. return hres;
  338. hres = _psm->QueryInterface(IID_PPV_ARG(IMenuBand, &pmb));
  339. if (FAILED(hres))
  340. return hres;
  341. HWND hwndParent = GetTopLevelAncestor(hwnd);
  342. // Did the user set a menu into the Shell Menu?
  343. HWND hwndSubclassed = NULL;
  344. GetMenu(NULL, &hwndSubclassed, NULL);
  345. if (hwndSubclassed == NULL)
  346. {
  347. // No; We need to artificially set one so that the message filtering and stuff works
  348. SetMenu(NULL, hwndParent, 0);
  349. }
  350. SetForegroundWindow(hwndParent);
  351. IMenuPopup* pmp;
  352. hres = CoCreateInstance(CLSID_MenuDeskBar, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IMenuPopup, &pmp));
  353. if (SUCCEEDED(hres))
  354. {
  355. IBandSite* pbs;
  356. hres = CoCreateInstance(CLSID_MenuBandSite, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IBandSite, &pbs));
  357. if (SUCCEEDED(hres))
  358. {
  359. hres = pmp->SetClient(pbs);
  360. if (SUCCEEDED(hres))
  361. {
  362. IDeskBand* pdb;
  363. hres = _psm->QueryInterface(IID_PPV_ARG(IDeskBand, &pdb));
  364. if (SUCCEEDED(hres))
  365. {
  366. hres = pbs->AddBand(pdb);
  367. pdb->Release();
  368. }
  369. }
  370. pbs->Release();
  371. }
  372. // If we've got a site ourselves, have MenuDeskBar use that.
  373. if (_punkSite)
  374. IUnknown_SetSite(pmp, _punkSite);
  375. if (SUCCEEDED(hres))
  376. {
  377. CMBMsgFilter* pmf = GetMessageFilter();
  378. void* pvContext = GetMessageFilter()->GetContext();
  379. hres = HookMenuWindow(hwndParent, pmb);
  380. if (SUCCEEDED(hres))
  381. {
  382. // This collapses any modal menus before we proceed. When switching between
  383. // Chevron menus, we need to collapse the previous menu. Refer to the comment
  384. // at the function definition.
  385. pmf->ForceModalCollapse();
  386. pmp->Popup(ppt, (LPRECTL)prcExclude, dwFlags);
  387. pmf->SetModal(TRUE);
  388. MSG msg;
  389. while (GetMessage(&msg, NULL, 0, 0))
  390. {
  391. HRESULT hres = pmb->IsMenuMessage(&msg);
  392. if (hres == E_FAIL)
  393. {
  394. // menuband says it's time to pack up and go home.
  395. // re-post this message so that it gets handled after
  396. // we've cleaned up the menu (avoid re-entrancy issues &
  397. // let rebar restore state of chevron button to unpressed)
  398. PostMessage(msg.hwnd, msg.message, msg.wParam, msg.lParam);
  399. break;
  400. }
  401. else if (hres != S_OK)
  402. {
  403. // menuband didn't handle this one
  404. TranslateMessage(&msg);
  405. DispatchMessage(&msg);
  406. }
  407. }
  408. hres = S_OK;
  409. UnHookMenuWindow(hwndParent);
  410. // We cannot change the context when modal, so unset the modal flag so that we can undo the context block.
  411. pmf->SetModal(FALSE);
  412. pmf->SetContext(pvContext, TRUE);
  413. }
  414. pmb->Release();
  415. }
  416. if (_psmClient)
  417. {
  418. // This is to fix a bug where if there is a cached ISHellMenu in the submenu,
  419. // and you share the callback (For example, Broweser menu callback and the
  420. // favorites menu being shared between the browser bar and the chevron menu)
  421. // when on menu collapsed, we were destroying the sub menu by doing a set site.
  422. // since we no longer do the set site on the sub menu, we need a way to say "Reset
  423. // your parent". and this is the best way.
  424. IUnknown_Exec(_psmClient, &CGID_MenuBand, MBANDCID_REFRESH, 0, NULL, NULL);
  425. }
  426. // This call is required regardless of whether we had a _punkSite above;
  427. // MenuDeskBar does its cleanup on SetSite(NULL).
  428. IUnknown_SetSite(pmp, NULL);
  429. pmp->Release();
  430. }
  431. return hres;
  432. }
  433. // *** IObjectWithSite methods ***
  434. HRESULT CTrackShellMenu::SetSite(IUnknown* punkSite)
  435. {
  436. ASSERT(NULL == punkSite || IS_VALID_CODE_PTR(punkSite, IUnknown));
  437. ATOMICRELEASE(_punkSite);
  438. _punkSite = punkSite;
  439. if (punkSite)
  440. punkSite->AddRef();
  441. return S_OK;
  442. }
  443. BOOL IsISFBand(IUnknown* punk)
  444. {
  445. OLECMD rgCmds[] = {
  446. ISFBID_PRIVATEID, 0,
  447. };
  448. IUnknown_QueryStatus(punk, &CGID_ISFBand, SIZEOF(rgCmds), rgCmds, NULL);
  449. return BOOLIFY(rgCmds[0].cmdf);
  450. }
  451. HRESULT DoISFBandStuff(ITrackShellMenu* ptsm, IUnknown* punk)
  452. {
  453. HRESULT hr = E_INVALIDARG;
  454. if (punk && ptsm)
  455. {
  456. IShellFolderBand* psfb;
  457. hr = punk->QueryInterface(IID_PPV_ARG(IShellFolderBand, &psfb));
  458. if (SUCCEEDED(hr))
  459. {
  460. BANDINFOSFB bi;
  461. bi.dwMask = ISFB_MASK_IDLIST | ISFB_MASK_SHELLFOLDER;
  462. hr = psfb->GetBandInfoSFB(&bi);
  463. if (SUCCEEDED(hr))
  464. {
  465. CISFMenuCallback* pCallback = new CISFMenuCallback();
  466. if (pCallback)
  467. {
  468. hr = pCallback->Initialize(punk);
  469. if (SUCCEEDED(hr))
  470. {
  471. ptsm->Initialize(SAFECAST(pCallback, IShellMenuCallback*), 0,
  472. ANCESTORDEFAULT, SMINIT_VERTICAL | SMINIT_TOPLEVEL);
  473. hr = ptsm->SetShellFolder(bi.psf, bi.pidl, NULL, SMSET_COLLAPSEONEMPTY);
  474. }
  475. pCallback->Release();
  476. }
  477. else
  478. {
  479. hr = E_OUTOFMEMORY;
  480. }
  481. bi.psf->Release();
  482. ILFree(bi.pidl);
  483. }
  484. psfb->Release();
  485. }
  486. }
  487. return hr;
  488. }
  489. // An API for internal use.
  490. HRESULT ToolbarMenu_Popup(HWND hwnd, LPRECT prc, IUnknown* punk, HWND hwndTB, int idMenu, DWORD dwFlags)
  491. {
  492. HRESULT hres = E_OUTOFMEMORY;
  493. ITrackShellMenu* ptsm;
  494. if (SUCCEEDED(CoCreateInstance(CLSID_TrackShellMenu, NULL, CLSCTX_INPROC_SERVER,
  495. IID_PPV_ARG(ITrackShellMenu, &ptsm))))
  496. {
  497. hres = S_OK;
  498. if (IsISFBand(punk))
  499. {
  500. hres = DoISFBandStuff(ptsm, punk);
  501. }
  502. else if (hwndTB)
  503. {
  504. ptsm->Initialize(NULL, 0, ANCESTORDEFAULT, SMINIT_TOPLEVEL | SMINIT_VERTICAL | SMINIT_RESTRICT_DRAGDROP);
  505. hres = ptsm->SetObscured(hwndTB, punk, SMSET_TOP);
  506. }
  507. IUnknown* punkSite;
  508. if (SUCCEEDED(IUnknown_GetSite(punk, IID_PPV_ARG(IUnknown, &punkSite))))
  509. IUnknown_SetSite(ptsm, punkSite);
  510. HMENU hmenu = idMenu ? LoadMenuPopup(idMenu) : NULL;
  511. if (SUCCEEDED(hres) && hmenu)
  512. hres = ptsm->SetMenu(hmenu, hwnd, SMSET_BOTTOM);
  513. if (SUCCEEDED(hres))
  514. {
  515. DWORD dwPopupFlags = MPPF_BOTTOM;
  516. // select first/last menu item if specified
  517. if (dwFlags == DBPC_SELECTFIRST)
  518. {
  519. dwPopupFlags |= MPPF_INITIALSELECT;
  520. }
  521. else if (dwFlags == DBPC_SELECTLAST)
  522. {
  523. dwPopupFlags |= MPPF_FINALSELECT;
  524. }
  525. else if (dwFlags != 0)
  526. {
  527. VARIANT var;
  528. var.vt = VT_I4;
  529. var.lVal = dwFlags;
  530. IShellMenu2* psm2;
  531. if (SUCCEEDED(ptsm->QueryInterface(IID_PPV_ARG(IShellMenu2, &psm2))))
  532. {
  533. IUnknown_QueryServiceExec(psm2, SID_SMenuBandChild, &CGID_MenuBand, MBANDCID_SELECTITEM, 0, &var, NULL);
  534. psm2->Release();
  535. }
  536. }
  537. POINTL ptPop = {prc->left, prc->bottom};
  538. hres = ptsm->Popup(hwnd, &ptPop, (LPRECTL)prc, dwPopupFlags);
  539. }
  540. IUnknown_SetSite(ptsm, NULL);
  541. ptsm->Release();
  542. }
  543. return hres;
  544. }