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.

2743 lines
91 KiB

  1. #include "shellprv.h"
  2. #include "ids.h"
  3. #include "pidl.h"
  4. #include "fstreex.h"
  5. #include "views.h"
  6. #include "shlwapip.h"
  7. #include "ole2dup.h"
  8. #include "filetbl.h"
  9. #include "datautil.h"
  10. #include "undo.h"
  11. #include "defview.h"
  12. #include "cowsite.h"
  13. #include "defcm.h"
  14. #include "rpctimeout.h"
  15. #define DEF_FOLDERMENU_MAXHKEYS 16
  16. // used with static defcm elements (pointer from mii.dwItemData)
  17. // and find extensions
  18. typedef struct
  19. {
  20. WCHAR wszMenuText[MAX_PATH];
  21. WCHAR wszHelpText[MAX_PATH];
  22. int iIcon;
  23. } SEARCHEXTDATA;
  24. typedef struct
  25. {
  26. SEARCHEXTDATA* psed;
  27. UINT idCmd;
  28. } SEARCHINFO;
  29. // Defined in fsmenu.obj
  30. BOOL _MenuCharMatch(LPCTSTR psz, TCHAR ch, BOOL fIgnoreAmpersand);
  31. const ICIVERBTOIDMAP c_sDFMCmdInfo[] = {
  32. { L"delete", "delete", DFM_CMD_DELETE, DCMIDM_DELETE },
  33. { c_szCut, "cut", DFM_CMD_MOVE, DCMIDM_CUT },
  34. { c_szCopy, "copy", DFM_CMD_COPY, DCMIDM_COPY },
  35. { c_szPaste, "paste", DFM_CMD_PASTE, DCMIDM_PASTE },
  36. { c_szPaste, "paste", DFM_CMD_PASTE, 0 },
  37. { c_szLink, "link", DFM_CMD_LINK, DCMIDM_LINK },
  38. { c_szProperties, "properties", DFM_CMD_PROPERTIES, DCMIDM_PROPERTIES },
  39. { c_szPasteLink, "pastelink", DFM_CMD_PASTELINK, 0 },
  40. { c_szRename, "rename", DFM_CMD_RENAME, DCMIDM_RENAME },
  41. };
  42. CDefBackgroundMenuCB::CDefBackgroundMenuCB(LPCITEMIDLIST pidlFolder) : _cRef(1)
  43. {
  44. _pidlFolder = ILClone(pidlFolder); // failure handled in the code
  45. }
  46. CDefBackgroundMenuCB::~CDefBackgroundMenuCB()
  47. {
  48. ILFree(_pidlFolder);
  49. }
  50. STDMETHODIMP CDefBackgroundMenuCB::QueryInterface(REFIID riid, void **ppvObj)
  51. {
  52. static const QITAB qit[] = {
  53. QITABENT(CDefBackgroundMenuCB, IContextMenuCB),
  54. { 0 },
  55. };
  56. return QISearch(this, qit, riid, ppvObj);
  57. }
  58. STDMETHODIMP_(ULONG) CDefBackgroundMenuCB::AddRef()
  59. {
  60. return InterlockedIncrement(&_cRef);
  61. }
  62. STDMETHODIMP_(ULONG) CDefBackgroundMenuCB::Release()
  63. {
  64. if (InterlockedDecrement(&_cRef))
  65. return _cRef;
  66. delete this;
  67. return 0;
  68. }
  69. STDMETHODIMP CDefBackgroundMenuCB::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
  70. {
  71. HRESULT hr = S_OK;
  72. switch (uMsg)
  73. {
  74. case DFM_MERGECONTEXTMENU_BOTTOM:
  75. if (!(wParam & (CMF_VERBSONLY | CMF_DVFILE)))
  76. {
  77. DWORD dwAttr = SFGAO_HASPROPSHEET;
  78. if ((NULL == _pidlFolder) ||
  79. FAILED(SHGetAttributesOf(_pidlFolder, &dwAttr)) ||
  80. (SFGAO_HASPROPSHEET & dwAttr))
  81. {
  82. CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_PROPERTIES_BG, 0, (LPQCMINFO)lParam);
  83. }
  84. }
  85. break;
  86. case DFM_GETHELPTEXT:
  87. LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));;
  88. break;
  89. case DFM_GETHELPTEXTW:
  90. LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));;
  91. break;
  92. case DFM_VALIDATECMD:
  93. switch (wParam)
  94. {
  95. case DFM_CMD_NEWFOLDER:
  96. break;
  97. default:
  98. hr = S_FALSE;
  99. break;
  100. }
  101. break;
  102. case DFM_INVOKECOMMAND:
  103. switch (wParam)
  104. {
  105. case FSIDM_PROPERTIESBG:
  106. hr = SHPropertiesForUnk(hwndOwner, psf, (LPCTSTR)lParam);
  107. break;
  108. default:
  109. hr = S_FALSE; // view menu items, use the default
  110. break;
  111. }
  112. break;
  113. default:
  114. hr = E_NOTIMPL;
  115. break;
  116. }
  117. return hr;
  118. }
  119. class CDefFolderMenu : public CObjectWithSite,
  120. public IContextMenu3,
  121. public IServiceProvider,
  122. public ISearchProvider,
  123. public IShellExtInit
  124. {
  125. friend HRESULT CDefFolderMenu_CreateHKeyMenu(HWND hwnd, HKEY hkey, IContextMenu **ppcm);
  126. friend HRESULT CDefFolderMenu_Create2Ex(LPCITEMIDLIST pidlFolder, HWND hwnd,
  127. UINT cidl, LPCITEMIDLIST *apidl,
  128. IShellFolder *psf, IContextMenuCB *pcmcb,
  129. UINT nKeys, const HKEY *ahkeys,
  130. IContextMenu **ppcm);
  131. public:
  132. CDefFolderMenu(BOOL fUnderKey);
  133. HRESULT Init(DEFCONTEXTMENU *pdcm);
  134. // IUnknown
  135. STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  136. STDMETHOD_(ULONG,AddRef)();
  137. STDMETHOD_(ULONG,Release)();
  138. // IContextMenu
  139. STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst,
  140. UINT idCmdLast, UINT uFlags);
  141. STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpici);
  142. STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType,
  143. UINT *pwRes, LPSTR pszName, UINT cchMax);
  144. // IContextMenu2
  145. STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam);
  146. // IContextMenu3
  147. STDMETHOD(HandleMenuMsg2)(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plResult);
  148. // IServiceProvider
  149. STDMETHOD(QueryService)(REFGUID guidService, REFIID riid, void **ppvObj);
  150. // ISearchProvider
  151. STDMETHOD(GetSearchGUID)(GUID *pGuid);
  152. // IShellExtInit
  153. STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);
  154. private:
  155. ~CDefFolderMenu();
  156. DWORD _AttributesOfItems(DWORD dwAttrMask);
  157. UINT _AddStatic(HMENU hmenu, UINT idCmd, UINT idCmdLast, HKEY hkey);
  158. void _InvokeStatic(UINT iCmd);
  159. HRESULT _InitDropTarget();
  160. HRESULT _GetMenuVerb(HMENU hmenu, int idFirst, int idMax, int item, LPWSTR psz, DWORD cch);
  161. void _UnduplicateVerbs(HMENU hmenu, int idFirst, int idMax);
  162. void _SetMenuDefault(HMENU hmenu, UINT idCmdFirst, UINT idMax);
  163. HRESULT _ProcessEditPaste(BOOL fPasteLink);
  164. HRESULT _ProcessRename();
  165. void _DrawItem(DRAWITEMSTRUCT *pdi);
  166. LRESULT _MeasureItem(MEASUREITEMSTRUCT *pmi);
  167. private:
  168. LONG _cRef; // Reference count
  169. IDropTarget *_pdtgt; // Drop target of selected item
  170. IContextMenuCB *_pcmcb; // Callback object
  171. IDataObject *_pdtobj; // Data object
  172. IShellFolder *_psf; // Shell folder
  173. HWND _hwnd; // Owner window
  174. UINT _idCmdFirst; // base id
  175. UINT _idStdMax; // standard commands (cut/copy/delete/properties) ID MAX
  176. UINT _idFolderMax; // Folder command ID MAX
  177. UINT _idVerbMax; // Add-in command (verbs) ID MAX
  178. UINT _idDelayInvokeMax;// extensiosn loaded at invoke time
  179. UINT _idFld2Max; // 2nd range of Folder command ID MAX
  180. HDSA _hdsaStatics; // For static menu items.
  181. HDXA _hdxa; // Dynamic menu array
  182. HDSA _hdsaCustomInfo; // array of SEARCHINFO's
  183. LPITEMIDLIST _pidlFolder;
  184. LPITEMIDLIST *_apidl;
  185. UINT _cidl;
  186. IAssociationArray *_paa;
  187. CSafeServiceSite *_psss;
  188. BOOL _bUnderKeys; // Data is directly under key, not
  189. // shellex\ContextMenuHandlers
  190. UINT _nKeys; // Number of class keys
  191. HKEY _hkeyClsKeys[DEF_FOLDERMENU_MAXHKEYS]; // Class keys
  192. HMENU _hmenu;
  193. UINT _uFlags;
  194. BOOL _bInitMenuPopup; // true if we received WM_INITMENUPOPUP and _uFlags & CMF_FINDHACK
  195. int _iStaticInvoked; // index of the invoked static item
  196. IDMAPFORQCMINFO _idMap; // our named separator mapping table
  197. };
  198. #define GetFldFirst(this) (_idStdMax + _idCmdFirst)
  199. HRESULT HDXA_FindByCommand(HDXA hdxa, UINT idCmd, REFIID riid, void **ppv);
  200. STDMETHODIMP CDefFolderMenu::QueryInterface(REFIID riid, void **ppvObj)
  201. {
  202. static const QITAB qit[] = {
  203. QITABENTMULTI(CDefFolderMenu, IContextMenu, IContextMenu3),
  204. QITABENTMULTI(CDefFolderMenu, IContextMenu2, IContextMenu3),
  205. QITABENT(CDefFolderMenu, IContextMenu3),
  206. QITABENT(CDefFolderMenu, IObjectWithSite),
  207. QITABENT(CDefFolderMenu, IServiceProvider),
  208. QITABENT(CDefFolderMenu, ISearchProvider),
  209. QITABENT(CDefFolderMenu, IShellExtInit),
  210. { 0 },
  211. };
  212. return QISearch(this, qit, riid, ppvObj);
  213. }
  214. STDMETHODIMP_(ULONG) CDefFolderMenu::AddRef()
  215. {
  216. return InterlockedIncrement(&_cRef);
  217. }
  218. CDefFolderMenu::CDefFolderMenu(BOOL fUnderKey)
  219. {
  220. _cRef = 1;
  221. _iStaticInvoked = -1;
  222. _bUnderKeys = fUnderKey;
  223. _psss = new CSafeServiceSite;
  224. if (_psss)
  225. _psss->SetProviderWeakRef(SAFECAST(this, IServiceProvider *));
  226. IDLData_InitializeClipboardFormats();
  227. ASSERT(_pidlFolder == NULL);
  228. ASSERT(_punkSite == NULL);
  229. }
  230. HRESULT CDefFolderMenu::Init(DEFCONTEXTMENU *pdcm)
  231. {
  232. _hwnd = pdcm->hwnd;
  233. _psf = pdcm->psf;
  234. if (_psf)
  235. _psf->AddRef();
  236. _pcmcb = pdcm->pcmcb;
  237. if (_pcmcb)
  238. {
  239. IUnknown_SetSite(_pcmcb, _psss);
  240. _pcmcb->AddRef();
  241. _pcmcb->CallBack(_psf, _hwnd, NULL, DFM_ADDREF, 0, 0);
  242. }
  243. _paa = pdcm->paa;
  244. if (_paa)
  245. _paa->AddRef();
  246. HRESULT hr = CloneIDListArray(pdcm->cidl, pdcm->apidl, &_cidl, &_apidl);
  247. if (SUCCEEDED(hr) && pdcm->pidlFolder)
  248. {
  249. hr = SHILClone(pdcm->pidlFolder, &_pidlFolder);
  250. }
  251. if (SUCCEEDED(hr) && _cidl && _psf)
  252. {
  253. hr = _psf->GetUIObjectOf(_hwnd, _cidl, (LPCITEMIDLIST *)_apidl, IID_PPV_ARG_NULL(IDataObject, &_pdtobj));
  254. }
  255. if (SUCCEEDED(hr))
  256. {
  257. _hdxa = HDXA_Create();
  258. if (NULL == _hdxa)
  259. hr = E_OUTOFMEMORY;
  260. }
  261. if (SUCCEEDED(hr))
  262. {
  263. if (pdcm->aKeys)
  264. {
  265. ASSERT(pdcm->cKeys <= ARRAYSIZE(_hkeyClsKeys));
  266. for (UINT i = 0; i < pdcm->cKeys; ++i)
  267. {
  268. if (pdcm->aKeys[i])
  269. {
  270. // Make a copy of the key for menu's use
  271. _hkeyClsKeys[_nKeys] = SHRegDuplicateHKey(pdcm->aKeys[i]);
  272. if (_hkeyClsKeys[_nKeys])
  273. {
  274. _nKeys++;
  275. }
  276. else
  277. hr = E_OUTOFMEMORY;
  278. }
  279. }
  280. }
  281. else if (_paa)
  282. {
  283. // we can get it from the _paa
  284. _nKeys = SHGetAssocKeysEx(_paa, ASSOCELEM_MASK_ENUMCONTEXTMENU, _hkeyClsKeys, ARRAYSIZE(_hkeyClsKeys));
  285. }
  286. }
  287. return hr;
  288. }
  289. void ContextMenuInfo_SetSite(ContextMenuInfo *pcmi, IUnknown *pSite)
  290. {
  291. // APPCOMPAT: PGP50 can only be QIed for IContextMenu, IShellExtInit, and IUnknown.
  292. if (!(pcmi->dwCompat & OBJCOMPATF_CTXMENU_LIMITEDQI))
  293. IUnknown_SetSite((IUnknown*)pcmi->pcm, pSite);
  294. }
  295. CDefFolderMenu::~CDefFolderMenu()
  296. {
  297. if (_psss)
  298. {
  299. _psss->SetProviderWeakRef(NULL);
  300. _psss->Release();
  301. }
  302. if (_pcmcb)
  303. {
  304. IUnknown_SetSite(_pcmcb, NULL);
  305. _pcmcb->CallBack(_psf, _hwnd, NULL, DFM_RELEASE, _idStdMax, 0);
  306. _pcmcb->Release();
  307. }
  308. if (_hdxa)
  309. {
  310. for (int i = 0; i < DSA_GetItemCount(_hdxa); i++)
  311. {
  312. ContextMenuInfo_SetSite((ContextMenuInfo *)DSA_GetItemPtr(_hdxa, i), NULL);
  313. }
  314. HDXA_Destroy(_hdxa);
  315. }
  316. ATOMICRELEASE(_psf);
  317. ATOMICRELEASE(_pdtgt);
  318. ATOMICRELEASE(_pdtobj);
  319. ATOMICRELEASE(_paa);
  320. for (UINT i = 0; i < _nKeys; i++)
  321. {
  322. RegCloseKey(_hkeyClsKeys[i]);
  323. }
  324. FreeIDListArray(_apidl, _cidl);
  325. _cidl = 0;
  326. _apidl = NULL;
  327. // if _bInitMenuPopup = true then we changed the dwItemData of the non static items
  328. // so we have to free them. otherwise don't touch them
  329. if (_hdsaCustomInfo)
  330. {
  331. // remove the custom data structures hanging off mii.dwItemData of static menu items
  332. // or all items if _uFlags & CMF_FINDHACK
  333. int cItems = DSA_GetItemCount(_hdsaCustomInfo);
  334. for (int i = 0; i < cItems; i++)
  335. {
  336. SEARCHINFO* psinfo = (SEARCHINFO*)DSA_GetItemPtr(_hdsaCustomInfo, i);
  337. ASSERT(psinfo);
  338. SEARCHEXTDATA* psed = psinfo->psed;
  339. if (psed)
  340. LocalFree(psed);
  341. }
  342. DSA_Destroy(_hdsaCustomInfo);
  343. }
  344. if (_hdsaStatics)
  345. DSA_Destroy(_hdsaStatics);
  346. ILFree(_pidlFolder);
  347. }
  348. STDMETHODIMP_(ULONG) CDefFolderMenu::Release()
  349. {
  350. if (InterlockedDecrement(&_cRef))
  351. return _cRef;
  352. delete this;
  353. return 0;
  354. }
  355. int _SHMergePopupMenus(HMENU hmMain, HMENU hmMerge, int idCmdFirst, int idCmdLast)
  356. {
  357. int i, idMax = idCmdFirst;
  358. for (i = GetMenuItemCount(hmMerge) - 1; i >= 0; --i)
  359. {
  360. MENUITEMINFO mii;
  361. mii.cbSize = sizeof(mii);
  362. mii.fMask = MIIM_ID|MIIM_SUBMENU;
  363. mii.cch = 0; // just in case
  364. if (GetMenuItemInfo(hmMerge, i, TRUE, &mii))
  365. {
  366. int idTemp = Shell_MergeMenus(_GetMenuFromID(hmMain, mii.wID),
  367. mii.hSubMenu, (UINT)0, idCmdFirst, idCmdLast,
  368. MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
  369. if (idMax < idTemp)
  370. idMax = idTemp;
  371. }
  372. }
  373. return idMax;
  374. }
  375. void CDefFolderMenu_MergeMenu(HINSTANCE hinst, UINT idMainMerge, UINT idPopupMerge, QCMINFO *pqcm)
  376. {
  377. UINT idMax = pqcm->idCmdFirst;
  378. if (idMainMerge)
  379. {
  380. HMENU hmMerge = SHLoadPopupMenu(hinst, idMainMerge);
  381. if (hmMerge)
  382. {
  383. idMax = Shell_MergeMenus(
  384. pqcm->hmenu, hmMerge, pqcm->indexMenu,
  385. pqcm->idCmdFirst, pqcm->idCmdLast,
  386. MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS | MM_DONTREMOVESEPS);
  387. DestroyMenu(hmMerge);
  388. }
  389. }
  390. if (idPopupMerge)
  391. {
  392. HMENU hmMerge = LoadMenu(hinst, MAKEINTRESOURCE(idPopupMerge));
  393. if (hmMerge)
  394. {
  395. UINT idTemp = _SHMergePopupMenus(pqcm->hmenu, hmMerge,
  396. pqcm->idCmdFirst, pqcm->idCmdLast);
  397. if (idMax < idTemp)
  398. idMax = idTemp;
  399. DestroyMenu(hmMerge);
  400. }
  401. }
  402. pqcm->idCmdFirst = idMax;
  403. }
  404. BOOL _IsDesktop(IShellFolder *psf, UINT cidl, LPCITEMIDLIST *apidl)
  405. {
  406. CLSID clsid;
  407. return IsSelf(cidl, apidl) &&
  408. SUCCEEDED(IUnknown_GetClassID(psf, &clsid)) &&
  409. IsEqualGUID(clsid, CLSID_ShellDesktop);
  410. }
  411. DWORD CDefFolderMenu::_AttributesOfItems(DWORD dwAttrMask)
  412. {
  413. if (!_psf || !_cidl || FAILED(_psf->GetAttributesOf(_cidl, (LPCITEMIDLIST *)_apidl, &dwAttrMask)))
  414. dwAttrMask = 0;
  415. return dwAttrMask;
  416. }
  417. void _DisableRemoveMenuItem(HMENU hmInit, UINT uID, BOOL bAvail, BOOL bRemoveUnavail)
  418. {
  419. if (bAvail)
  420. {
  421. EnableMenuItem(hmInit, uID, MF_ENABLED|MF_BYCOMMAND);
  422. }
  423. else if (bRemoveUnavail)
  424. {
  425. DeleteMenu(hmInit, uID, MF_BYCOMMAND);
  426. }
  427. else
  428. {
  429. EnableMenuItem(hmInit, uID, MF_GRAYED|MF_BYCOMMAND);
  430. }
  431. }
  432. // Enable/disable menuitems in the "File" & popup context menu
  433. void Def_InitFileCommands(ULONG dwAttr, HMENU hmInit, UINT idCmdFirst, BOOL bContext)
  434. {
  435. idCmdFirst -= SFVIDM_FIRST;
  436. _DisableRemoveMenuItem(hmInit, SFVIDM_FILE_RENAME + idCmdFirst, dwAttr & SFGAO_CANRENAME, bContext);
  437. _DisableRemoveMenuItem(hmInit, SFVIDM_FILE_DELETE + idCmdFirst, dwAttr & SFGAO_CANDELETE, bContext);
  438. _DisableRemoveMenuItem(hmInit, SFVIDM_FILE_LINK + idCmdFirst, dwAttr & SFGAO_CANLINK, bContext);
  439. _DisableRemoveMenuItem(hmInit, SFVIDM_FILE_PROPERTIES + idCmdFirst, dwAttr & SFGAO_HASPROPSHEET, bContext);
  440. }
  441. STDAPI_(BOOL) IsClipboardOwnerHung(DWORD dwTimeout)
  442. {
  443. HWND hwnd = GetClipboardOwner();
  444. if (!hwnd)
  445. return FALSE;
  446. DWORD_PTR dwResult;
  447. return !SendMessageTimeout(hwnd, WM_NULL, 0, 0, SMTO_ABORTIFHUNG, dwTimeout, &dwResult);
  448. }
  449. STDAPI_(BOOL) Def_IsPasteAvailable(IDropTarget *pdtgt, DWORD *pdwEffect)
  450. {
  451. BOOL fRet = FALSE;
  452. *pdwEffect = 0; // assume none
  453. // Count the number of clipboard formats available, if there are none then there
  454. // is no point making the clipboard available.
  455. if (pdtgt && (CountClipboardFormats() > 0))
  456. {
  457. DECLAREWAITCURSOR;
  458. SetWaitCursor();
  459. // The clipboard owner might be hung, so time him out if he takes too long.
  460. // We don't want context menus to hang just because some app is hung.
  461. CRPCTimeout rpctimeout;
  462. IDataObject *pdtobj;
  463. if (!IsClipboardOwnerHung(1000) && SUCCEEDED(OleGetClipboard(&pdtobj)))
  464. {
  465. POINTL pt = {0, 0};
  466. DWORD dwEffectOffered = DataObj_GetDWORD(pdtobj, g_cfPreferredDropEffect, DROPEFFECT_COPY | DROPEFFECT_LINK);
  467. // Unfortunately, OLE turns RPC errors into generic errors
  468. // so we can't use the HRESULT from IDataObject::GetData
  469. // to tell whether the object is alive and doesn't support
  470. // PreferredDropEffect or is hung and OLE turned the
  471. // error code into DV_E_FORMATETC. So see if our timeout fired.
  472. // This is not foolproof because OLE sometimes caches the "is
  473. // the data object hung" state and returns error immediately
  474. // instead of timing out. But it's better than nothing.
  475. if (rpctimeout.TimedOut())
  476. {
  477. dwEffectOffered = 0;
  478. }
  479. // Check if we can paste.
  480. DWORD dwEffect = (dwEffectOffered & (DROPEFFECT_MOVE | DROPEFFECT_COPY));
  481. if (dwEffect)
  482. {
  483. if (SUCCEEDED(pdtgt->DragEnter(pdtobj, MK_RBUTTON, pt, &dwEffect)))
  484. {
  485. pdtgt->DragLeave();
  486. }
  487. else
  488. {
  489. dwEffect = 0;
  490. }
  491. }
  492. // Check if we can past-link.
  493. DWORD dwEffectLink = (dwEffectOffered & DROPEFFECT_LINK);
  494. if (dwEffectLink)
  495. {
  496. if (SUCCEEDED(pdtgt->DragEnter(pdtobj, MK_RBUTTON, pt, &dwEffectLink)))
  497. {
  498. pdtgt->DragLeave();
  499. dwEffect |= dwEffectLink;
  500. }
  501. }
  502. fRet = (dwEffect & (DROPEFFECT_MOVE | DROPEFFECT_COPY));
  503. *pdwEffect = dwEffect;
  504. pdtobj->Release();
  505. }
  506. ResetWaitCursor();
  507. }
  508. return fRet;
  509. }
  510. void Def_InitEditCommands(ULONG dwAttr, HMENU hmInit, UINT idCmdFirst, IDropTarget *pdtgt, UINT fContext)
  511. {
  512. DWORD dwEffect = 0;
  513. TCHAR szMenuText[80];
  514. idCmdFirst -= SFVIDM_FIRST;
  515. // Do the UNDO stuff only if the menu has an Undo option
  516. if (GetMenuState(hmInit, SFVIDM_EDIT_UNDO + idCmdFirst, MF_BYCOMMAND) != 0xFFFFFFFF)
  517. {
  518. // enable undo if there's an undo history
  519. BOOL bEnableUndo = IsUndoAvailable();
  520. if (bEnableUndo)
  521. {
  522. GetUndoText(szMenuText, ARRAYSIZE(szMenuText), UNDO_MENUTEXT);
  523. }
  524. else
  525. {
  526. szMenuText[0] = 0;
  527. LoadString(HINST_THISDLL, IDS_UNDOMENU, szMenuText, ARRAYSIZE(szMenuText));
  528. }
  529. if (szMenuText[0])
  530. {
  531. ModifyMenu(hmInit, SFVIDM_EDIT_UNDO + idCmdFirst, MF_BYCOMMAND | MF_STRING,
  532. SFVIDM_EDIT_UNDO + idCmdFirst, szMenuText);
  533. }
  534. _DisableRemoveMenuItem(hmInit, SFVIDM_EDIT_UNDO + idCmdFirst, bEnableUndo, fContext);
  535. }
  536. _DisableRemoveMenuItem(hmInit, SFVIDM_EDIT_CUT + idCmdFirst, dwAttr & SFGAO_CANMOVE, fContext);
  537. _DisableRemoveMenuItem(hmInit, SFVIDM_EDIT_COPY + idCmdFirst, dwAttr & SFGAO_CANCOPY, fContext);
  538. // Never remove the "Paste" command
  539. _DisableRemoveMenuItem(hmInit, SFVIDM_EDIT_PASTE + idCmdFirst, Def_IsPasteAvailable(pdtgt, &dwEffect), fContext & DIEC_SELECTIONCONTEXT);
  540. _DisableRemoveMenuItem(hmInit, SFVIDM_EDIT_PASTELINK + idCmdFirst, dwEffect & DROPEFFECT_LINK, fContext & DIEC_SELECTIONCONTEXT);
  541. _DisableRemoveMenuItem(hmInit, SFVIDM_EDIT_MOVETO + idCmdFirst, dwAttr & SFGAO_CANMOVE, fContext);
  542. _DisableRemoveMenuItem(hmInit, SFVIDM_EDIT_COPYTO + idCmdFirst, dwAttr & SFGAO_CANCOPY, fContext);
  543. }
  544. int Static_ExtractIcon(HKEY hkeyMenuItem)
  545. {
  546. HKEY hkeyDefIcon;
  547. int iImage = -1;
  548. if (RegOpenKey(hkeyMenuItem, c_szDefaultIcon, &hkeyDefIcon) == ERROR_SUCCESS)
  549. {
  550. TCHAR szDefIcon[MAX_PATH];
  551. DWORD cb = sizeof(szDefIcon);
  552. if (SHQueryValueEx(hkeyDefIcon, NULL, NULL, NULL, (BYTE*)szDefIcon, &cb) == ERROR_SUCCESS)
  553. {
  554. iImage = Shell_GetCachedImageIndex(szDefIcon, PathParseIconLocation(szDefIcon), 0);
  555. }
  556. RegCloseKey(hkeyDefIcon);
  557. }
  558. return iImage;
  559. }
  560. typedef struct
  561. {
  562. CLSID clsid;
  563. UINT idCmd;
  564. UINT idMenu; // used in cleanup
  565. GUID guidSearch; //used with search extensions only
  566. } STATICITEMINFO;
  567. #define LAST_ITEM (int)0x7FFFFFFF
  568. UINT CDefFolderMenu::_AddStatic(HMENU hmenu, UINT idCmd, UINT idCmdLast, HKEY hkey)
  569. {
  570. if (idCmd > idCmdLast)
  571. {
  572. DebugMsg(DM_ERROR, TEXT("si_a: Out of command ids!"));
  573. return idCmd;
  574. }
  575. ASSERT(!_hdsaStatics);
  576. ASSERT(!_hdsaCustomInfo);
  577. HDSA hdsaCustomInfo = DSA_Create(sizeof(SEARCHINFO), 1);
  578. // Create a hdsaStatics.
  579. HDSA hdsaStatics = DSA_Create(sizeof(STATICITEMINFO), 1);
  580. if (hdsaStatics && hdsaCustomInfo)
  581. {
  582. HKEY hkeyStatic;
  583. // Try to open the "Static" subkey.
  584. if (RegOpenKey(hkey, TEXT("Static"), &hkeyStatic) == ERROR_SUCCESS)
  585. {
  586. TCHAR szClass[MAX_PATH];
  587. BOOL bFindFilesInserted = FALSE;
  588. // For each subkey of static.
  589. for (int i = 0; RegEnumKey(hkeyStatic, i, szClass, ARRAYSIZE(szClass)) == ERROR_SUCCESS; i++)
  590. {
  591. HKEY hkeyClass;
  592. // Record the GUID.
  593. if (RegOpenKey(hkeyStatic, szClass, &hkeyClass) == ERROR_SUCCESS)
  594. {
  595. TCHAR szCLSID[MAX_PATH];
  596. DWORD cb = sizeof(szCLSID);
  597. // HACKHACK: (together with bWebSearchInserted above
  598. // we need to have On the Internet as the first menu item
  599. // and Find Files or Folders as second
  600. BOOL bWebSearch = lstrcmp(szClass, TEXT("WebSearch")) == 0;
  601. BOOL bFindFiles = FALSE;
  602. if (SHQueryValueEx(hkeyClass, NULL, NULL, NULL, (BYTE*)szCLSID, &cb) == ERROR_SUCCESS)
  603. {
  604. HKEY hkeyMenuItem;
  605. TCHAR szSubKey[32];
  606. // enum the sub keys 0..N
  607. for (int iMenuItem = 0; wsprintf(szSubKey, TEXT("%d"), iMenuItem),
  608. RegOpenKey(hkeyClass, szSubKey, &hkeyMenuItem) == ERROR_SUCCESS;
  609. iMenuItem++)
  610. {
  611. TCHAR szMenuText[MAX_PATH];
  612. if (SUCCEEDED(SHLoadLegacyRegUIString(hkeyMenuItem, NULL, szMenuText, ARRAYSIZE(szMenuText))))
  613. {
  614. STATICITEMINFO sii;
  615. SEARCHINFO sinfo;
  616. TCHAR szHelpText[MAX_PATH];
  617. SHLoadLegacyRegUIString(hkeyMenuItem, TEXT("HelpText"), szHelpText, ARRAYSIZE(szHelpText));
  618. SHCLSIDFromString(szCLSID, &sii.clsid); // store it
  619. sii.idCmd = iMenuItem;
  620. sii.idMenu = idCmd;
  621. // get the search guid if any...
  622. TCHAR szSearchGUID[MAX_PATH];
  623. cb = sizeof(szSearchGUID);
  624. if (SHGetValue(hkeyMenuItem, TEXT("SearchGUID"), NULL, NULL, (BYTE*)szSearchGUID, &cb) == ERROR_SUCCESS)
  625. SHCLSIDFromString(szSearchGUID, &sii.guidSearch);
  626. else
  627. sii.guidSearch = GUID_NULL;
  628. // cleanup -- allow non-static
  629. // find extensions to specify a search guid and then we can
  630. // remove this static "Find Computer" business...
  631. //
  632. // if this is FindComputer item and the restriction is not set
  633. // don't add it to the menu
  634. if (IsEqualGUID(sii.guidSearch, SRCID_SFindComputer) &&
  635. !SHRestricted(REST_HASFINDCOMPUTERS))
  636. continue;
  637. bFindFiles = IsEqualGUID(sii.guidSearch, SRCID_SFileSearch);
  638. if (bFindFiles && SHRestricted(REST_NOFIND))
  639. continue;
  640. if (IsEqualGUID(sii.guidSearch, SRCID_SFindPrinter))
  641. continue;
  642. DSA_AppendItem(hdsaStatics, &sii);
  643. SEARCHEXTDATA *psed = (SEARCHEXTDATA *)LocalAlloc(LPTR, sizeof(*psed));
  644. if (psed)
  645. {
  646. psed->iIcon = Static_ExtractIcon(hkeyMenuItem);
  647. SHTCharToUnicode(szHelpText, psed->wszHelpText, ARRAYSIZE(psed->wszHelpText));
  648. SHTCharToUnicode(szMenuText, psed->wszMenuText, ARRAYSIZE(psed->wszMenuText));
  649. }
  650. MENUITEMINFO mii;
  651. mii.cbSize = sizeof(mii);
  652. mii.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID;
  653. mii.fType = MFT_OWNERDRAW;
  654. mii.wID = idCmd;
  655. mii.dwItemData = (DWORD_PTR)psed;
  656. sinfo.psed = psed;
  657. sinfo.idCmd = idCmd;
  658. if (DSA_AppendItem(hdsaCustomInfo, &sinfo) != -1)
  659. {
  660. // insert Files or Folders in the first place (see HACKHACK above)
  661. if (!bFindFilesInserted && bFindFiles)
  662. bFindFilesInserted = InsertMenuItem(hmenu, 0, TRUE, &mii);
  663. else
  664. {
  665. UINT uiPos = LAST_ITEM;
  666. // if this is Find Files or Folders insert it after
  667. // On the Internet or in the first place if OtI is
  668. // not inserted yet
  669. if (bWebSearch)
  670. uiPos = bFindFilesInserted ? 1 : 0;
  671. // we don't free psed if Insert fails because it's
  672. // in dsa and it's going to be freed on destroy
  673. InsertMenuItem(hmenu, uiPos, TRUE, &mii);
  674. }
  675. }
  676. // Next command.
  677. idCmd++;
  678. if (idCmd > idCmdLast)
  679. {
  680. DebugMsg(DM_ERROR, TEXT("si_a: Out of command ids!"));
  681. break;
  682. }
  683. }
  684. RegCloseKey(hkeyMenuItem);
  685. }
  686. }
  687. RegCloseKey(hkeyClass);
  688. }
  689. }
  690. RegCloseKey(hkeyStatic);
  691. }
  692. _hdsaStatics = hdsaStatics;
  693. _hdsaCustomInfo = hdsaCustomInfo;
  694. }
  695. return idCmd;
  696. }
  697. void CDefFolderMenu::_InvokeStatic(UINT iCmd)
  698. {
  699. if (_hdsaStatics)
  700. {
  701. STATICITEMINFO *psii = (STATICITEMINFO *)DSA_GetItemPtr(_hdsaStatics, iCmd);
  702. if (psii)
  703. {
  704. IContextMenu *pcm;
  705. if (SUCCEEDED(SHExtCoCreateInstance(NULL, &psii->clsid, NULL, IID_PPV_ARG(IContextMenu, &pcm))))
  706. {
  707. HMENU hmenu = CreatePopupMenu();
  708. if (hmenu)
  709. {
  710. CMINVOKECOMMANDINFO ici;
  711. CHAR szSearchGUID[GUIDSTR_MAX];
  712. LPSTR psz = NULL;
  713. _iStaticInvoked = iCmd;
  714. IUnknown_SetSite(pcm, _psss);
  715. pcm->QueryContextMenu(hmenu, 0, CONTEXTMENU_IDCMD_FIRST, CONTEXTMENU_IDCMD_LAST, CMF_NORMAL);
  716. ici.cbSize = sizeof(ici);
  717. ici.fMask = 0;
  718. ici.hwnd = NULL;
  719. ici.lpVerb = (LPSTR)MAKEINTRESOURCE(psii->idCmd);
  720. if (!IsEqualGUID(psii->guidSearch, GUID_NULL))
  721. {
  722. SHStringFromGUIDA(psii->guidSearch, szSearchGUID, ARRAYSIZE(szSearchGUID));
  723. psz = szSearchGUID;
  724. }
  725. ici.lpParameters = psz;
  726. ici.lpDirectory = NULL;
  727. ici.nShow = SW_NORMAL;
  728. pcm->InvokeCommand(&ici);
  729. DestroyMenu(hmenu);
  730. IUnknown_SetSite(pcm, NULL);
  731. }
  732. pcm->Release();
  733. }
  734. }
  735. }
  736. }
  737. HRESULT CDefFolderMenu::_InitDropTarget()
  738. {
  739. HRESULT hr;
  740. if (_pdtgt)
  741. hr = S_OK; // have cached version
  742. else
  743. {
  744. // try to create _pdtgt
  745. if (_cidl)
  746. {
  747. ASSERT(NULL != _psf); // _pdtobj came from _psf
  748. hr = _psf->GetUIObjectOf(_hwnd, 1, (LPCITEMIDLIST *)_apidl, IID_PPV_ARG_NULL(IDropTarget, &_pdtgt));
  749. }
  750. else if (_psf)
  751. {
  752. hr = _psf->CreateViewObject(_hwnd, IID_PPV_ARG(IDropTarget, &_pdtgt));
  753. }
  754. else
  755. hr = E_FAIL;
  756. }
  757. return hr;
  758. }
  759. // Note on context menus ranges:
  760. // Standard Items // DFM_MERGECONTEXTMENU, context menu extensions, DFM_MERGECONTEXTMENU_TOP
  761. // Separator
  762. // View Items // context menu extensions can get here
  763. // Separator
  764. // (defcm S_FALSE "default" items, if applicable)
  765. // Separator
  766. // Folder Items // context menu extensions can get here
  767. // Separator
  768. // Bottom Items // DFM_MERGECONTEXTMENU_BOTTOM
  769. // Separator
  770. // ("File" menu, if applicable)
  771. //
  772. // Defcm uses names separators to do this magic. Unfortunately _SHPrettyMenu
  773. // removes duplicate separators and we don't always control when that happens.
  774. // So we build up the above empty menu first, and then insert into appropriate ranges.
  775. //
  776. // If you call SHPrepareMenuForDefcm, you must call SHPrettyMenuForDefcm before you return/TrackPopupMenu
  777. //
  778. #define DEFCM_RANGE 5 // the number of FSIDMs belor
  779. #define IS_VALID_DEFCM_RANGE(idCmdFirst, idCmdLast) (((idCmdLast)-(DEFCM_RANGE))>(idCmdFirst))
  780. #define FSIDM_FOLDER_SEP(idCmdLast) ((idCmdLast)-1)
  781. #define FSIDM_VIEW_SEP(idCmdLast) ((idCmdLast)-2)
  782. #define FSIDM_PLACE_SEP(idCmdLast) ((idCmdLast)-3)
  783. #define FSIDM_PLACE_VAL(idCmdLast) ((idCmdLast)-4)
  784. HRESULT SHPrepareMenuForDefcm(HMENU hmenu, UINT indexMenu, UINT uFlags, UINT idCmdFirst, UINT idCmdLast)
  785. {
  786. HRESULT hr = S_OK;
  787. if (!(uFlags & CMF_DEFAULTONLY) && IS_VALID_DEFCM_RANGE(idCmdFirst, idCmdLast))
  788. {
  789. UINT uPosView = GetMenuPosFromID(hmenu, FSIDM_VIEW_SEP(idCmdLast));
  790. UINT uPosFolder = GetMenuPosFromID(hmenu, FSIDM_FOLDER_SEP(idCmdLast));
  791. if (-1 != uPosView && -1 != uPosFolder)
  792. {
  793. // Menu is already set up correctly
  794. }
  795. else if (-1 == uPosView && -1 == uPosFolder)
  796. {
  797. // Insert everything backwords at position indexMenu
  798. //
  799. InsertMenu(hmenu, indexMenu, MF_BYPOSITION, FSIDM_PLACE_VAL(idCmdLast), TEXT("placeholder"));
  800. InsertMenu(hmenu, indexMenu, MF_BYPOSITION | MF_SEPARATOR, FSIDM_PLACE_SEP(idCmdLast), TEXT(""));
  801. InsertMenu(hmenu, indexMenu, MF_BYPOSITION, FSIDM_PLACE_VAL(idCmdLast), TEXT("placeholder"));
  802. InsertMenu(hmenu, indexMenu, MF_BYPOSITION | MF_SEPARATOR, FSIDM_FOLDER_SEP(idCmdLast), TEXT(""));
  803. InsertMenu(hmenu, indexMenu, MF_BYPOSITION, FSIDM_PLACE_VAL(idCmdLast), TEXT("placeholder"));
  804. InsertMenu(hmenu, indexMenu, MF_BYPOSITION | MF_SEPARATOR, FSIDM_PLACE_SEP(idCmdLast), TEXT(""));
  805. InsertMenu(hmenu, indexMenu, MF_BYPOSITION, FSIDM_PLACE_VAL(idCmdLast), TEXT("placeholder"));
  806. InsertMenu(hmenu, indexMenu, MF_BYPOSITION | MF_SEPARATOR, FSIDM_VIEW_SEP(idCmdLast), TEXT(""));
  807. InsertMenu(hmenu, indexMenu, MF_BYPOSITION, FSIDM_PLACE_VAL(idCmdLast), TEXT("placeholder"));
  808. hr = S_FALSE;
  809. }
  810. else
  811. {
  812. TraceMsg(TF_ERROR, "Some context menu removed a single named separator, we're in a screwy state");
  813. if (-1 == uPosFolder)
  814. InsertMenu(hmenu, indexMenu, MF_BYPOSITION | MF_SEPARATOR, FSIDM_FOLDER_SEP(idCmdLast), TEXT(""));
  815. if (-1 == uPosView)
  816. {
  817. InsertMenu(hmenu, indexMenu, MF_BYPOSITION | MF_SEPARATOR, FSIDM_PLACE_SEP(idCmdLast), TEXT(""));
  818. InsertMenu(hmenu, indexMenu, MF_BYPOSITION | MF_SEPARATOR, FSIDM_VIEW_SEP(idCmdLast), TEXT(""));
  819. }
  820. }
  821. }
  822. return hr;
  823. }
  824. HRESULT SHPrettyMenuForDefcm(HMENU hmenu, UINT uFlags, UINT idCmdFirst, UINT idCmdLast, HRESULT hrPrepare)
  825. {
  826. if (!(uFlags & CMF_DEFAULTONLY) && IS_VALID_DEFCM_RANGE(idCmdFirst, idCmdLast))
  827. {
  828. if (S_FALSE == hrPrepare)
  829. {
  830. while (DeleteMenu(hmenu, FSIDM_PLACE_VAL(idCmdLast), MF_BYCOMMAND))
  831. {
  832. // Remove all our non-separator menu items
  833. }
  834. }
  835. }
  836. _SHPrettyMenu(hmenu);
  837. return S_OK;
  838. }
  839. HRESULT SHUnprepareMenuForDefcm(HMENU hmenu, UINT idCmdFirst, UINT idCmdLast)
  840. {
  841. if (IS_VALID_DEFCM_RANGE(idCmdFirst, idCmdLast))
  842. {
  843. // Remove all the named separators we may have added
  844. DeleteMenu(hmenu, FSIDM_VIEW_SEP(idCmdLast), MF_BYCOMMAND);
  845. DeleteMenu(hmenu, FSIDM_FOLDER_SEP(idCmdLast), MF_BYCOMMAND);
  846. while (DeleteMenu(hmenu, FSIDM_PLACE_SEP(idCmdLast), MF_BYCOMMAND))
  847. {
  848. // Remove all our placeholder separators
  849. }
  850. }
  851. return S_OK;
  852. }
  853. void CDefFolderMenu::_SetMenuDefault(HMENU hmenu, UINT idCmdFirst, UINT idMax)
  854. {
  855. // we are about to set the default menu id, give the callback a chance
  856. // to override and set one of the static entries instead of the
  857. // first entry in the menu.
  858. WPARAM idStatic;
  859. if (_pcmcb && SUCCEEDED(_pcmcb->CallBack(_psf, _hwnd, _pdtobj,
  860. DFM_GETDEFSTATICID,
  861. 0, (LPARAM)&idStatic)))
  862. {
  863. for (int i = 0; i < ARRAYSIZE(c_sDFMCmdInfo); i++)
  864. {
  865. if (idStatic == c_sDFMCmdInfo[i].idDFMCmd)
  866. {
  867. SetMenuDefaultItem(hmenu, idCmdFirst + c_sDFMCmdInfo[i].idDefCmd, MF_BYCOMMAND);
  868. break;
  869. }
  870. }
  871. }
  872. if (GetMenuDefaultItem(hmenu, MF_BYPOSITION, 0) == -1)
  873. {
  874. int i = 0;
  875. int cMenu = GetMenuItemCount(hmenu);
  876. for (; i < cMenu; i++)
  877. {
  878. // fallback to openas so that files that have progids
  879. // dont endup using AFSO or * for their default verbs
  880. WCHAR szi[CCH_KEYMAX];
  881. HRESULT hr = _GetMenuVerb(hmenu, idCmdFirst, idMax, i, szi, ARRAYSIZE(szi));
  882. if (hr == S_OK && *szi && 0 == StrCmpI(szi, TEXT("openas")))
  883. {
  884. SetMenuDefaultItem(hmenu, i, MF_BYPOSITION);
  885. break;
  886. }
  887. }
  888. if (i == cMenu)
  889. {
  890. ASSERT(GetMenuDefaultItem(hmenu, MF_BYPOSITION, 0) == -1);
  891. SetMenuDefaultItem(hmenu, 0, MF_BYPOSITION);
  892. }
  893. }
  894. }
  895. STDMETHODIMP CDefFolderMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  896. {
  897. QCMINFO qcm = { hmenu, indexMenu, idCmdFirst, idCmdLast };
  898. DECLAREWAITCURSOR;
  899. BOOL fUseDefExt;
  900. SetWaitCursor();
  901. _idCmdFirst = idCmdFirst;
  902. _hmenu = hmenu;
  903. _uFlags = uFlags;
  904. _bInitMenuPopup = FALSE;
  905. // Set up the menu for defcm
  906. HRESULT hrPrepare = SHPrepareMenuForDefcm(hmenu, indexMenu, uFlags, idCmdFirst, idCmdLast);
  907. if (IS_VALID_DEFCM_RANGE(idCmdFirst, idCmdLast))
  908. {
  909. _idMap.max = 2;
  910. _idMap.list[0].id = FSIDM_FOLDER_SEP(idCmdLast);
  911. _idMap.list[0].fFlags = QCMINFO_PLACE_BEFORE;
  912. _idMap.list[1].id = FSIDM_VIEW_SEP(idCmdLast);
  913. _idMap.list[1].fFlags = QCMINFO_PLACE_AFTER;
  914. qcm.pIdMap = (const QCMINFO_IDMAP *)&_idMap;
  915. qcm.idCmdLast = idCmdLast - DEFCM_RANGE;
  916. }
  917. // first add in the folder commands like cut/copy/paste
  918. if (_pdtobj && !(uFlags & (CMF_VERBSONLY | CMF_DVFILE)))
  919. {
  920. if (!(CMF_DEFAULTONLY & uFlags))
  921. {
  922. ATOMICRELEASE(_pdtgt); // If we previously got the drop target, release it.
  923. _InitDropTarget(); // ignore failure, NULL _pdtgt is handled below
  924. }
  925. // We're going to merge two HMENUs into the context menu,
  926. // but we want only one id range for them... Remember the idCmdFirst.
  927. //
  928. UINT idCmdFirstTmp = qcm.idCmdFirst;
  929. UINT indexMenuTmp = qcm.indexMenu;
  930. UINT uPos = GetMenuPosFromID(hmenu, FSIDM_FOLDER_SEP(idCmdLast));
  931. // POPUP_DCM_ITEM2 goes after FSIDM_FOLDER_SEP(idCmdLast)
  932. if (-1 != uPos)
  933. qcm.indexMenu = uPos + 1;
  934. CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_DCM_ITEM2, 0, &qcm);
  935. UINT idCmdFirstMax = qcm.idCmdFirst;
  936. qcm.idCmdFirst = idCmdFirstTmp;
  937. // POPUP_DCM_ITEM goes TWO before FSIDM_FOLDER_SEP(idCmdLast)
  938. if (-1 != uPos)
  939. qcm.indexMenu = uPos - 1;
  940. CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_DCM_ITEM, 0, &qcm);
  941. qcm.indexMenu = indexMenuTmp;
  942. qcm.idCmdFirst = max(idCmdFirstTmp, qcm.idCmdFirst);
  943. ULONG dwAttr = _AttributesOfItems(
  944. SFGAO_CANRENAME | SFGAO_CANDELETE |
  945. SFGAO_CANLINK | SFGAO_HASPROPSHEET |
  946. SFGAO_CANCOPY | SFGAO_CANMOVE);
  947. if (!(uFlags & CMF_CANRENAME))
  948. dwAttr &= ~SFGAO_CANRENAME;
  949. Def_InitFileCommands(dwAttr, hmenu, idCmdFirst, TRUE);
  950. // Don't try to figure out paste if we're just going to invoke the default
  951. // (Figuring out paste is expensive)
  952. if (CMF_DEFAULTONLY & uFlags)
  953. {
  954. ASSERT(_pdtgt == NULL);
  955. }
  956. Def_InitEditCommands(dwAttr, hmenu, idCmdFirst, _pdtgt, DIEC_SELECTIONCONTEXT);
  957. }
  958. _idStdMax = qcm.idCmdFirst - idCmdFirst;
  959. // DFM_MERGECONTEXTMENU returns S_FALSE if we should not add any verbs.
  960. if (_pcmcb)
  961. {
  962. HRESULT hr = _pcmcb->CallBack(_psf, _hwnd, _pdtobj, DFM_MERGECONTEXTMENU, uFlags, (LPARAM)&qcm);
  963. fUseDefExt = (hr == S_OK);
  964. UINT indexMenuTmp = qcm.indexMenu;
  965. UINT uPos = GetMenuPosFromID(hmenu, FSIDM_FOLDER_SEP(idCmdLast));
  966. if (-1 != uPos)
  967. qcm.indexMenu = uPos + 1;
  968. hr = _pcmcb->CallBack(_psf, _hwnd, _pdtobj, DFM_MERGECONTEXTMENU_BOTTOM, uFlags, (LPARAM)&qcm);
  969. if (!fUseDefExt)
  970. fUseDefExt = (hr == S_OK);
  971. qcm.indexMenu = indexMenuTmp;
  972. }
  973. else
  974. {
  975. fUseDefExt = FALSE;
  976. }
  977. _idFolderMax = qcm.idCmdFirst - idCmdFirst;
  978. // add registry verbs
  979. if ((!(uFlags & CMF_NOVERBS)) ||
  980. (!_pdtobj && !_psf && _nKeys)) // this second case is for find extensions
  981. {
  982. // HACK: Default Extenstions EXPECT a selection, Let's hope all don't
  983. if (!_pdtobj)
  984. fUseDefExt = FALSE;
  985. // Put the separator between container menuitems and object menuitems
  986. // only if we don't have the separator at the insertion point.
  987. MENUITEMINFO mii = {0};
  988. mii.cbSize = sizeof(mii);
  989. mii.fMask = MIIM_TYPE;
  990. mii.fType = MFT_SEPARATOR; // to avoid ramdom result.
  991. if (GetMenuItemInfo(hmenu, indexMenu, TRUE, &mii) && !(mii.fType & MFT_SEPARATOR))
  992. {
  993. InsertMenu(hmenu, indexMenu, MF_BYPOSITION | MF_SEPARATOR, (UINT)-1, NULL);
  994. }
  995. HDCA hdca = DCA_Create();
  996. if (hdca)
  997. {
  998. // Add default extensions, only if the folder callback returned
  999. // S_OK. The Printer and Control folder returns S_FALSE
  1000. // indicating that they don't need any default extension.
  1001. if (fUseDefExt)
  1002. {
  1003. // Always add this default extention at the top.
  1004. DCA_AddItem(hdca, CLSID_ShellFileDefExt);
  1005. }
  1006. // Append menus for all extensions
  1007. for (UINT nKeys = 0; nKeys < _nKeys; ++nKeys)
  1008. {
  1009. DCA_AddItemsFromKey(hdca, _hkeyClsKeys[nKeys],
  1010. _bUnderKeys ? NULL : STRREG_SHEX_MENUHANDLER);
  1011. }
  1012. // Work Around:
  1013. // first time we call this _hdxa is empty
  1014. // after that it has the same items as before but will not add any new ones
  1015. // if user keeps right clicking we will eventually run out of menu item ids
  1016. // read comment in HDXA_AppendMenuItems2. to prevent it we empty _hdxa
  1017. HDXA_DeleteAll(_hdxa);
  1018. // (lamadio) For background context menu handlers, the pidlFolder
  1019. // should be a valid pidl, but, for backwards compatilility, this
  1020. // parameter should be NULL, if the Dataobject is NOT NULL.
  1021. qcm.idCmdFirst = HDXA_AppendMenuItems2(_hdxa, _pdtobj,
  1022. _nKeys, _hkeyClsKeys,
  1023. !_pdtobj ? _pidlFolder : NULL,
  1024. &qcm, uFlags, hdca, _psss);
  1025. DCA_Destroy(hdca);
  1026. }
  1027. _idVerbMax = qcm.idCmdFirst - idCmdFirst;
  1028. // menu extensions that are loaded at invoke time
  1029. if (uFlags & CMF_INCLUDESTATIC)
  1030. {
  1031. qcm.idCmdFirst = _AddStatic(hmenu, qcm.idCmdFirst, qcm.idCmdLast, _hkeyClsKeys[0]);
  1032. }
  1033. _idDelayInvokeMax = qcm.idCmdFirst - idCmdFirst;
  1034. // Remove the separator if we did not add any.
  1035. if (_idDelayInvokeMax == _idFolderMax)
  1036. {
  1037. if (GetMenuState(hmenu, 0, MF_BYPOSITION) & MF_SEPARATOR)
  1038. DeleteMenu(hmenu, 0, MF_BYPOSITION);
  1039. }
  1040. }
  1041. // if no default menu got set, choose the first one.
  1042. if (_pdtobj && !(uFlags & CMF_NODEFAULT) &&
  1043. GetMenuDefaultItem(hmenu, MF_BYPOSITION, 0) == -1)
  1044. {
  1045. _SetMenuDefault(hmenu, idCmdFirst, qcm.idCmdFirst);
  1046. }
  1047. // And now we give the callback the option to put (more) commands on top
  1048. // of everything else
  1049. if (_pcmcb)
  1050. _pcmcb->CallBack(_psf, _hwnd, _pdtobj, DFM_MERGECONTEXTMENU_TOP, uFlags, (LPARAM)&qcm);
  1051. _idFld2Max = qcm.idCmdFirst - idCmdFirst;
  1052. SHPrettyMenuForDefcm(hmenu, uFlags, idCmdFirst, idCmdLast, hrPrepare);
  1053. _UnduplicateVerbs(hmenu, idCmdFirst, qcm.idCmdFirst);
  1054. ResetWaitCursor();
  1055. return ResultFromShort(_idFld2Max);
  1056. }
  1057. HRESULT CDefFolderMenu::_GetMenuVerb(HMENU hmenu, int idFirst, int idMax, int item, LPWSTR psz, DWORD cch)
  1058. {
  1059. MENUITEMINFO mii = {0};
  1060. mii.cbSize = sizeof(MENUITEMINFO);
  1061. mii.fMask = MIIM_TYPE | MIIM_ID;
  1062. *psz = 0;
  1063. if (GetMenuItemInfo(hmenu, item, TRUE, &mii)
  1064. && ((int)mii.wID >= idFirst && (int)mii.wID < idMax))
  1065. {
  1066. if (mii.fType & MFT_SEPARATOR)
  1067. return S_FALSE;
  1068. else
  1069. return GetCommandString(mii.wID - idFirst, GCS_VERBW, NULL, (LPSTR)psz, cch);
  1070. }
  1071. return E_FAIL;
  1072. }
  1073. void CDefFolderMenu::_UnduplicateVerbs(HMENU hmenu, int idFirst, int idMax)
  1074. {
  1075. HRESULT hr = S_OK;
  1076. int iDefault = GetMenuDefaultItem(hmenu, MF_BYPOSITION, 0);
  1077. for (int i = 0; i < GetMenuItemCount(hmenu); i++)
  1078. {
  1079. WCHAR szi[CCH_KEYMAX];
  1080. hr = _GetMenuVerb(hmenu, idFirst, idMax, i, szi, ARRAYSIZE(szi));
  1081. if (hr == S_OK && *szi)
  1082. {
  1083. for (int j = i + 1; j < GetMenuItemCount(hmenu); j++)
  1084. {
  1085. WCHAR szj[CCH_KEYMAX];
  1086. hr = _GetMenuVerb(hmenu, idFirst, idMax, j, szj, ARRAYSIZE(szj));
  1087. if (hr == S_OK && *szj)
  1088. {
  1089. if (0 == StrCmpIW(szj, szi))
  1090. {
  1091. if (j != iDefault)
  1092. {
  1093. DeleteMenu(hmenu, j, MF_BYPOSITION);
  1094. j--;
  1095. }
  1096. }
  1097. }
  1098. }
  1099. }
  1100. }
  1101. }
  1102. HRESULT CDefFolderMenu::_ProcessEditPaste(BOOL fPasteLink)
  1103. {
  1104. DECLAREWAITCURSOR;
  1105. SetWaitCursor();
  1106. HRESULT hr = _InitDropTarget();
  1107. if (SUCCEEDED(hr))
  1108. {
  1109. IDataObject *pdtobj;
  1110. hr = OleGetClipboard(&pdtobj);
  1111. if (SUCCEEDED(hr))
  1112. {
  1113. DWORD grfKeyState;
  1114. DWORD dwEffect = DataObj_GetDWORD(pdtobj, g_cfPreferredDropEffect, DROPEFFECT_COPY | DROPEFFECT_LINK);
  1115. if (fPasteLink)
  1116. {
  1117. // MK_FAKEDROP to avoid drag/drop pop up menu
  1118. grfKeyState = MK_LBUTTON | MK_CONTROL | MK_SHIFT | MK_FAKEDROP;
  1119. dwEffect &= DROPEFFECT_LINK;
  1120. }
  1121. else
  1122. {
  1123. grfKeyState = MK_LBUTTON;
  1124. dwEffect &= ~DROPEFFECT_LINK;
  1125. }
  1126. hr = SimulateDropWithPasteSucceeded(_pdtgt, pdtobj, grfKeyState, NULL, dwEffect, _psss, TRUE);
  1127. pdtobj->Release();
  1128. }
  1129. }
  1130. ResetWaitCursor();
  1131. if (FAILED(hr))
  1132. MessageBeep(0);
  1133. return hr;
  1134. }
  1135. HRESULT CDefFolderMenu::_ProcessRename()
  1136. {
  1137. IDefViewFrame3 *dvf3;
  1138. HRESULT hr = IUnknown_QueryService(_punkSite, SID_DefView, IID_PPV_ARG(IDefViewFrame3, &dvf3));
  1139. if (SUCCEEDED(hr))
  1140. {
  1141. hr = dvf3->DoRename();
  1142. dvf3->Release();
  1143. }
  1144. return hr;
  1145. }
  1146. // deal with the versioning of this structure...
  1147. void CopyInvokeInfo(CMINVOKECOMMANDINFOEX *pici, const CMINVOKECOMMANDINFO *piciIn)
  1148. {
  1149. ASSERT(piciIn->cbSize >= sizeof(*piciIn));
  1150. ZeroMemory(pici, sizeof(*pici));
  1151. memcpy(pici, piciIn, min(sizeof(*pici), piciIn->cbSize));
  1152. pici->cbSize = sizeof(*pici);
  1153. }
  1154. #ifdef UNICODE
  1155. #define IS_UNICODE_ICI(pici) ((pici->cbSize >= CMICEXSIZE_NT4) && ((pici->fMask & CMIC_MASK_UNICODE) == CMIC_MASK_UNICODE))
  1156. #else
  1157. #define IS_UNICODE_ICI(pici) (FALSE)
  1158. #endif
  1159. typedef int (WINAPI * PFN_LSTRCMPIW)(LPCWSTR, LPCWSTR);
  1160. HRESULT SHMapICIVerbToCmdID(LPCMINVOKECOMMANDINFO pici, const ICIVERBTOIDMAP* pmap, UINT cmap, UINT* pid)
  1161. {
  1162. HRESULT hr = E_FAIL;
  1163. if (!IS_INTRESOURCE(pici->lpVerb))
  1164. {
  1165. PFN_LSTRCMPIW pfnCompare;
  1166. LPCWSTR pszVerb;
  1167. BOOL fUnicode;
  1168. if (IS_UNICODE_ICI(pici) && ((LPCMINVOKECOMMANDINFOEX)pici)->lpVerbW)
  1169. {
  1170. pszVerb = ((LPCMINVOKECOMMANDINFOEX)pici)->lpVerbW;
  1171. pfnCompare = lstrcmpiW;
  1172. fUnicode = TRUE;
  1173. }
  1174. else
  1175. {
  1176. pszVerb = (LPCWSTR)(pici->lpVerb);
  1177. pfnCompare = (PFN_LSTRCMPIW)lstrcmpiA;
  1178. fUnicode = FALSE;
  1179. }
  1180. for (UINT i = 0; i < cmap ; i++)
  1181. {
  1182. LPCWSTR pszCompare = (fUnicode) ? pmap[i].pszCmd : (LPCWSTR)(pmap[i].pszCmdA);
  1183. if (!pfnCompare(pszVerb, pszCompare))
  1184. {
  1185. *pid = pmap[i].idDFMCmd;
  1186. hr = S_OK;
  1187. break;
  1188. }
  1189. }
  1190. }
  1191. else
  1192. {
  1193. *pid = LOWORD((UINT_PTR)pici->lpVerb);
  1194. hr = S_OK;
  1195. }
  1196. return hr;
  1197. }
  1198. HRESULT SHMapCmdIDToVerb(UINT_PTR idCmd, const ICIVERBTOIDMAP* pmap, UINT cmap, LPSTR pszName, UINT cchMax, BOOL bUnicode)
  1199. {
  1200. LPCWSTR pszNull = L"";
  1201. LPCSTR pszVerb = (LPCSTR)pszNull;
  1202. for (UINT i = 0 ; i < cmap ; i++)
  1203. {
  1204. if (pmap[i].idDefCmd == idCmd)
  1205. {
  1206. pszVerb = (bUnicode) ? (LPCSTR)pmap[i].pszCmd : pmap[i].pszCmdA;
  1207. break;
  1208. }
  1209. }
  1210. if (bUnicode)
  1211. StrCpyNW((LPWSTR)pszName, (LPWSTR)pszVerb, cchMax);
  1212. else
  1213. StrCpyNA(pszName, pszVerb, cchMax);
  1214. return (pszVerb == (LPCSTR)pszNull) ? E_NOTIMPL : S_OK;
  1215. }
  1216. STDMETHODIMP CDefFolderMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
  1217. {
  1218. HRESULT hr = S_OK;
  1219. UINT idCmd = (UINT)-1;
  1220. UINT idCmdLocal; // this is used within each if block for the local idCmd value
  1221. LPCMINVOKECOMMANDINFOEX picix = (LPCMINVOKECOMMANDINFOEX)pici; // This value is only usable when fCmdInfoEx is true
  1222. BOOL fUnicode = IS_UNICODE_ICI(pici);
  1223. if (pici->cbSize < sizeof(CMINVOKECOMMANDINFO))
  1224. return E_INVALIDARG;
  1225. if (!IS_INTRESOURCE(pici->lpVerb))
  1226. {
  1227. if (SUCCEEDED(SHMapICIVerbToCmdID(pici, c_sDFMCmdInfo, ARRAYSIZE(c_sDFMCmdInfo), &idCmdLocal)))
  1228. {
  1229. // We need to use goto because idFolderMax might not be initialized
  1230. // yet (QueryContextMenu might have not been called).
  1231. goto ProcessCommand;
  1232. }
  1233. // see if this is a command provided by name by the callback
  1234. LPCTSTR pszVerb;
  1235. WCHAR szVerb[MAX_PATH];
  1236. if (!fUnicode || picix->lpVerbW == NULL)
  1237. {
  1238. SHAnsiToUnicode(picix->lpVerb, szVerb, ARRAYSIZE(szVerb));
  1239. pszVerb = szVerb;
  1240. }
  1241. else
  1242. pszVerb = picix->lpVerbW;
  1243. idCmdLocal = idCmd;
  1244. if (*pszVerb && SUCCEEDED(_pcmcb->CallBack(_psf, _hwnd, _pdtobj, DFM_MAPCOMMANDNAME, (WPARAM)&idCmdLocal, (LPARAM)pszVerb)))
  1245. {
  1246. goto ProcessCommand;
  1247. }
  1248. // we need to give the verbs a chance in case they asked for it by string
  1249. goto ProcessVerb;
  1250. }
  1251. else
  1252. {
  1253. idCmd = LOWORD((UINT_PTR)pici->lpVerb);
  1254. }
  1255. if (idCmd < _idStdMax)
  1256. {
  1257. idCmdLocal = idCmd;
  1258. for (int i = 0; i < ARRAYSIZE(c_sDFMCmdInfo); i++)
  1259. {
  1260. if (idCmdLocal == c_sDFMCmdInfo[i].idDefCmd)
  1261. {
  1262. idCmdLocal = c_sDFMCmdInfo[i].idDFMCmd;
  1263. goto ProcessCommand;
  1264. }
  1265. }
  1266. hr = E_INVALIDARG;
  1267. }
  1268. else if (idCmd < _idFolderMax)
  1269. {
  1270. DFMICS dfmics;
  1271. LPARAM lParam;
  1272. WCHAR szLParamBuffer[MAX_PATH];
  1273. idCmdLocal = idCmd - _idStdMax;
  1274. ProcessCommand:
  1275. if (!fUnicode || picix->lpParametersW == NULL)
  1276. {
  1277. if (pici->lpParameters == NULL)
  1278. {
  1279. lParam = (LPARAM)NULL;
  1280. }
  1281. else
  1282. {
  1283. SHAnsiToUnicode(pici->lpParameters, szLParamBuffer, ARRAYSIZE(szLParamBuffer));
  1284. lParam = (LPARAM)szLParamBuffer;
  1285. }
  1286. }
  1287. else
  1288. lParam = (LPARAM)picix->lpParametersW;
  1289. switch (idCmdLocal)
  1290. {
  1291. case DFM_CMD_LINK:
  1292. if (!fUnicode || picix->lpDirectoryW == NULL)
  1293. {
  1294. if (pici->lpDirectory == NULL)
  1295. {
  1296. lParam = (LPARAM)NULL;
  1297. }
  1298. else
  1299. {
  1300. SHAnsiToUnicode(pici->lpDirectory, szLParamBuffer, ARRAYSIZE(szLParamBuffer));
  1301. lParam = (LPARAM)szLParamBuffer;
  1302. }
  1303. }
  1304. else
  1305. lParam = (LPARAM)picix->lpDirectoryW;
  1306. break;
  1307. case DFM_CMD_PROPERTIES:
  1308. if (SHRestricted(REST_NOVIEWCONTEXTMENU))
  1309. {
  1310. // This is what the NT4 QFE returned, but I wonder
  1311. // if HRESULT_FROM_WIN32(E_ACCESSDENIED) would be better?
  1312. return hr;
  1313. }
  1314. break;
  1315. }
  1316. // try to use a DFM_INVOKECOMMANDEX first so the callback can see
  1317. // the INVOKECOMMANDINFO struct (for stuff like the 'no ui' flag)
  1318. dfmics.cbSize = sizeof(dfmics);
  1319. dfmics.fMask = pici->fMask;
  1320. dfmics.lParam = lParam;
  1321. dfmics.idCmdFirst = _idCmdFirst;
  1322. dfmics.idDefMax = _idStdMax;
  1323. dfmics.pici = pici;
  1324. // this for the property pages to show up right at
  1325. // the POINT where they were activated.
  1326. if ((idCmdLocal == DFM_CMD_PROPERTIES) && (pici->fMask & CMIC_MASK_PTINVOKE) && _pdtobj)
  1327. {
  1328. ASSERT(pici->cbSize >= sizeof(CMINVOKECOMMANDINFOEX));
  1329. POINT *ppt = (POINT *)GlobalAlloc(GPTR, sizeof(*ppt));
  1330. if (ppt)
  1331. {
  1332. *ppt = picix->ptInvoke;
  1333. if (FAILED(DataObj_SetGlobal(_pdtobj, g_cfOFFSETS, ppt)))
  1334. GlobalFree(ppt);
  1335. }
  1336. }
  1337. hr = _pcmcb->CallBack(_psf, _hwnd, _pdtobj, DFM_INVOKECOMMANDEX, idCmdLocal, (LPARAM)&dfmics);
  1338. if (hr == E_NOTIMPL)
  1339. {
  1340. // the callback didn't understand the DFM_INVOKECOMMANDEX
  1341. // fall back to a regular DFM_INVOKECOMMAND instead
  1342. hr = _pcmcb->CallBack(_psf, _hwnd, _pdtobj, DFM_INVOKECOMMAND, idCmdLocal, lParam);
  1343. }
  1344. // Check if we need to execute the default code.
  1345. if (hr == S_FALSE)
  1346. {
  1347. hr = S_OK; // assume no error
  1348. if (_pdtobj)
  1349. {
  1350. switch (idCmdLocal)
  1351. {
  1352. case DFM_CMD_MOVE:
  1353. case DFM_CMD_COPY:
  1354. DataObj_SetDWORD(_pdtobj, g_cfPreferredDropEffect,
  1355. (idCmdLocal == DFM_CMD_MOVE) ?
  1356. DROPEFFECT_MOVE : (DROPEFFECT_COPY | DROPEFFECT_LINK));
  1357. IShellFolderView *psfv;
  1358. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IShellFolderView, &psfv))))
  1359. psfv->SetPoints(_pdtobj);
  1360. OleSetClipboard(_pdtobj);
  1361. if (psfv)
  1362. {
  1363. // notify view so it can setup itself in the
  1364. // clipboard viewer chain
  1365. psfv->SetClipboard(DFM_CMD_MOVE == idCmdLocal);
  1366. psfv->Release();
  1367. }
  1368. break;
  1369. case DFM_CMD_LINK:
  1370. SHCreateLinks(pici->hwnd, NULL, _pdtobj, lParam ? SHCL_USETEMPLATE | SHCL_USEDESKTOP : SHCL_USETEMPLATE, NULL);
  1371. break;
  1372. case DFM_CMD_PASTE:
  1373. case DFM_CMD_PASTELINK:
  1374. hr = _ProcessEditPaste(idCmdLocal == DFM_CMD_PASTELINK);
  1375. break;
  1376. case DFM_CMD_RENAME:
  1377. hr = _ProcessRename();
  1378. break;
  1379. default:
  1380. DebugMsg(TF_WARNING, TEXT("DefCM item command not processed in %s at %d (%x)"),
  1381. __FILE__, __LINE__, idCmdLocal);
  1382. break;
  1383. }
  1384. }
  1385. else
  1386. {
  1387. // This is a background menu. Process common command ids.
  1388. switch(idCmdLocal)
  1389. {
  1390. case DFM_CMD_PASTE:
  1391. case DFM_CMD_PASTELINK:
  1392. hr = _ProcessEditPaste(idCmdLocal == DFM_CMD_PASTELINK);
  1393. break;
  1394. default:
  1395. // Only our commands should come here
  1396. DebugMsg(TF_WARNING, TEXT("DefCM background command not processed in %s at %d (%x)"),
  1397. __FILE__, __LINE__, idCmdLocal);
  1398. break;
  1399. }
  1400. }
  1401. }
  1402. }
  1403. else if (idCmd < _idVerbMax)
  1404. {
  1405. idCmdLocal = idCmd - _idFolderMax;
  1406. ProcessVerb:
  1407. {
  1408. CMINVOKECOMMANDINFOEX ici;
  1409. UINT_PTR idCmdSave;
  1410. CopyInvokeInfo(&ici, pici);
  1411. if (IS_INTRESOURCE(pici->lpVerb))
  1412. ici.lpVerb = (LPSTR)MAKEINTRESOURCE(idCmdLocal);
  1413. // One of extension menu is selected.
  1414. idCmdSave = (UINT_PTR)ici.lpVerb;
  1415. UINT_PTR idCmd = 0;
  1416. hr = HDXA_LetHandlerProcessCommandEx(_hdxa, &ici, &idCmd);
  1417. if (SUCCEEDED(hr) && (idCmd == idCmdSave))
  1418. {
  1419. // hdxa failed to handle it
  1420. hr = E_INVALIDARG;
  1421. }
  1422. }
  1423. }
  1424. else if (idCmd < _idDelayInvokeMax)
  1425. {
  1426. _InvokeStatic((UINT)(idCmd-_idVerbMax));
  1427. }
  1428. else if (idCmd < _idFld2Max)
  1429. {
  1430. idCmdLocal = idCmd - _idDelayInvokeMax;
  1431. goto ProcessCommand;
  1432. }
  1433. else
  1434. {
  1435. hr = E_INVALIDARG;
  1436. }
  1437. return hr;
  1438. }
  1439. STDMETHODIMP CDefFolderMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax)
  1440. {
  1441. HRESULT hr = E_INVALIDARG;
  1442. UINT_PTR idCmdLocal;
  1443. int i;
  1444. if (!IS_INTRESOURCE(idCmd))
  1445. {
  1446. // This must be a string
  1447. if (HDXA_GetCommandString(_hdxa, idCmd, uType, pwReserved, pszName, cchMax) == S_OK)
  1448. {
  1449. return S_OK;
  1450. }
  1451. // String can either be Ansi or Unicode. Since shell32 is built unicode, we need to compare
  1452. // idCmd against the ansi version of the verb string.
  1453. LPTSTR pCmd;
  1454. LPSTR pCmdA;
  1455. pCmd = (LPTSTR)idCmd;
  1456. pCmdA = (LPSTR)idCmd;
  1457. // Convert the string into an ID
  1458. for (i = 0; i < ARRAYSIZE(c_sDFMCmdInfo); i++)
  1459. {
  1460. if (!lstrcmpi(pCmd, c_sDFMCmdInfo[i].pszCmd) || !StrCmpIA(pCmdA, c_sDFMCmdInfo[i].pszCmdA))
  1461. {
  1462. idCmdLocal = (UINT) c_sDFMCmdInfo[i].idDFMCmd;
  1463. goto ProcessCommand;
  1464. }
  1465. }
  1466. return E_INVALIDARG;
  1467. }
  1468. if (idCmd < _idStdMax)
  1469. {
  1470. idCmdLocal = idCmd;
  1471. switch (uType)
  1472. {
  1473. case GCS_HELPTEXTA:
  1474. // HACK: DCM commands are in the same order as SFV commands
  1475. return(LoadStringA(HINST_THISDLL,
  1476. (UINT) idCmdLocal + (UINT)(SFVIDM_FIRST + SFVIDS_MH_FIRST),
  1477. (LPSTR)pszName, cchMax) ? S_OK : E_OUTOFMEMORY);
  1478. break;
  1479. case GCS_HELPTEXTW:
  1480. // HACK: DCM commands are in the same order as SFV commands
  1481. return(LoadStringW(HINST_THISDLL,
  1482. (UINT) idCmdLocal + (UINT)(SFVIDM_FIRST + SFVIDS_MH_FIRST),
  1483. (LPWSTR)pszName, cchMax) ? S_OK : E_OUTOFMEMORY);
  1484. break;
  1485. case GCS_VERBA:
  1486. case GCS_VERBW:
  1487. return SHMapCmdIDToVerb(idCmdLocal, c_sDFMCmdInfo, ARRAYSIZE(c_sDFMCmdInfo), pszName, cchMax, uType == GCS_VERBW);
  1488. case GCS_VALIDATEA:
  1489. case GCS_VALIDATEW:
  1490. default:
  1491. return E_NOTIMPL;
  1492. }
  1493. }
  1494. else if (idCmd < _idFolderMax)
  1495. {
  1496. idCmdLocal = idCmd - _idStdMax;
  1497. ProcessCommand:
  1498. if (!_pcmcb)
  1499. return E_NOTIMPL; // REVIEW: If no callback, how can idFolderMax be > 0?
  1500. // This is a folder menu
  1501. switch (uType)
  1502. {
  1503. case GCS_HELPTEXTA:
  1504. return _pcmcb->CallBack(_psf, _hwnd, _pdtobj, DFM_GETHELPTEXT,
  1505. (WPARAM)MAKELONG(idCmdLocal, cchMax), (LPARAM)pszName);
  1506. case GCS_HELPTEXTW:
  1507. return _pcmcb->CallBack(_psf, _hwnd, _pdtobj, DFM_GETHELPTEXTW,
  1508. (WPARAM)MAKELONG(idCmdLocal, cchMax), (LPARAM)pszName);
  1509. case GCS_VALIDATEA:
  1510. case GCS_VALIDATEW:
  1511. return _pcmcb->CallBack(_psf, _hwnd, _pdtobj,
  1512. DFM_VALIDATECMD, idCmdLocal, 0);
  1513. case GCS_VERBA:
  1514. return _pcmcb->CallBack(_psf, _hwnd, _pdtobj,
  1515. DFM_GETVERBA, (WPARAM)MAKELONG(idCmdLocal, cchMax), (LPARAM)pszName);
  1516. case GCS_VERBW:
  1517. return _pcmcb->CallBack(_psf, _hwnd, _pdtobj,
  1518. DFM_GETVERBW, (WPARAM)MAKELONG(idCmdLocal, cchMax), (LPARAM)pszName);
  1519. default:
  1520. return E_NOTIMPL;
  1521. }
  1522. }
  1523. else if (idCmd < _idVerbMax)
  1524. {
  1525. idCmdLocal = idCmd - _idFolderMax;
  1526. // One of extension menu is selected.
  1527. hr = HDXA_GetCommandString(_hdxa, idCmdLocal, uType, pwReserved, pszName, cchMax);
  1528. }
  1529. else if (idCmd < _idDelayInvokeMax)
  1530. {
  1531. // menu extensions that are loaded at invoke time don't support this
  1532. }
  1533. else if (idCmd < _idFld2Max)
  1534. {
  1535. idCmdLocal = idCmd - _idDelayInvokeMax;
  1536. goto ProcessCommand;
  1537. }
  1538. return hr;
  1539. }
  1540. STDMETHODIMP CDefFolderMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam,
  1541. LPARAM lParam,LRESULT* plResult)
  1542. {
  1543. UINT uMsgFld;
  1544. WPARAM wParamFld; // map the folder call back params to these
  1545. LPARAM lParamFld;
  1546. UINT idCmd;
  1547. UINT id; //temp var
  1548. switch (uMsg) {
  1549. case WM_MEASUREITEM:
  1550. idCmd = GET_WM_COMMAND_ID(((MEASUREITEMSTRUCT *)lParam)->itemID, 0);
  1551. // cannot use InRange because _idVerbMax can be equal to _idDelayInvokeMax
  1552. id = idCmd-_idCmdFirst;
  1553. if ((_bInitMenuPopup || (_hdsaStatics && _idVerbMax <= id)) && id < _idDelayInvokeMax)
  1554. {
  1555. _MeasureItem((MEASUREITEMSTRUCT *)lParam);
  1556. return S_OK;
  1557. }
  1558. uMsgFld = DFM_WM_MEASUREITEM;
  1559. wParamFld = GetFldFirst(this);
  1560. lParamFld = lParam;
  1561. break;
  1562. case WM_DRAWITEM:
  1563. idCmd = GET_WM_COMMAND_ID(((LPDRAWITEMSTRUCT)lParam)->itemID, 0);
  1564. // cannot use InRange because _idVerbMax can be equal to _idDelayInvokeMax
  1565. id = idCmd-_idCmdFirst;
  1566. if ((_bInitMenuPopup || (_hdsaStatics && _idVerbMax <= id)) && id < _idDelayInvokeMax)
  1567. {
  1568. _DrawItem((LPDRAWITEMSTRUCT)lParam);
  1569. return S_OK;
  1570. }
  1571. uMsgFld = DFM_WM_DRAWITEM;
  1572. wParamFld = GetFldFirst(this);
  1573. lParamFld = lParam;
  1574. break;
  1575. case WM_INITMENUPOPUP:
  1576. idCmd = GetMenuItemID((HMENU)wParam, 0);
  1577. if (_uFlags & CMF_FINDHACK)
  1578. {
  1579. HMENU hmenu = (HMENU)wParam;
  1580. int cItems = GetMenuItemCount(hmenu);
  1581. _bInitMenuPopup = TRUE;
  1582. if (!_hdsaCustomInfo)
  1583. _hdsaCustomInfo = DSA_Create(sizeof(SEARCHINFO), 1);
  1584. if (_hdsaCustomInfo && cItems > 0)
  1585. {
  1586. // need to go bottom up because we may delete some items
  1587. for (int i = cItems - 1; i >= 0; i--)
  1588. {
  1589. MENUITEMINFO mii = {0};
  1590. TCHAR szMenuText[MAX_PATH];
  1591. mii.cbSize = sizeof(mii);
  1592. mii.fMask = MIIM_TYPE | MIIM_DATA | MIIM_ID;
  1593. mii.dwTypeData = szMenuText;
  1594. mii.cch = ARRAYSIZE(szMenuText);
  1595. if (GetMenuItemInfo(hmenu, i, TRUE, &mii) && (MFT_STRING == mii.fType))
  1596. {
  1597. SEARCHINFO sinfo;
  1598. // static items already have correct dwItemData (pointer to SEARCHEXTDATA added in _AddStatic)
  1599. // we now have to change other find extension's dwItemData from having an index into the icon
  1600. // cache to pointer to SEARCHEXTDATA
  1601. // cannot use InRange because _idVerbMax can be equal to _idDelayInvokeMax
  1602. id = mii.wID - _idCmdFirst;
  1603. if (!(_hdsaStatics && _idVerbMax <= id && id < _idDelayInvokeMax))
  1604. {
  1605. UINT iIcon = (UINT) mii.dwItemData;
  1606. SEARCHEXTDATA *psed = (SEARCHEXTDATA *)LocalAlloc(LPTR, sizeof(*psed));
  1607. if (psed)
  1608. {
  1609. psed->iIcon = iIcon;
  1610. SHTCharToUnicode(szMenuText, psed->wszMenuText, ARRAYSIZE(psed->wszMenuText));
  1611. }
  1612. mii.fMask = MIIM_DATA | MIIM_TYPE;
  1613. mii.fType = MFT_OWNERDRAW;
  1614. mii.dwItemData = (DWORD_PTR)psed;
  1615. sinfo.psed = psed;
  1616. sinfo.idCmd = mii.wID;
  1617. if (DSA_AppendItem(_hdsaCustomInfo, &sinfo) == -1)
  1618. {
  1619. DeleteMenu(hmenu, i, MF_BYPOSITION);
  1620. if (psed)
  1621. LocalFree(psed);
  1622. }
  1623. else
  1624. SetMenuItemInfo(hmenu, i, TRUE, &mii);
  1625. }
  1626. }
  1627. }
  1628. }
  1629. else if (!_hdsaCustomInfo)
  1630. {
  1631. // we could not allocate space for _hdsaCustomInfo
  1632. // delete all items because there will be no pointer hanging off dwItemData
  1633. // so start | search will fault
  1634. for (int i = 0; i < cItems; i++)
  1635. DeleteMenu(hmenu, i, MF_BYPOSITION);
  1636. }
  1637. }
  1638. uMsgFld = DFM_WM_INITMENUPOPUP;
  1639. wParamFld = wParam;
  1640. lParamFld = GetFldFirst(this);
  1641. break;
  1642. case WM_MENUSELECT:
  1643. idCmd = (UINT) LOWORD(wParam);
  1644. // cannot use InRange because _idVerbMax can be equal to _idDelayInvokeMax
  1645. id = idCmd-_idCmdFirst;
  1646. if (_punkSite && (_bInitMenuPopup || (_hdsaStatics && _idVerbMax <= id)) && id < _idDelayInvokeMax)
  1647. {
  1648. IShellBrowser *psb;
  1649. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb))))
  1650. {
  1651. MENUITEMINFO mii;
  1652. mii.cbSize = sizeof(mii);
  1653. mii.fMask = MIIM_DATA;
  1654. mii.cch = 0; //just in case
  1655. if (GetMenuItemInfo(_hmenu, idCmd, FALSE, &mii))
  1656. {
  1657. SEARCHEXTDATA *psed = (SEARCHEXTDATA *)mii.dwItemData;
  1658. psb->SetStatusTextSB(psed->wszHelpText);
  1659. }
  1660. psb->Release();
  1661. }
  1662. }
  1663. return S_OK;
  1664. case WM_MENUCHAR:
  1665. if ((_uFlags & CMF_FINDHACK) && _hdsaCustomInfo)
  1666. {
  1667. int cItems = DSA_GetItemCount(_hdsaCustomInfo);
  1668. for (int i = 0; i < cItems; i++)
  1669. {
  1670. SEARCHINFO* psinfo = (SEARCHINFO*)DSA_GetItemPtr(_hdsaCustomInfo, i);
  1671. ASSERT(psinfo);
  1672. SEARCHEXTDATA* psed = psinfo->psed;
  1673. if (psed)
  1674. {
  1675. TCHAR szMenu[MAX_PATH];
  1676. SHUnicodeToTChar(psed->wszMenuText, szMenu, ARRAYSIZE(szMenu));
  1677. if (_MenuCharMatch(szMenu, (TCHAR)LOWORD(wParam), FALSE))
  1678. {
  1679. if (plResult)
  1680. *plResult = MAKELONG(GetMenuPosFromID((HMENU)lParam, psinfo->idCmd), MNC_EXECUTE);
  1681. return S_OK;
  1682. }
  1683. }
  1684. }
  1685. if (plResult)
  1686. *plResult = MAKELONG(0, MNC_IGNORE);
  1687. return S_FALSE;
  1688. }
  1689. else
  1690. {
  1691. // TODO: this should probably get the idCmd of the MFS_HILITE item so we forward to the correct hdxa...
  1692. idCmd = GetMenuItemID((HMENU)lParam, 0);
  1693. }
  1694. break;
  1695. default:
  1696. return E_FAIL;
  1697. }
  1698. // bias this down to the extension range (comes right after the folder range)
  1699. idCmd -= _idCmdFirst + _idFolderMax;
  1700. // Only forward along on IContextMenu3 as some shell extensions say they support
  1701. // IContextMenu2, but fail and bring down the shell...
  1702. IContextMenu3 *pcmItem;
  1703. if (SUCCEEDED(HDXA_FindByCommand(_hdxa, idCmd, IID_PPV_ARG(IContextMenu3, &pcmItem))))
  1704. {
  1705. HRESULT hr = pcmItem->HandleMenuMsg2(uMsg, wParam, lParam, plResult);
  1706. pcmItem->Release();
  1707. return hr;
  1708. }
  1709. // redirect to the folder callback
  1710. if (_pcmcb)
  1711. return _pcmcb->CallBack(_psf, _hwnd, _pdtobj, uMsgFld, wParamFld, lParamFld);
  1712. return E_FAIL;
  1713. }
  1714. STDMETHODIMP CDefFolderMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
  1715. {
  1716. return HandleMenuMsg2(uMsg,wParam,lParam,NULL);
  1717. }
  1718. STDMETHODIMP CDefFolderMenu::QueryService(REFGUID guidService, REFIID riid, void **ppvObj)
  1719. {
  1720. if (IsEqualGUID(guidService, SID_CtxQueryAssociations))
  1721. {
  1722. if (_paa)
  1723. return _paa->QueryInterface(riid, ppvObj);
  1724. else
  1725. {
  1726. *ppvObj = NULL;
  1727. return E_NOINTERFACE;
  1728. }
  1729. }
  1730. else
  1731. return IUnknown_QueryService(_punkSite, guidService, riid, ppvObj);
  1732. }
  1733. STDMETHODIMP CDefFolderMenu::GetSearchGUID(GUID *pGuid)
  1734. {
  1735. HRESULT hr = E_FAIL;
  1736. if (_iStaticInvoked != -1)
  1737. {
  1738. STATICITEMINFO *psii = (STATICITEMINFO *)DSA_GetItemPtr(_hdsaStatics, _iStaticInvoked);
  1739. if (psii)
  1740. {
  1741. *pGuid = psii->guidSearch;
  1742. hr = S_OK;
  1743. }
  1744. }
  1745. return hr;
  1746. }
  1747. STDMETHODIMP CDefFolderMenu::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
  1748. {
  1749. IUnknown_Set((IUnknown **)&_pdtobj, pdtobj); // just grab this guy
  1750. for (int i = 0; i < DSA_GetItemCount(_hdxa); i++)
  1751. {
  1752. ContextMenuInfo *pcmi = (ContextMenuInfo *)DSA_GetItemPtr(_hdxa, i);
  1753. IShellExtInit *psei;
  1754. if (SUCCEEDED(pcmi->pcm->QueryInterface(IID_PPV_ARG(IShellExtInit, &psei))))
  1755. {
  1756. psei->Initialize(pidlFolder, pdtobj, hkeyProgID);
  1757. psei->Release();
  1758. }
  1759. }
  1760. return S_OK;
  1761. }
  1762. //=============================================================================
  1763. // HDXA stuff
  1764. //=============================================================================
  1765. //
  1766. // This function enumerate all the context menu handlers and let them
  1767. // append menuitems. Each context menu handler will create an object
  1768. // which support IContextMenu interface. We call QueryContextMenu()
  1769. // member function of all those IContextMenu object to let them append
  1770. // menuitems. For each IContextMenu object, we create ContextMenuInfo
  1771. // struct and append it to hdxa (which is a dynamic array of ContextMenuInfo).
  1772. //
  1773. // The caller will release all those IContextMenu objects, by calling
  1774. // its Release() member function.
  1775. //
  1776. // Arguments:
  1777. // hdxa -- Handler of the dynamic ContextMenuInfo struct array
  1778. // pdata -- Specifies the selected items (files)
  1779. // hkeyShellEx -- Specifies the reg.dat class we should enumurate handlers
  1780. // hkeyProgID -- Specifies the program identifier of the selected file/directory
  1781. // pszHandlerKey -- Specifies the reg.dat key to the handler list
  1782. // pidlFolder -- Specifies the folder (drop target)
  1783. // hmenu -- Specifies the menu to be modified
  1784. // uInsert -- Specifies the position to be insert menuitems
  1785. // idCmdFirst -- Specifies the first menuitem ID to be used
  1786. // idCmdLast -- Specifies the last menuitem ID to be used
  1787. //
  1788. // Returns:
  1789. // The first menuitem ID which is not used.
  1790. //
  1791. // History:
  1792. // 02-25-93 SatoNa Created
  1793. //
  1794. // 06-30-97 lAmadio Modified to add ID mapping support.
  1795. UINT HDXA_AppendMenuItems(HDXA hdxa, IDataObject *pdtobj,
  1796. UINT nKeys, HKEY *ahkeys, LPCITEMIDLIST pidlFolder,
  1797. HMENU hmenu, UINT uInsert, UINT idCmdFirst, UINT idCmdLast,
  1798. UINT fFlags, HDCA hdca)
  1799. {
  1800. QCMINFO qcm = {hmenu, uInsert, idCmdFirst, idCmdLast, NULL};
  1801. return HDXA_AppendMenuItems2(hdxa, pdtobj, nKeys, ahkeys, pidlFolder, &qcm, fFlags, hdca, NULL);
  1802. }
  1803. UINT HDXA_AppendMenuItems2(HDXA hdxa, IDataObject *pdtobj,
  1804. UINT nKeys, HKEY *ahkeys, LPCITEMIDLIST pidlFolder,
  1805. QCMINFO* pqcm, UINT fFlags, HDCA hdca, IUnknown* pSite)
  1806. {
  1807. const UINT idCmdBase = pqcm->idCmdFirst;
  1808. UINT idCmdFirst = pqcm->idCmdFirst;
  1809. // Apparently, somebody has already called into here with this object. We
  1810. // need to keep the ID ranges separate, so we'll put the new ones at the
  1811. // end.
  1812. // If QueryContextMenu is called too many times, we will run out of
  1813. // ID range and not add anything. We could try storing the information
  1814. // used to create each pcm (HKEY, GUID, and fFlags) and reuse some of them,
  1815. // but then we would have to worry about what if the number of commands
  1816. // grows and other details; this is just not worth the effort since
  1817. // probably nobody will ever have a problem. The rule of thumb is to
  1818. // create an IContextMenu, do the QueryContextMenu and InvokeCommand, and
  1819. // then Release it.
  1820. int idca = DSA_GetItemCount(hdxa);
  1821. if (idca > 0)
  1822. {
  1823. ContextMenuInfo *pcmi = (ContextMenuInfo *)DSA_GetItemPtr(hdxa, idca-1);
  1824. idCmdFirst += pcmi->idCmdMax;
  1825. }
  1826. // Note that we need to reverse the order because each extension
  1827. // will insert menuitems "above" uInsert.
  1828. UINT uInsertOffset = 0;
  1829. for (idca = DCA_GetItemCount(hdca) - 1; idca >= 0; idca--)
  1830. {
  1831. TCHAR szCLSID[GUIDSTR_MAX];
  1832. TCHAR szRegKey[GUIDSTR_MAX + 40];
  1833. CLSID clsid = *DCA_GetItem(hdca, idca);
  1834. SHStringFromGUID(clsid, szCLSID, ARRAYSIZE(szCLSID));
  1835. // avoid creating an instance (loading the DLL) when:
  1836. // 1. fFlags has CMF_DEFAULTONLY and
  1837. // 2. CLSID\clsid\MayChangeDefault does not exist
  1838. if ((fFlags & CMF_DEFAULTONLY) && (clsid != CLSID_ShellFileDefExt))
  1839. {
  1840. wsprintf(szRegKey, TEXT("CLSID\\%s\\shellex\\MayChangeDefaultMenu"), szCLSID);
  1841. if (SHRegQueryValue(HKEY_CLASSES_ROOT, szRegKey, NULL, NULL) != ERROR_SUCCESS)
  1842. {
  1843. DebugMsg(TF_MENU, TEXT("HDXA_AppendMenuItems skipping %s"), szCLSID);
  1844. continue;
  1845. }
  1846. }
  1847. IShellExtInit *psei = NULL;
  1848. IContextMenu *pcm = NULL;
  1849. // Try all the class keys in order
  1850. for (UINT nCurKey = 0; nCurKey < nKeys; nCurKey++)
  1851. {
  1852. // These cam from HKCR so need to go through administrator approval
  1853. if (!psei && FAILED(DCA_ExtCreateInstance(hdca, idca, IID_PPV_ARG(IShellExtInit, &psei))))
  1854. break;
  1855. if (FAILED(psei->Initialize(pidlFolder, pdtobj, ahkeys[nCurKey])))
  1856. continue;
  1857. // Only get the pcm after initializing
  1858. if (!pcm && FAILED(psei->QueryInterface(IID_PPV_ARG(IContextMenu, &pcm))))
  1859. continue;
  1860. wsprintf(szRegKey, TEXT("CLSID\\%s"), szCLSID);
  1861. // Webvw needs the site in order to do its QueryContextMenu
  1862. ContextMenuInfo cmi;
  1863. cmi.pcm = pcm;
  1864. cmi.dwCompat = SHGetObjectCompatFlags(NULL, &clsid);
  1865. ContextMenuInfo_SetSite(&cmi, pSite);
  1866. HRESULT hr;
  1867. int cMenuItemsLast = GetMenuItemCount(pqcm->hmenu);
  1868. DWORD dwExtType, dwType, dwSize = sizeof(dwExtType);
  1869. if (SHGetValue(HKEY_CLASSES_ROOT, szRegKey, TEXT("flags"), &dwType, (BYTE*)&dwExtType, &dwSize) == ERROR_SUCCESS &&
  1870. dwType == REG_DWORD &&
  1871. (NULL != pqcm->pIdMap) &&
  1872. dwExtType < pqcm->pIdMap->nMaxIds)
  1873. {
  1874. //Explanation:
  1875. //Here we are trying to add a context menu extension to an already
  1876. //existing menu, owned by the sister object of DefView. We used the callback
  1877. //to get a list of extension "types" and their place within the menu, relative
  1878. //to IDs that the sister object inserted already. That object also told us
  1879. //where to put extensions, before or after the ID. Since they are IDs and not
  1880. //positions, we have to convert using GetMenuPosFromID.
  1881. hr = pcm->QueryContextMenu(
  1882. pqcm->hmenu,
  1883. GetMenuPosFromID(pqcm->hmenu, pqcm->pIdMap->pIdList[dwExtType].id) +
  1884. ((pqcm->pIdMap->pIdList[dwExtType].fFlags & QCMINFO_PLACE_AFTER) ? 1 : 0),
  1885. idCmdFirst,
  1886. pqcm->idCmdLast, fFlags);
  1887. }
  1888. else
  1889. hr = pcm->QueryContextMenu(pqcm->hmenu, pqcm->indexMenu + uInsertOffset, idCmdFirst, pqcm->idCmdLast, fFlags);
  1890. UINT citems = HRESULT_CODE(hr);
  1891. if (SUCCEEDED(hr) && citems)
  1892. {
  1893. cmi.idCmdFirst = idCmdFirst - idCmdBase;
  1894. cmi.idCmdMax = cmi.idCmdFirst + citems;
  1895. cmi.clsid = clsid; // for debugging
  1896. if (DSA_AppendItem(hdxa, &cmi) == -1)
  1897. {
  1898. // There is no "clean" way to remove menu items, so
  1899. // we should check the add to the DSA before adding the
  1900. // menu items
  1901. DebugMsg(DM_ERROR, TEXT("filemenu.c ERROR: DSA_GetItemPtr failed (memory overflow)"));
  1902. }
  1903. else
  1904. {
  1905. pcm->AddRef();
  1906. }
  1907. idCmdFirst += citems;
  1908. FullDebugMsg(TF_MENU, TEXT("HDXA_Append: %d, %d"), idCmdFirst, citems);
  1909. // keep going if it is our internal handler
  1910. if (clsid == CLSID_ShellFileDefExt)
  1911. {
  1912. //
  1913. // for static registry verbs, make sure that
  1914. // they are added in priority of their specificity.
  1915. //
  1916. // the first key needs its verbs at the top
  1917. // unless it is not the default handler.
  1918. // so if the default hasnt been set,
  1919. // then we dont push down the insert position.
  1920. //
  1921. // like "Directory" is more specific than "Folder"
  1922. // but the default verb is on "Folder". so "Directory"
  1923. // wont set the default verb, but "Folder" will.
  1924. //
  1925. if (-1 != GetMenuDefaultItem(pqcm->hmenu, TRUE, 0))
  1926. {
  1927. // a default has been set, so each subsequent
  1928. // key is less important.
  1929. uInsertOffset += GetMenuItemCount(pqcm->hmenu) - cMenuItemsLast;
  1930. }
  1931. }
  1932. else
  1933. {
  1934. // try to bubble up the default to the top if possible,
  1935. // since some apps just invoke the 0th index on the menu
  1936. // instead of querying the menu for the default
  1937. if (0 == uInsertOffset && (0 == GetMenuDefaultItem(pqcm->hmenu, TRUE, 0)))
  1938. uInsertOffset++;
  1939. // only CLSID_ShellFileDefExt gets a shot
  1940. // at every key. the rest are assumed
  1941. // to do most of their work from the IDataObject
  1942. break;
  1943. }
  1944. pcm->Release();
  1945. pcm = NULL;
  1946. psei->Release();
  1947. psei = NULL;
  1948. continue; // next hkey
  1949. }
  1950. }
  1951. if (pcm)
  1952. pcm->Release();
  1953. if (psei)
  1954. psei->Release();
  1955. }
  1956. return idCmdFirst;
  1957. }
  1958. // This function is called after the user select one of add-in menu items.
  1959. // This function calls IncokeCommand method of corresponding context menu
  1960. // object.
  1961. //
  1962. // hdxa -- Handler of the dynamic ContextMenuInfo struct array
  1963. // idCmd -- Specifies the menu item ID
  1964. // hwndParent -- Specifies the parent window.
  1965. // pszWorkingDir -- Specifies the working directory.
  1966. //
  1967. // Returns:
  1968. // IDCMD_PROCESSED, if InvokeCommand method is called; idCmd, otherwise
  1969. HRESULT HDXA_LetHandlerProcessCommandEx(HDXA hdxa, LPCMINVOKECOMMANDINFOEX pici, UINT_PTR * pidCmd)
  1970. {
  1971. HRESULT hr = S_OK;
  1972. UINT_PTR idCmd;
  1973. if (!pidCmd)
  1974. pidCmd = &idCmd;
  1975. *pidCmd = (UINT_PTR)pici->lpVerb;
  1976. // try handlers in order, the first to take it wins
  1977. for (int i = 0; i < DSA_GetItemCount(hdxa); i++)
  1978. {
  1979. ContextMenuInfo *pcmi = (ContextMenuInfo *)DSA_GetItemPtr(hdxa, i);
  1980. if (!IS_INTRESOURCE(pici->lpVerb))
  1981. {
  1982. // invoke by cannonical name case
  1983. // app compat: some ctx menu extension always succeed regardless
  1984. // if it is theirs or not. better to never pass them a string
  1985. if (!(pcmi->dwCompat & OBJCOMPATF_CTXMENU_NOVERBS))
  1986. {
  1987. hr = pcmi->pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)pici);
  1988. if (SUCCEEDED(hr))
  1989. {
  1990. *pidCmd = IDCMD_PROCESSED;
  1991. break;
  1992. }
  1993. }
  1994. else
  1995. hr = E_FAIL;
  1996. }
  1997. else if ((*pidCmd >= pcmi->idCmdFirst) && (*pidCmd < pcmi->idCmdMax))
  1998. {
  1999. CMINVOKECOMMANDINFOEX ici;
  2000. CopyInvokeInfo(&ici, (CMINVOKECOMMANDINFO *)pici);
  2001. ici.lpVerb = (LPSTR)MAKEINTRESOURCE(*pidCmd - pcmi->idCmdFirst);
  2002. hr = pcmi->pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
  2003. if (SUCCEEDED(hr))
  2004. {
  2005. *pidCmd = IDCMD_PROCESSED;
  2006. }
  2007. break;
  2008. }
  2009. }
  2010. // It's OK if (idCmd != IDCMD_PROCESSED) because some callers will try to use several
  2011. // IContextMenu implementations in order to get the IContextMenu for the selected items,
  2012. // the IContextMenu for the background, etc. CBackgrndMenu::InvokeCommand() does this.
  2013. // -BryanSt (04/29/1999)
  2014. return hr;
  2015. }
  2016. HRESULT HDXA_GetCommandString(HDXA hdxa, UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax)
  2017. {
  2018. HRESULT hr = E_INVALIDARG;
  2019. LPTSTR pCmd = (LPTSTR)idCmd;
  2020. if (!hdxa)
  2021. return E_INVALIDARG;
  2022. //
  2023. // One of add-in menuitems is selected. Let the context
  2024. // menu handler process it.
  2025. //
  2026. for (int i = 0; i < DSA_GetItemCount(hdxa); i++)
  2027. {
  2028. ContextMenuInfo *pcmi = (ContextMenuInfo *)DSA_GetItemPtr(hdxa, i);
  2029. if (!IS_INTRESOURCE(idCmd))
  2030. {
  2031. // This must be a string command; see if this handler wants it
  2032. if (pcmi->pcm->GetCommandString(idCmd, uType,
  2033. pwReserved, pszName, cchMax) == S_OK)
  2034. {
  2035. return S_OK;
  2036. }
  2037. }
  2038. //
  2039. // Check if it is for this context menu handler.
  2040. //
  2041. // Notes: We can't use InRange macro because idCmdFirst might
  2042. // be equal to idCmdLast.
  2043. // if (InRange(idCmd, pcmi->idCmdFirst, pcmi->idCmdMax-1))
  2044. else if (idCmd >= pcmi->idCmdFirst && idCmd < pcmi->idCmdMax)
  2045. {
  2046. //
  2047. // Yes, it is. Let it handle this menuitem.
  2048. //
  2049. hr = pcmi->pcm->GetCommandString(idCmd-pcmi->idCmdFirst, uType, pwReserved, pszName, cchMax);
  2050. break;
  2051. }
  2052. }
  2053. return hr;
  2054. }
  2055. HRESULT HDXA_FindByCommand(HDXA hdxa, UINT idCmd, REFIID riid, void **ppv)
  2056. {
  2057. HRESULT hr = E_FAIL;
  2058. *ppv = NULL; // bug nt power toy does not properly null out in error cases...
  2059. if (hdxa)
  2060. {
  2061. for (int i = 0; i < DSA_GetItemCount(hdxa); i++)
  2062. {
  2063. ContextMenuInfo *pcmi = (ContextMenuInfo *)DSA_GetItemPtr(hdxa, i);
  2064. if (idCmd >= pcmi->idCmdFirst && idCmd < pcmi->idCmdMax)
  2065. {
  2066. // APPCOMPAT: PGP50 can only be QIed for IContextMenu, IShellExtInit, and IUnknown.
  2067. if (!(pcmi->dwCompat & OBJCOMPATF_CTXMENU_LIMITEDQI))
  2068. hr = pcmi->pcm->QueryInterface(riid, ppv);
  2069. else
  2070. hr = E_FAIL;
  2071. break;
  2072. }
  2073. }
  2074. }
  2075. return hr;
  2076. }
  2077. //
  2078. // This function releases all the IContextMenu objects in the dynamic
  2079. // array of ContextMenuInfo,
  2080. //
  2081. void HDXA_DeleteAll(HDXA hdxa)
  2082. {
  2083. if (hdxa)
  2084. {
  2085. // Release all the IContextMenu objects, then destroy the DSA.
  2086. for (int i = 0; i < DSA_GetItemCount(hdxa); i++)
  2087. {
  2088. ContextMenuInfo *pcmi = (ContextMenuInfo *)DSA_GetItemPtr(hdxa, i);
  2089. if (pcmi->pcm)
  2090. {
  2091. pcmi->pcm->Release();
  2092. }
  2093. }
  2094. DSA_DeleteAllItems(hdxa);
  2095. }
  2096. }
  2097. // This function releases all the IContextMenu objects in the dynamic
  2098. // array of ContextMenuInfo, then destroys the dynamic array.
  2099. void HDXA_Destroy(HDXA hdxa)
  2100. {
  2101. if (hdxa)
  2102. {
  2103. HDXA_DeleteAll(hdxa);
  2104. DSA_Destroy(hdxa);
  2105. }
  2106. }
  2107. class CContextMenuCBImpl : public IContextMenuCB
  2108. {
  2109. public:
  2110. CContextMenuCBImpl(LPFNDFMCALLBACK pfn) : _pfn(pfn), _cRef(1) {}
  2111. // IUnknown
  2112. STDMETHOD(QueryInterface)(REFIID riid, void **ppv)
  2113. {
  2114. static const QITAB qit[] = {
  2115. QITABENT(CContextMenuCBImpl, IContextMenuCB), // IID_IContextMenuCB
  2116. { 0 },
  2117. };
  2118. return QISearch(this, qit, riid, ppv);
  2119. }
  2120. STDMETHOD_(ULONG,AddRef)()
  2121. {
  2122. return InterlockedIncrement(&_cRef);
  2123. }
  2124. STDMETHOD_(ULONG,Release)()
  2125. {
  2126. if (InterlockedDecrement(&_cRef))
  2127. return _cRef;
  2128. delete this;
  2129. return 0;
  2130. }
  2131. // IContextMenuCB
  2132. STDMETHOD(CallBack)(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2133. {
  2134. return _pfn ? _pfn(psf, hwnd, pdtobj, uMsg, wParam, lParam) : E_FAIL;
  2135. }
  2136. private:
  2137. LONG _cRef;
  2138. LPFNDFMCALLBACK _pfn;
  2139. };
  2140. STDAPI CreateDefaultContextMenu(DEFCONTEXTMENU *pdcm, IContextMenu **ppcm)
  2141. {
  2142. HRESULT hr = E_OUTOFMEMORY;
  2143. *ppcm = 0;
  2144. CDefFolderMenu *pmenu = new CDefFolderMenu(FALSE);
  2145. if (pmenu)
  2146. {
  2147. hr = pmenu->Init(pdcm);
  2148. if (SUCCEEDED(hr))
  2149. hr = pmenu->QueryInterface(IID_PPV_ARG(IContextMenu, ppcm));
  2150. pmenu->Release();
  2151. }
  2152. return hr;
  2153. }
  2154. STDAPI CDefFolderMenu_CreateHKeyMenu(HWND hwnd, HKEY hkey, IContextMenu **ppcm)
  2155. {
  2156. HRESULT hr = E_OUTOFMEMORY;
  2157. *ppcm = 0;
  2158. CDefFolderMenu *pmenu = new CDefFolderMenu(TRUE);
  2159. if (pmenu)
  2160. {
  2161. DEFCONTEXTMENU dcm = {0};
  2162. dcm.hwnd = hwnd;
  2163. dcm.aKeys = &hkey;
  2164. dcm.cKeys = 1;
  2165. hr = pmenu->Init(&dcm);
  2166. if (SUCCEEDED(hr))
  2167. hr = pmenu->QueryInterface(IID_PPV_ARG(IContextMenu, ppcm));
  2168. pmenu->Release();
  2169. }
  2170. return hr;
  2171. }
  2172. STDAPI CDefFolderMenu_Create2Ex(LPCITEMIDLIST pidlFolder, HWND hwnd,
  2173. UINT cidl, LPCITEMIDLIST *apidl,
  2174. IShellFolder *psf, IContextMenuCB *pcmcb,
  2175. UINT nKeys, const HKEY *ahkeys,
  2176. IContextMenu **ppcm)
  2177. {
  2178. DEFCONTEXTMENU dcm = {
  2179. hwnd,
  2180. pcmcb,
  2181. pidlFolder,
  2182. psf,
  2183. cidl,
  2184. apidl,
  2185. NULL,
  2186. nKeys,
  2187. ahkeys};
  2188. return CreateDefaultContextMenu(&dcm, ppcm);
  2189. }
  2190. STDAPI CDefFolderMenu_CreateEx(LPCITEMIDLIST pidlFolder,
  2191. HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
  2192. IShellFolder *psf, IContextMenuCB *pcmcb,
  2193. HKEY hkeyProgID, HKEY hkeyBaseProgID,
  2194. IContextMenu **ppcm)
  2195. {
  2196. HKEY aKeys[2] = { hkeyProgID, hkeyBaseProgID};
  2197. DEFCONTEXTMENU dcm = {
  2198. hwnd,
  2199. pcmcb,
  2200. pidlFolder,
  2201. psf,
  2202. cidl,
  2203. apidl,
  2204. NULL,
  2205. 2,
  2206. aKeys};
  2207. return CreateDefaultContextMenu(&dcm, ppcm);
  2208. }
  2209. //
  2210. // old style CDefFolderMenu_Create and CDefFolderMenu_Create2
  2211. //
  2212. STDAPI CDefFolderMenu_Create(LPCITEMIDLIST pidlFolder,
  2213. HWND hwndOwner,
  2214. UINT cidl, LPCITEMIDLIST * apidl,
  2215. IShellFolder *psf,
  2216. LPFNDFMCALLBACK pfn,
  2217. HKEY hkeyProgID, HKEY hkeyBaseProgID,
  2218. IContextMenu **ppcm)
  2219. {
  2220. HRESULT hr;
  2221. IContextMenuCB *pcmcb = new CContextMenuCBImpl(pfn);
  2222. if (pcmcb)
  2223. {
  2224. HKEY aKeys[2] = { hkeyProgID, hkeyBaseProgID};
  2225. DEFCONTEXTMENU dcm = {
  2226. hwndOwner,
  2227. pcmcb,
  2228. pidlFolder,
  2229. psf,
  2230. cidl,
  2231. apidl,
  2232. NULL,
  2233. 2,
  2234. aKeys};
  2235. hr = CreateDefaultContextMenu(&dcm, ppcm);
  2236. pcmcb->Release();
  2237. }
  2238. else
  2239. {
  2240. *ppcm = NULL;
  2241. hr = E_OUTOFMEMORY;
  2242. }
  2243. return hr;
  2244. }
  2245. STDAPI CDefFolderMenu_Create2(LPCITEMIDLIST pidlFolder, HWND hwnd,
  2246. UINT cidl, LPCITEMIDLIST *apidl,
  2247. IShellFolder *psf, LPFNDFMCALLBACK pfn,
  2248. UINT nKeys, const HKEY *ahkeys,
  2249. IContextMenu **ppcm)
  2250. {
  2251. HRESULT hr;
  2252. IContextMenuCB *pcmcb = new CContextMenuCBImpl(pfn);
  2253. if (pcmcb)
  2254. {
  2255. hr = CDefFolderMenu_Create2Ex(pidlFolder, hwnd, cidl, apidl, psf, pcmcb,
  2256. nKeys, ahkeys, ppcm);
  2257. pcmcb->Release();
  2258. }
  2259. else
  2260. {
  2261. *ppcm = NULL;
  2262. hr = E_OUTOFMEMORY;
  2263. }
  2264. return hr;
  2265. }
  2266. #define CXIMAGEGAP 6
  2267. void DrawMenuItem(DRAWITEMSTRUCT* pdi, LPCTSTR pszText, UINT iIcon)
  2268. {
  2269. if ((pdi->itemAction & ODA_SELECT) || (pdi->itemAction & ODA_DRAWENTIRE))
  2270. {
  2271. int x, y;
  2272. SIZE sz;
  2273. RECT rc;
  2274. // Draw the image (if there is one).
  2275. GetTextExtentPoint(pdi->hDC, pszText, lstrlen(pszText), &sz);
  2276. if (pdi->itemState & ODS_SELECTED)
  2277. {
  2278. SetBkColor(pdi->hDC, GetSysColor(COLOR_HIGHLIGHT));
  2279. SetTextColor(pdi->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
  2280. FillRect(pdi->hDC,&pdi->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
  2281. }
  2282. else
  2283. {
  2284. SetTextColor(pdi->hDC, GetSysColor(COLOR_MENUTEXT));
  2285. FillRect(pdi->hDC,&pdi->rcItem,GetSysColorBrush(COLOR_MENU));
  2286. }
  2287. rc = pdi->rcItem;
  2288. rc.left += +2 * CXIMAGEGAP + g_cxSmIcon;
  2289. DrawText(pdi->hDC,pszText,lstrlen(pszText), &rc, DT_SINGLELINE | DT_VCENTER);
  2290. if (iIcon != -1)
  2291. {
  2292. x = pdi->rcItem.left + CXIMAGEGAP;
  2293. y = (pdi->rcItem.bottom+pdi->rcItem.top-g_cySmIcon)/2;
  2294. HIMAGELIST himlSmall;
  2295. Shell_GetImageLists(NULL, &himlSmall);
  2296. ImageList_Draw(himlSmall, iIcon, pdi->hDC, x, y, ILD_TRANSPARENT);
  2297. }
  2298. else
  2299. {
  2300. x = pdi->rcItem.left + CXIMAGEGAP;
  2301. y = (pdi->rcItem.bottom+pdi->rcItem.top-g_cySmIcon)/2;
  2302. }
  2303. }
  2304. }
  2305. LRESULT MeasureMenuItem(MEASUREITEMSTRUCT *pmi, LPCTSTR pszText)
  2306. {
  2307. LRESULT lres = FALSE;
  2308. // Get the rough height of an item so we can work out when to break the
  2309. // menu. User should really do this for us but that would be useful.
  2310. HDC hdc = GetDC(NULL);
  2311. if (hdc)
  2312. {
  2313. // REVIEW cache out the menu font?
  2314. NONCLIENTMETRICS ncm;
  2315. ncm.cbSize = sizeof(ncm);
  2316. if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE))
  2317. {
  2318. HFONT hfont = CreateFontIndirect(&ncm.lfMenuFont);
  2319. if (hfont)
  2320. {
  2321. SIZE sz;
  2322. HFONT hfontOld = (HFONT)SelectObject(hdc, hfont);
  2323. GetTextExtentPoint(hdc, pszText, lstrlen(pszText), &sz);
  2324. pmi->itemHeight = max (g_cySmIcon + CXIMAGEGAP / 2, ncm.iMenuHeight);
  2325. pmi->itemWidth = g_cxSmIcon + 2 * CXIMAGEGAP + sz.cx;
  2326. pmi->itemWidth = 2 * CXIMAGEGAP + sz.cx;
  2327. SelectObject(hdc, hfontOld);
  2328. DeleteObject(hfont);
  2329. lres = TRUE;
  2330. }
  2331. }
  2332. ReleaseDC(NULL, hdc);
  2333. }
  2334. return lres;
  2335. }
  2336. void CDefFolderMenu::_DrawItem(DRAWITEMSTRUCT *pdi)
  2337. {
  2338. SEARCHEXTDATA *psed = (SEARCHEXTDATA *)pdi->itemData;
  2339. if (psed)
  2340. {
  2341. TCHAR szMenuText[MAX_PATH];
  2342. SHUnicodeToTChar(psed->wszMenuText, szMenuText, ARRAYSIZE(szMenuText));
  2343. DrawMenuItem(pdi, szMenuText, psed->iIcon);
  2344. }
  2345. }
  2346. LRESULT CDefFolderMenu::_MeasureItem(MEASUREITEMSTRUCT *pmi)
  2347. {
  2348. SEARCHEXTDATA *psed = (SEARCHEXTDATA *)pmi->itemData;
  2349. if (psed)
  2350. {
  2351. TCHAR szMenuText[MAX_PATH];
  2352. SHUnicodeToTChar(psed->wszMenuText, szMenuText, ARRAYSIZE(szMenuText));
  2353. return MeasureMenuItem(pmi, szMenuText);
  2354. }
  2355. return FALSE;
  2356. }