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.

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