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.

820 lines
24 KiB

  1. #include "shellprv.h"
  2. #include "cowsite.h"
  3. #include "contextmenu.h"
  4. // Context Menu Forwarding base class, desinged to delegate
  5. // to a real IContextMenu, and provide inheriting class
  6. // an easy way to override minor bits of functionality
  7. //
  8. CContextMenuForwarder::CContextMenuForwarder(IUnknown* punk) : _cRef(1)
  9. {
  10. _punk = punk;
  11. _punk->AddRef();
  12. _punk->QueryInterface(IID_PPV_ARG(IObjectWithSite, &_pows));
  13. _punk->QueryInterface(IID_PPV_ARG(IContextMenu, &_pcm));
  14. _punk->QueryInterface(IID_PPV_ARG(IContextMenu2, &_pcm2));
  15. _punk->QueryInterface(IID_PPV_ARG(IContextMenu3, &_pcm3));
  16. }
  17. CContextMenuForwarder::~CContextMenuForwarder()
  18. {
  19. if (_pows) _pows->Release();
  20. if (_pcm) _pcm->Release();
  21. if (_pcm2) _pcm2->Release();
  22. if (_pcm3) _pcm3->Release();
  23. _punk->Release();
  24. }
  25. STDMETHODIMP CContextMenuForwarder::QueryInterface(REFIID riid, void **ppv)
  26. {
  27. HRESULT hr = _punk->QueryInterface(riid, ppv);
  28. if (SUCCEEDED(hr))
  29. {
  30. IUnknown* punkTmp = (IUnknown*)(*ppv);
  31. static const QITAB qit[] = {
  32. QITABENT(CContextMenuForwarder, IObjectWithSite), // IID_IObjectWithSite
  33. QITABENT(CContextMenuForwarder, IContextMenu3), // IID_IContextMenu3
  34. QITABENTMULTI(CContextMenuForwarder, IContextMenu2, IContextMenu3), // IID_IContextMenu2
  35. QITABENTMULTI(CContextMenuForwarder, IContextMenu, IContextMenu3), // IID_IContextMenu
  36. { 0 },
  37. };
  38. HRESULT hrTmp = QISearch(this, qit, riid, ppv);
  39. if (SUCCEEDED(hrTmp))
  40. {
  41. punkTmp->Release();
  42. }
  43. else
  44. {
  45. RIPMSG(FALSE, "CContextMenuForwarder asked for an interface it doesn't support");
  46. *ppv = NULL;
  47. hr = E_NOINTERFACE;
  48. }
  49. }
  50. return hr;
  51. }
  52. STDMETHODIMP_(ULONG) CContextMenuForwarder::AddRef()
  53. {
  54. return InterlockedIncrement(&_cRef);
  55. }
  56. STDMETHODIMP_(ULONG) CContextMenuForwarder::Release()
  57. {
  58. if (InterlockedDecrement(&_cRef))
  59. return _cRef;
  60. delete this;
  61. return 0;
  62. }
  63. // A context menu implementation on an array of context menus
  64. //
  65. // use the Create_ContextMenuOnContextMenuArray construction function
  66. //
  67. #define MAX_CM_WRAP 5
  68. class CContextMenuOnContextMenuArray : public IContextMenu3, public CObjectWithSite
  69. {
  70. public:
  71. // IUnknown
  72. STDMETHODIMP QueryInterface(REFIID, void **);
  73. STDMETHODIMP_(ULONG) AddRef(void);
  74. STDMETHODIMP_(ULONG) Release(void);
  75. // IContextMenu
  76. STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags);
  77. STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpici);
  78. STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax);
  79. // IContextMenu2
  80. STDMETHODIMP HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
  81. // IContextMenu3
  82. STDMETHODIMP HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult);
  83. // IObjectWithSite
  84. STDMETHODIMP SetSite(IUnknown *punkSite); // override
  85. BOOL IsEmpty() { return 0 == _count; }
  86. protected:
  87. CContextMenuOnContextMenuArray(IContextMenu* rgpcm[], UINT cpcm);
  88. ~CContextMenuOnContextMenuArray();
  89. friend HRESULT Create_ContextMenuOnContextMenuArray(IContextMenu* rgpcm[], UINT cpcm, REFIID riid, void** ppv);
  90. private:
  91. LONG _cRef;
  92. UINT _count;
  93. UINT _idFirst; // The begining of the first range is _idFirst
  94. UINT _idOffsets[MAX_CM_WRAP]; // The END of each range (BEGINing of next range is +1)
  95. IContextMenu *_pcmItem[MAX_CM_WRAP]; // The contextmenu for the item
  96. IContextMenu2 *_pcm2Item[MAX_CM_WRAP]; // The contextmenu for the item
  97. IContextMenu3 *_pcm3Item[MAX_CM_WRAP]; // The contextmenu for the item
  98. };
  99. CContextMenuOnContextMenuArray::CContextMenuOnContextMenuArray(IContextMenu* rgpcm[], UINT cpcm) : _cRef(1)
  100. {
  101. ASSERT(cpcm <= MAX_CM_WRAP);
  102. ASSERT(0 == _count);
  103. for (UINT i = 0 ; i < cpcm ; i++)
  104. {
  105. if (rgpcm[i])
  106. {
  107. rgpcm[i]->QueryInterface(IID_PPV_ARG(IContextMenu, &_pcmItem[_count]));
  108. ASSERT(_pcmItem[_count]);
  109. rgpcm[i]->QueryInterface(IID_PPV_ARG(IContextMenu2, &_pcm2Item[_count]));
  110. rgpcm[i]->QueryInterface(IID_PPV_ARG(IContextMenu3, &_pcm3Item[_count]));
  111. _count++;
  112. }
  113. }
  114. }
  115. HRESULT Create_ContextMenuOnContextMenuArray(IContextMenu* rgpcm[], UINT cpcm, REFIID riid, void** ppv)
  116. {
  117. HRESULT hr;
  118. *ppv = NULL;
  119. if (cpcm < MAX_CM_WRAP)
  120. {
  121. CContextMenuOnContextMenuArray* p = new CContextMenuOnContextMenuArray(rgpcm, cpcm);
  122. if (p)
  123. {
  124. if (p->IsEmpty())
  125. {
  126. hr = E_OUTOFMEMORY; // caller didn't check the array it gave us?
  127. }
  128. else
  129. {
  130. hr = p->QueryInterface(riid, ppv);
  131. }
  132. p->Release();
  133. }
  134. else
  135. {
  136. hr = E_OUTOFMEMORY;
  137. }
  138. }
  139. else
  140. {
  141. RIPMSG(FALSE, "Create_ContextMenuOnContextMenuArray with too many items!");
  142. hr = E_INVALIDARG;
  143. }
  144. return hr;
  145. }
  146. CContextMenuOnContextMenuArray::~CContextMenuOnContextMenuArray()
  147. {
  148. for (UINT i = 0 ; i < _count ; i++)
  149. {
  150. _pcmItem[i]->Release();
  151. if (_pcm2Item[i])
  152. _pcm2Item[i]->Release();
  153. if (_pcm3Item[i])
  154. _pcm3Item[i]->Release();
  155. }
  156. }
  157. STDMETHODIMP CContextMenuOnContextMenuArray::QueryInterface(REFIID riid, void **ppv)
  158. {
  159. static const QITAB qit[] = {
  160. QITABENTMULTI(CContextMenuOnContextMenuArray, IContextMenu, IContextMenu3), // IID_IContextMenu
  161. QITABENTMULTI(CContextMenuOnContextMenuArray, IContextMenu2, IContextMenu3), // IID_IContextMenu2
  162. QITABENT(CContextMenuOnContextMenuArray, IContextMenu3), // IID_IContextMenu3
  163. QITABENT(CContextMenuOnContextMenuArray, IObjectWithSite), // IID_IObjectWithSite
  164. { 0 },
  165. };
  166. return QISearch(this, qit, riid, ppv);
  167. }
  168. STDMETHODIMP_(ULONG) CContextMenuOnContextMenuArray::AddRef()
  169. {
  170. return InterlockedIncrement(&_cRef);
  171. }
  172. STDMETHODIMP_(ULONG) CContextMenuOnContextMenuArray::Release()
  173. {
  174. if (InterlockedDecrement(&_cRef))
  175. return _cRef;
  176. delete this;
  177. return 0;
  178. }
  179. STDMETHODIMP CContextMenuOnContextMenuArray::SetSite(IUnknown *punkSite)
  180. {
  181. // let all the kids know
  182. for (UINT i = 0; i < _count; i++)
  183. {
  184. IUnknown_SetSite(_pcmItem[i], punkSite);
  185. }
  186. return CObjectWithSite::SetSite(punkSite);
  187. }
  188. STDMETHODIMP CContextMenuOnContextMenuArray::QueryContextMenu(HMENU hmenu, UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags)
  189. {
  190. _idFirst = idCmdFirst;
  191. // We need the placeholder for the below to work
  192. if (InsertMenu(hmenu, indexMenu, MF_BYPOSITION|MF_STRING, 0, L"{44075D61-2050-4DF4-BC5D-CBA88A84E75B}"))
  193. {
  194. BOOL fIndexMenuIsPlaceholder = TRUE;
  195. // For each of our context menus...
  196. for (UINT i = 0; i < _count && idCmdFirst < idCmdLast; i++)
  197. {
  198. HRESULT hr = _pcmItem[i]->QueryContextMenu(hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
  199. if (SUCCEEDED(hr))
  200. {
  201. fIndexMenuIsPlaceholder = FALSE;
  202. _idOffsets[i] = idCmdFirst - _idFirst + (UINT)ShortFromResult(hr);
  203. idCmdFirst = idCmdFirst + (UINT)ShortFromResult(hr) + 1;
  204. // Find the placeholder so we know where to insert the next menu
  205. int cMenuItems = GetMenuItemCount(hmenu);
  206. for (int iItem = 0; iItem < cMenuItems; iItem++)
  207. {
  208. WCHAR szName[60];
  209. if (GetMenuString(hmenu, (iItem + indexMenu) % cMenuItems, szName, ARRAYSIZE(szName), MF_BYPOSITION)
  210. && !lstrcmp(szName, L"{44075D61-2050-4DF4-BC5D-CBA88A84E75B}"))
  211. {
  212. indexMenu = (iItem + indexMenu) % cMenuItems;
  213. fIndexMenuIsPlaceholder = TRUE;
  214. break;
  215. }
  216. }
  217. RIPMSG(fIndexMenuIsPlaceholder, "CContextMenuOnContextMenuArray::QueryContextMenu - some context menu removed our placeholder string");
  218. }
  219. else
  220. {
  221. if (0 == i)
  222. _idOffsets[i] = 0;
  223. else
  224. _idOffsets[i] = _idOffsets[i-1];
  225. }
  226. }
  227. // Remove the placeholder
  228. if (fIndexMenuIsPlaceholder)
  229. {
  230. DeleteMenu(hmenu, indexMenu, MF_BYPOSITION);
  231. }
  232. }
  233. else
  234. {
  235. TraceMsg(TF_ERROR, "CContextMenuOnContextMenuArray::QueryContextMenu - could not add placeholder element");
  236. }
  237. return idCmdFirst - _idFirst;
  238. }
  239. STDMETHODIMP CContextMenuOnContextMenuArray::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
  240. {
  241. HRESULT hr;
  242. for (UINT i = 0; i < _count; i++)
  243. {
  244. if (IS_INTRESOURCE(lpici->lpVerb))
  245. {
  246. UINT idCmd = (UINT)LOWORD((DWORD_PTR)lpici->lpVerb);
  247. if (idCmd <= _idOffsets[i])
  248. {
  249. // adjust id to be in proper range for this pcm
  250. if (i > 0)
  251. {
  252. lpici->lpVerb = MAKEINTRESOURCEA(idCmd - _idOffsets[i-1] - 1);
  253. }
  254. hr = _pcmItem[i]->InvokeCommand(lpici);
  255. return hr;
  256. }
  257. }
  258. else
  259. {
  260. // I guess we try until it works
  261. hr = _pcmItem[i]->InvokeCommand(lpici);
  262. if (SUCCEEDED(hr))
  263. return hr;
  264. }
  265. }
  266. TraceMsg(TF_ERROR, "Someone's passing CContextMenuOnContextMenuArray::InvokeCommand an id we didn't insert...");
  267. return E_FAIL;
  268. }
  269. STDMETHODIMP CContextMenuOnContextMenuArray::GetCommandString(UINT_PTR idCmd, UINT wFlags, UINT * pmf, LPSTR pszName, UINT cchMax)
  270. {
  271. for (UINT i = 0; i < _count; i++)
  272. {
  273. if (idCmd <= _idOffsets[i])
  274. {
  275. // adjust id to be in proper range for this pcm
  276. if (i>0)
  277. {
  278. idCmd = idCmd - _idOffsets[i-1] - 1;
  279. }
  280. return _pcmItem[i]->GetCommandString(idCmd, wFlags, pmf, pszName, cchMax);
  281. }
  282. }
  283. TraceMsg(TF_ERROR, "Someone's passing CContextMenuOnContextMenuArray::GetCommandString an id we didn't insert...");
  284. return E_FAIL;
  285. }
  286. STDMETHODIMP CContextMenuOnContextMenuArray::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
  287. {
  288. return HandleMenuMsg2(uMsg, wParam, lParam, NULL);
  289. }
  290. STDMETHODIMP CContextMenuOnContextMenuArray::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
  291. {
  292. HRESULT hr = E_FAIL;
  293. UINT idCmd;
  294. // Find the menu command id -- it's packed differently depending on the message
  295. //
  296. switch (uMsg)
  297. {
  298. case WM_MEASUREITEM:
  299. idCmd = ((MEASUREITEMSTRUCT *)lParam)->itemID;
  300. break;
  301. case WM_DRAWITEM:
  302. idCmd = ((LPDRAWITEMSTRUCT)lParam)->itemID;
  303. break;
  304. case WM_INITMENUPOPUP:
  305. idCmd = GetMenuItemID((HMENU)wParam, 0);
  306. break;
  307. case WM_MENUSELECT:
  308. {
  309. idCmd = GET_WM_MENUSELECT_CMD(wParam, lParam);
  310. UINT wFlags = GET_WM_MENUSELECT_FLAGS(wParam, lParam);
  311. // if idCmd is an offset, convert it to a menu id
  312. if (wFlags & MF_POPUP)
  313. {
  314. MENUITEMINFO miiSubMenu;
  315. miiSubMenu.cbSize = sizeof(MENUITEMINFO);
  316. miiSubMenu.fMask = MIIM_ID;
  317. miiSubMenu.cch = 0; // just in case
  318. if (GetMenuItemInfo(GET_WM_MENUSELECT_HMENU(wParam, lParam), idCmd, TRUE, &miiSubMenu))
  319. {
  320. idCmd = miiSubMenu.wID;
  321. }
  322. else
  323. {
  324. return E_FAIL;
  325. }
  326. }
  327. }
  328. break;
  329. case WM_MENUCHAR:
  330. if (NULL != plResult)
  331. {
  332. for (UINT i = 0; i < _count; i++)
  333. {
  334. if (_pcm3Item[i])
  335. {
  336. hr = _pcm3Item[i]->HandleMenuMsg2(uMsg, wParam, lParam, plResult);
  337. if (S_OK == hr)
  338. return hr;
  339. }
  340. }
  341. }
  342. return E_FAIL;
  343. default:
  344. return E_FAIL;
  345. }
  346. // make sure it's in our range
  347. if (idCmd >= _idFirst)
  348. {
  349. idCmd -= _idFirst;
  350. for (UINT i = 0; i < _count; i++)
  351. {
  352. if (idCmd <= _idOffsets[i])
  353. {
  354. if (_pcm3Item[i])
  355. hr = _pcm3Item[i]->HandleMenuMsg2(uMsg, wParam, lParam, plResult);
  356. else if (_pcm2Item[i] && NULL == plResult)
  357. hr = _pcm2Item[i]->HandleMenuMsg(uMsg, wParam, lParam);
  358. break;
  359. }
  360. }
  361. }
  362. return hr;
  363. }
  364. // CContextMenuOnHMENU takes ownership of HMENU and creates
  365. // an IContextMenu implementation out of it, forwarding all
  366. // messages to hwndOwner.
  367. //
  368. class CContextMenuOnHMENU : IContextMenu3
  369. {
  370. public:
  371. // IUnknown
  372. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  373. STDMETHODIMP_(ULONG) AddRef(void) ;
  374. STDMETHODIMP_(ULONG) Release(void);
  375. // IContextMenu
  376. STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags);
  377. STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpici);
  378. STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax);
  379. // IContextMenu2
  380. STDMETHODIMP HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
  381. // IContextMenu3
  382. STDMETHODIMP HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult);
  383. protected:
  384. CContextMenuOnHMENU(HMENU hmenu, HWND hwndOwner);
  385. virtual ~CContextMenuOnHMENU();
  386. friend HRESULT Create_ContextMenuOnHMENU(HMENU hmenu, HWND hwndOwner, REFIID iid, void** ppv);
  387. private:
  388. LONG _cRef;
  389. HMENU _hmenu; // menu to wrap
  390. HWND _hwndOwner;// window to forward menu messages to
  391. UINT _idCmdFirst;
  392. UINT _rgid[200]; // mapping of context menu ids to the original hmenu command ids (arbitrary limit to the size of an hmenu we support)
  393. UINT _crgid;
  394. void _RebaseMenu(HMENU hmenu, UINT uFlags); // maps _hmenu's ids such that _rgid[newid-1]=oldid
  395. BOOL _IsValidID(UINT wID) { return (wID > 0 && wID <= _crgid); } // can we index _rgid[] with [wID-1]?
  396. };
  397. CContextMenuOnHMENU::CContextMenuOnHMENU(HMENU hmenu, HWND hwndOwner) : _cRef(1)
  398. {
  399. _hmenu = hmenu;
  400. _hwndOwner = hwndOwner;
  401. }
  402. // takes ownership of hmenu
  403. HRESULT Create_ContextMenuOnHMENU(HMENU hmenu, HWND hwndOwner, REFIID riid, void** ppv)
  404. {
  405. HRESULT hr;
  406. *ppv = NULL;
  407. if (hmenu)
  408. {
  409. CContextMenuOnHMENU* p = new CContextMenuOnHMENU(hmenu, hwndOwner);
  410. if (p)
  411. {
  412. hr = p->QueryInterface(riid, ppv);
  413. p->Release();
  414. }
  415. else
  416. {
  417. DestroyMenu(hmenu);
  418. hr = E_OUTOFMEMORY;
  419. }
  420. }
  421. else
  422. {
  423. hr = E_OUTOFMEMORY; // caller probably just didn't check for this error case
  424. }
  425. return hr;
  426. }
  427. CContextMenuOnHMENU::~CContextMenuOnHMENU()
  428. {
  429. DestroyMenu(_hmenu);
  430. }
  431. STDMETHODIMP CContextMenuOnHMENU::QueryInterface(REFIID riid, void **ppv)
  432. {
  433. static const QITAB qit[] = {
  434. QITABENT(CContextMenuOnHMENU, IContextMenu3), // IID_IContextMenu3
  435. QITABENTMULTI(CContextMenuOnHMENU, IContextMenu2, IContextMenu3), // IID_IContextMenu2
  436. QITABENTMULTI(CContextMenuOnHMENU, IContextMenu, IContextMenu3), // IID_IContextMenu
  437. { 0 },
  438. };
  439. return QISearch(this, qit, riid, ppv);
  440. }
  441. STDMETHODIMP_(ULONG) CContextMenuOnHMENU::AddRef()
  442. {
  443. return InterlockedIncrement(&_cRef);
  444. }
  445. STDMETHODIMP_(ULONG) CContextMenuOnHMENU::Release()
  446. {
  447. if (InterlockedDecrement(&_cRef))
  448. return _cRef;
  449. delete this;
  450. return 0;
  451. }
  452. // What is the lowest menu id used in hmenu?
  453. // (Note that "-1" is often used for separators,
  454. // but that is a very LARGE number...)
  455. //
  456. void CContextMenuOnHMENU::_RebaseMenu(HMENU hmenu, UINT uFlags)
  457. {
  458. for (int nItem = GetMenuItemCount(hmenu) - 1; nItem >= 0; nItem--)
  459. {
  460. MENUITEMINFO mii = {0};
  461. mii.cbSize = sizeof(MENUITEMINFO);
  462. mii.fMask = MIIM_ID | MIIM_SUBMENU;
  463. if (!GetMenuItemInfo(hmenu, nItem, TRUE, &mii))
  464. {
  465. continue;
  466. }
  467. if (!mii.hSubMenu || (uFlags & MM_SUBMENUSHAVEIDS))
  468. {
  469. if (_crgid < ARRAYSIZE(_rgid))
  470. {
  471. _rgid[_crgid] = mii.wID;
  472. mii.wID = ++_crgid;
  473. SetMenuItemInfo(hmenu, nItem, TRUE, &mii);
  474. }
  475. else
  476. {
  477. RIPMSG(FALSE, "CContextMenuOnHMENU::_RebaseMenu() - Someone is using an HMENU that's too big...");
  478. DeleteMenu(hmenu, nItem, MF_BYPOSITION);
  479. continue;
  480. }
  481. }
  482. if (mii.hSubMenu)
  483. {
  484. _RebaseMenu(mii.hSubMenu, uFlags);
  485. }
  486. }
  487. }
  488. HRESULT CContextMenuOnHMENU::QueryContextMenu(HMENU hmenu, UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags)
  489. {
  490. _idCmdFirst = idCmdFirst;
  491. _RebaseMenu(_hmenu, uFlags);
  492. UINT idMax = Shell_MergeMenus(hmenu, _hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
  493. return idMax - _idCmdFirst;
  494. }
  495. HRESULT CContextMenuOnHMENU::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
  496. {
  497. if (IS_INTRESOURCE(lpici->lpVerb))
  498. {
  499. UINT wID = LOWORD((UINT_PTR)lpici->lpVerb);
  500. RIPMSG(_IsValidID(wID), "CContextMenuOnHMENU::InvokeCommand() received invalid wID");
  501. if (_IsValidID(wID))
  502. {
  503. wID = _rgid[wID-1];
  504. SendMessage(_hwndOwner, WM_COMMAND, GET_WM_COMMAND_MPS(wID, 0, 0));
  505. return S_OK;
  506. }
  507. }
  508. return E_INVALIDARG;
  509. }
  510. HRESULT CContextMenuOnHMENU::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax)
  511. {
  512. if (cchMax)
  513. pszName[0] = 0;
  514. if (IS_INTRESOURCE(idCmd))
  515. {
  516. RIPMSG(_IsValidID(idCmd), "CContextMenuOnHMENU::InvokeCommand() received invalid idCmd");
  517. if (_IsValidID(idCmd))
  518. {
  519. UINT wID = _rgid[idCmd - 1];
  520. switch (uType)
  521. {
  522. case GCS_HELPTEXT:
  523. // The only time this seems to be called is in response to a WM_MENUSELECT,
  524. // so forward it back to _hwndOwner so it can be the real WM_MENUSELECT
  525. SendMessage(_hwndOwner, WM_MENUSELECT, GET_WM_MENUSELECT_MPS(wID, 0, _hmenu));
  526. return E_FAIL;
  527. }
  528. return E_NOTIMPL;
  529. }
  530. }
  531. return E_INVALIDARG;
  532. }
  533. HRESULT CContextMenuOnHMENU::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
  534. {
  535. return HandleMenuMsg2(uMsg,wParam,lParam,NULL);
  536. }
  537. HRESULT CContextMenuOnHMENU::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
  538. {
  539. HRESULT hr = E_FAIL;
  540. LRESULT lRes = 0;
  541. switch (uMsg)
  542. {
  543. case WM_INITMENUPOPUP:
  544. lRes = SendMessage(_hwndOwner, uMsg, (WPARAM)_hmenu, lParam);
  545. hr = S_OK;
  546. break;
  547. case WM_DRAWITEM:
  548. {
  549. LPDRAWITEMSTRUCT pdi = ((LPDRAWITEMSTRUCT)lParam);
  550. DRAWITEMSTRUCT di = *pdi;
  551. RIPMSG(_IsValidID(di.itemID - _idCmdFirst), "CContextMenuOnHMENU::HandleMenuMsg2(WM_DRAWITEM) received invalid itemID");
  552. if (_IsValidID(di.itemID - _idCmdFirst))
  553. {
  554. di.itemID = _rgid[di.itemID - _idCmdFirst - 1];
  555. lRes = SendMessage(_hwndOwner, uMsg, wParam, (LPARAM)&di);
  556. hr = S_OK;
  557. }
  558. else
  559. hr = E_INVALIDARG;
  560. break;
  561. }
  562. case WM_MEASUREITEM:
  563. {
  564. LPMEASUREITEMSTRUCT pmi =((LPMEASUREITEMSTRUCT)lParam);
  565. MEASUREITEMSTRUCT mi = *pmi;
  566. RIPMSG(_IsValidID(mi.itemID - _idCmdFirst), "CContextMenuOnHMENU::HandleMenuMsg2(WM_MEASUREITEM) received invalid itemID");
  567. if (_IsValidID(mi.itemID - _idCmdFirst))
  568. {
  569. mi.itemID = _rgid[mi.itemID - _idCmdFirst - 1];
  570. lRes = SendMessage(_hwndOwner, uMsg, wParam, (LPARAM)&mi);
  571. hr = S_OK;
  572. }
  573. else
  574. hr = E_INVALIDARG;
  575. break;
  576. }
  577. case WM_MENUSELECT:
  578. {
  579. UINT wID = GET_WM_MENUSELECT_CMD(wParam, lParam);
  580. UINT wFlags = GET_WM_MENUSELECT_FLAGS(wParam, lParam);
  581. if (!(wFlags & MF_POPUP))
  582. {
  583. RIPMSG(_IsValidID(wID - _idCmdFirst), "CContextMenuOnHMENU::HandleMenuMsg2(WM_MENUSELECT) received invalid wID");
  584. if (_IsValidID(wID - _idCmdFirst))
  585. {
  586. wID = _rgid[wID - _idCmdFirst - 1];
  587. }
  588. else
  589. {
  590. hr = E_INVALIDARG;
  591. break;
  592. }
  593. }
  594. lRes = SendMessage(_hwndOwner, uMsg, GET_WM_MENUSELECT_MPS(wID, wFlags, _hmenu));
  595. hr = S_OK;
  596. break;
  597. }
  598. case WM_MENUCHAR:
  599. // should probably be SendMessage(_hwndOwner, uMsg, wParam, (LPARAM)_hmenu)
  600. // but our WM_MENUCHAR forwarding doesn't find the correct owner...
  601. //
  602. lRes = DefWindowProc(_hwndOwner, uMsg, wParam, (LPARAM)_hmenu);
  603. hr = (0 == lRes) ? S_FALSE : S_OK;
  604. break;
  605. default:
  606. RIPMSG(FALSE, "CContextMenuOnHMENU::HandleMenuMsg2 was forwarded an unexpected window message");
  607. lRes = 0;
  608. hr = E_FAIL;
  609. break;
  610. }
  611. if (plResult)
  612. *plResult = lRes;
  613. return hr;
  614. }
  615. // Forward everything to the given context menu,
  616. // but remove menu items with the canonical verbs
  617. // given in the semicolon-separated list of canonical verbs
  618. //
  619. class CContextMenuWithoutVerbs : CContextMenuForwarder
  620. {
  621. public:
  622. STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags);
  623. protected:
  624. CContextMenuWithoutVerbs(IUnknown* punk, LPCWSTR pszVerbList);
  625. friend HRESULT Create_ContextMenuWithoutVerbs(IUnknown* punk, LPCWSTR pszVerbList, REFIID riid, void **ppv);
  626. private:
  627. LPCWSTR _pszVerbList;
  628. };
  629. CContextMenuWithoutVerbs::CContextMenuWithoutVerbs(IUnknown* punk, LPCWSTR pszVerbList) : CContextMenuForwarder(punk)
  630. {
  631. _pszVerbList = pszVerbList; // no reference - this should be a pointer to the code segment
  632. }
  633. HRESULT Create_ContextMenuWithoutVerbs(IUnknown* punk, LPCWSTR pszVerbList, REFIID riid, void **ppv)
  634. {
  635. HRESULT hr = E_OUTOFMEMORY;
  636. *ppv = NULL;
  637. if (pszVerbList)
  638. {
  639. CContextMenuWithoutVerbs* p = new CContextMenuWithoutVerbs(punk, pszVerbList);
  640. if (p)
  641. {
  642. hr = p->QueryInterface(riid, ppv);
  643. p->Release();
  644. }
  645. }
  646. return hr;
  647. }
  648. HRESULT CContextMenuWithoutVerbs::QueryContextMenu(HMENU hmenu, UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags)
  649. {
  650. HRESULT hr = CContextMenuForwarder::QueryContextMenu(hmenu,indexMenu,idCmdFirst,idCmdLast,uFlags);
  651. if (SUCCEEDED(hr))
  652. {
  653. LPCWSTR pszVerb = _pszVerbList;
  654. while (*pszVerb)
  655. {
  656. WCHAR szVerb[64];
  657. LPCWSTR pszNext = StrChrW(pszVerb, L';');
  658. if (pszNext)
  659. {
  660. UINT cch = (UINT)(pszNext - pszVerb) + 1;
  661. ASSERT(0 < cch && cch < ARRAYSIZE(szVerb)); // we should be large enough for all the canonical verbs we use
  662. StrCpyN(szVerb, pszVerb, min(cch, ARRAYSIZE(szVerb)));
  663. pszVerb = pszNext + 1;
  664. }
  665. else
  666. {
  667. UINT cch = lstrlen(pszVerb) + 1;
  668. ASSERT(0 < cch && cch < ARRAYSIZE(szVerb)); // we should be large enough for all the canonical verbs we use
  669. StrCpyN(szVerb, pszVerb, min(cch, ARRAYSIZE(szVerb)));
  670. pszVerb += cch - 1; // point at NULL
  671. }
  672. ContextMenu_DeleteCommandByName(_pcm, hmenu, idCmdFirst, szVerb);
  673. }
  674. }
  675. return hr;
  676. }