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.

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