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.

3327 lines
95 KiB

  1. #include "stdafx.h"
  2. #pragma hdrstop
  3. #include <oleacc.h> // MSAAMENUINFO stuff
  4. #include <runtask.h>
  5. #include "datautil.h"
  6. #include "idlcomm.h"
  7. #include "stgutil.h"
  8. #include <winnls.h>
  9. #include "filetbl.h"
  10. #include "cdburn.h"
  11. #include "mtpt.h"
  12. #ifndef CMF_DVFILE
  13. #define CMF_DVFILE 0x00010000 // "File" pulldown
  14. #endif
  15. class CSendToMenu : public IContextMenu3, IShellExtInit, IOleWindow
  16. {
  17. public:
  18. // IUnknown
  19. STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
  20. STDMETHOD_(ULONG,AddRef)(void);
  21. STDMETHOD_(ULONG,Release)(void);
  22. // IContextMenu
  23. STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  24. STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpici);
  25. STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax);
  26. // IContextMenu2
  27. STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam);
  28. // IContextMenu3
  29. STDMETHOD(HandleMenuMsg2)(UINT uMsg, WPARAM wParam, LPARAM lParam,LRESULT *lResult);
  30. // IShellExtInit
  31. STDMETHOD(Initialize)(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);
  32. // IOleWindow
  33. STDMETHOD(GetWindow)(HWND *phwnd);
  34. STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode) {return E_NOTIMPL;};
  35. private:
  36. CSendToMenu();
  37. ~CSendToMenu();
  38. LONG _cRef;
  39. HMENU _hmenu;
  40. UINT _idCmdFirst;
  41. BOOL _bFirstTime;
  42. HWND _hwnd;
  43. IDataObject *_pdtobj;
  44. LPITEMIDLIST _pidlLast;
  45. DWORD _GetKeyState(void);
  46. HRESULT _DoDragDrop(HWND hwndParent, IDropTarget *pdrop);
  47. BOOL _CanDrop(IShellFolder *psf, LPCITEMIDLIST pidl);
  48. HRESULT _MenuCallback(UINT fmm, IShellFolder *psf, LPCITEMIDLIST pidl);
  49. HRESULT _RemovableDrivesMenuCallback(UINT fmm, IShellFolder *psf, LPCITEMIDLIST pidl);
  50. static HRESULT CALLBACK s_MenuCallback(UINT fmm, LPARAM lParam, IShellFolder *psf, LPCITEMIDLIST pidl);
  51. static HRESULT CALLBACK s_RemovableDrivesMenuCallback(UINT fmm, LPARAM lParam, IShellFolder *psf, LPCITEMIDLIST pidl);
  52. friend HRESULT CSendToMenu_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvOut);
  53. };
  54. CSendToMenu::CSendToMenu() : _cRef(1)
  55. {
  56. DllAddRef();
  57. }
  58. CSendToMenu::~CSendToMenu()
  59. {
  60. if (_hmenu)
  61. FileMenu_DeleteAllItems(_hmenu);
  62. if (_pdtobj)
  63. _pdtobj->Release();
  64. ILFree(_pidlLast);
  65. DllRelease();
  66. }
  67. HRESULT CSendToMenu_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvOut)
  68. {
  69. HRESULT hr = E_OUTOFMEMORY;
  70. CSendToMenu *pstm = new CSendToMenu();
  71. if (pstm)
  72. {
  73. hr = pstm->QueryInterface(riid, ppvOut);
  74. pstm->Release();
  75. }
  76. return hr;
  77. }
  78. HRESULT CSendToMenu::QueryInterface(REFIID riid, void **ppvObj)
  79. {
  80. static const QITAB qit[] = {
  81. QITABENT(CSendToMenu, IShellExtInit), // IID_IShellExtInit
  82. QITABENT(CSendToMenu, IOleWindow), // IID_IOleWindow
  83. QITABENT(CSendToMenu, IContextMenu3), // IID_IContextMenu3
  84. QITABENTMULTI(CSendToMenu, IContextMenu2, IContextMenu3), // IID_IContextMenu2
  85. QITABENTMULTI(CSendToMenu, IContextMenu, IContextMenu3), // IID_IContextMenu
  86. { 0 }
  87. };
  88. return QISearch(this, qit, riid, ppvObj);
  89. }
  90. ULONG CSendToMenu::AddRef()
  91. {
  92. return InterlockedIncrement(&_cRef);
  93. }
  94. ULONG CSendToMenu::Release()
  95. {
  96. if (InterlockedDecrement(&_cRef))
  97. return _cRef;
  98. delete this;
  99. return 0;
  100. }
  101. HRESULT CSendToMenu::GetWindow(HWND *phwnd)
  102. {
  103. HRESULT hr = E_INVALIDARG;
  104. if (phwnd)
  105. {
  106. *phwnd = _hwnd;
  107. hr = S_OK;
  108. }
  109. return hr;
  110. }
  111. HRESULT CSendToMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  112. {
  113. // if they want the default menu only (CMF_DEFAULTONLY) OR
  114. // this is being called for a shortcut (CMF_VERBSONLY)
  115. // we don't want to be on the context menu
  116. if (uFlags & (CMF_DEFAULTONLY | CMF_VERBSONLY))
  117. return S_OK;
  118. UINT idMax = idCmdFirst;
  119. _hmenu = CreatePopupMenu();
  120. if (_hmenu)
  121. {
  122. TCHAR szSendLinkTo[80];
  123. TCHAR szSendPageTo[80];
  124. MENUITEMINFO mii;
  125. // add a dummy item so we are identified at WM_INITMENUPOPUP time
  126. LoadString(g_hinst, IDS_SENDLINKTO, szSendLinkTo, ARRAYSIZE(szSendLinkTo));
  127. LoadString(g_hinst, IDS_SENDPAGETO, szSendPageTo, ARRAYSIZE(szSendPageTo));
  128. mii.cbSize = sizeof(MENUITEMINFO);
  129. mii.fMask = MIIM_ID | MIIM_TYPE;
  130. mii.fType = MFT_STRING;
  131. mii.dwTypeData = szSendLinkTo;
  132. mii.wID = idCmdFirst + 1;
  133. if (InsertMenuItem(_hmenu, 0, TRUE, &mii))
  134. {
  135. _idCmdFirst = idCmdFirst + 1; // remember this for later
  136. mii.fType = MFT_STRING;
  137. mii.dwTypeData = szSendLinkTo;
  138. mii.wID = idCmdFirst;
  139. mii.fState = MF_DISABLED | MF_GRAYED;
  140. mii.fMask = MIIM_TYPE | MIIM_SUBMENU | MIIM_ID;
  141. mii.hSubMenu = _hmenu;
  142. if (InsertMenuItem(hmenu, indexMenu, TRUE, &mii))
  143. {
  144. idMax += 0x40; // reserve space for this many items
  145. _bFirstTime = TRUE; // fill this at WM_INITMENUPOPUP time
  146. }
  147. else
  148. {
  149. _hmenu = NULL;
  150. }
  151. }
  152. }
  153. _hmenu = NULL;
  154. return ResultFromShort(idMax - idCmdFirst);
  155. }
  156. DWORD CSendToMenu::_GetKeyState(void)
  157. {
  158. DWORD grfKeyState = MK_LBUTTON; // default
  159. if (GetAsyncKeyState(VK_CONTROL) < 0)
  160. grfKeyState |= MK_CONTROL;
  161. if (GetAsyncKeyState(VK_SHIFT) < 0)
  162. grfKeyState |= MK_SHIFT;
  163. if (GetAsyncKeyState(VK_MENU) < 0)
  164. grfKeyState |= MK_ALT; // menu's don't really allow this
  165. return grfKeyState;
  166. }
  167. HRESULT CSendToMenu::_DoDragDrop(HWND hwndParent, IDropTarget *pdrop)
  168. {
  169. DWORD grfKeyState = _GetKeyState();
  170. if (grfKeyState == MK_LBUTTON)
  171. {
  172. // no modifieres, change default to COPY
  173. grfKeyState = MK_LBUTTON | MK_CONTROL;
  174. DataObj_SetDWORD(_pdtobj, g_cfPreferredDropEffect, DROPEFFECT_COPY);
  175. }
  176. _hwnd = hwndParent;
  177. IUnknown_SetSite(pdrop, SAFECAST(this, IOleWindow *)); // Let them have access to our HWND.
  178. HRESULT hr = SHSimulateDrop(pdrop, _pdtobj, grfKeyState, NULL, NULL);
  179. IUnknown_SetSite(pdrop, NULL);
  180. if (hr == S_FALSE)
  181. {
  182. ShellMessageBox(g_hinst, hwndParent,
  183. MAKEINTRESOURCE(IDS_SENDTO_ERRORMSG),
  184. MAKEINTRESOURCE(IDS_CABINET),
  185. MB_OK|MB_ICONEXCLAMATION);
  186. }
  187. return hr;
  188. }
  189. HRESULT CSendToMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
  190. {
  191. HRESULT hr;
  192. if (_pdtobj && _pidlLast)
  193. {
  194. IDropTarget *pdrop;
  195. hr = SHGetUIObjectFromFullPIDL(_pidlLast, pici->hwnd, IID_PPV_ARG(IDropTarget, &pdrop));
  196. if (SUCCEEDED(hr))
  197. {
  198. hr = _DoDragDrop(pici->hwnd, pdrop);
  199. pdrop->Release();
  200. }
  201. }
  202. else
  203. hr = E_INVALIDARG;
  204. return hr;
  205. }
  206. HRESULT CSendToMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax)
  207. {
  208. return E_NOTIMPL;
  209. }
  210. HRESULT CSendToMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
  211. {
  212. return HandleMenuMsg2(uMsg, wParam, lParam, NULL);
  213. }
  214. BOOL CSendToMenu::_CanDrop(IShellFolder *psf, LPCITEMIDLIST pidl)
  215. {
  216. BOOL fCanDrop = FALSE;
  217. IDropTarget *pdt;
  218. if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST *)&pidl, IID_X_PPV_ARG(IDropTarget, 0, &pdt))))
  219. {
  220. POINTL pt = {0};
  221. DWORD dwEffect = DROPEFFECT_COPY;
  222. // Do a drag enter, if they return no drop effect then we can't drop
  223. if (SUCCEEDED(pdt->DragEnter(_pdtobj, _GetKeyState(), pt, &dwEffect)))
  224. {
  225. if (dwEffect != DROPEFFECT_NONE)
  226. fCanDrop = TRUE; // show it!
  227. pdt->DragLeave();
  228. }
  229. pdt->Release();
  230. }
  231. return fCanDrop;
  232. }
  233. HRESULT CSendToMenu::_MenuCallback(UINT fmm, IShellFolder *psf, LPCITEMIDLIST pidl)
  234. {
  235. HRESULT hr = S_OK;
  236. switch (fmm)
  237. {
  238. case FMM_ADD:
  239. hr = _CanDrop(psf, pidl) ? S_OK : S_FALSE;
  240. break;
  241. case FMM_SETLASTPIDL:
  242. Pidl_Set(&_pidlLast, pidl);
  243. break;
  244. default:
  245. hr = E_FAIL;
  246. }
  247. return hr;
  248. }
  249. HRESULT CSendToMenu::_RemovableDrivesMenuCallback(UINT fmm, IShellFolder *psf, LPCITEMIDLIST pidl)
  250. {
  251. HRESULT hr = S_OK;
  252. switch (fmm)
  253. {
  254. case FMM_ADD:
  255. hr = S_FALSE; // assume we wont show it
  256. if (_CanDrop(psf, pidl))
  257. {
  258. // now we know it's a removable drive. in general we dont want to display cd-rom drives.
  259. // we know this is the my computer folder so just get the parsing name, we need it for GetDriveType.
  260. WCHAR szDrive[MAX_PATH];
  261. if (SUCCEEDED(DisplayNameOf(psf, pidl, SHGDN_FORPARSING, szDrive, ARRAYSIZE(szDrive))))
  262. {
  263. CMountPoint *pmtpt = CMountPoint::GetMountPoint(szDrive);
  264. if (pmtpt)
  265. {
  266. if (pmtpt->IsCDROM())
  267. {
  268. // of all cdroms, only the enabled burning folder is okay to put on sendto
  269. WCHAR szRecorder[4];
  270. if (SUCCEEDED(CDBurn_GetRecorderDriveLetter(szRecorder, ARRAYSIZE(szRecorder))) &&
  271. (lstrcmpiW(szRecorder, szDrive) == 0))
  272. {
  273. hr = S_OK;
  274. }
  275. }
  276. else if (pmtpt->IsFloppy() || pmtpt->IsStrictRemovable() || pmtpt->IsRemovableDevice())
  277. {
  278. // also put on removable devices.
  279. hr = S_OK;
  280. }
  281. pmtpt->Release();
  282. }
  283. else
  284. {
  285. // if this failed it could be a memory condition but its more likely to be that the
  286. // parsing name doesnt map to a mountpoint. in that case fall back to SFGAO_REMOVABLE
  287. // to pick up portable audio devices. if this was because of lowmem its no biggie.
  288. if (SHGetAttributes(psf, pidl, SFGAO_REMOVABLE))
  289. {
  290. hr = S_OK;
  291. }
  292. }
  293. }
  294. }
  295. break;
  296. case FMM_SETLASTPIDL:
  297. Pidl_Set(&_pidlLast, pidl);
  298. break;
  299. default:
  300. hr = E_FAIL;
  301. }
  302. return hr;
  303. }
  304. HRESULT CSendToMenu::s_MenuCallback(UINT fmm, LPARAM lParam, IShellFolder *psf, LPCITEMIDLIST pidl)
  305. {
  306. return ((CSendToMenu*)lParam)->_MenuCallback(fmm, psf, pidl);
  307. }
  308. HRESULT CSendToMenu::s_RemovableDrivesMenuCallback(UINT fmm, LPARAM lParam, IShellFolder *psf, LPCITEMIDLIST pidl)
  309. {
  310. return ((CSendToMenu*)lParam)->_RemovableDrivesMenuCallback(fmm, psf, pidl);
  311. }
  312. HRESULT GetFolder(int csidl, IShellFolder **ppsf)
  313. {
  314. LPITEMIDLIST pidl;
  315. HRESULT hr = SHGetFolderLocation(NULL, csidl, NULL, 0, &pidl);
  316. if (SUCCEEDED(hr))
  317. {
  318. hr = SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidl, ppsf));
  319. ILFree(pidl);
  320. }
  321. return hr;
  322. }
  323. HRESULT CSendToMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
  324. {
  325. HRESULT hr = S_OK;
  326. LRESULT lRes = 0;
  327. switch (uMsg)
  328. {
  329. case WM_INITMENUPOPUP:
  330. if (_bFirstTime)
  331. {
  332. _bFirstTime = FALSE;
  333. //In case of Shell_MergeMenus
  334. if (_hmenu == NULL)
  335. _hmenu = (HMENU)wParam;
  336. // delete the dummy entry
  337. DeleteMenu(_hmenu, 0, MF_BYPOSITION);
  338. FMCOMPOSE fmc = {0};
  339. if (SUCCEEDED(GetFolder(CSIDL_SENDTO, &fmc.psf)))
  340. {
  341. fmc.idCmd = _idCmdFirst;
  342. fmc.grfFlags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
  343. fmc.pfnCallback = s_MenuCallback;
  344. fmc.lParam = (LPARAM)this; // not reference counted
  345. FileMenu_Compose(_hmenu, FMCM_REPLACE, &fmc);
  346. fmc.psf->Release();
  347. }
  348. if (SUCCEEDED(GetFolder(CSIDL_DRIVES, &fmc.psf)))
  349. {
  350. fmc.dwMask = FMC_NOEXPAND;
  351. fmc.idCmd = _idCmdFirst;
  352. fmc.grfFlags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
  353. fmc.pfnCallback = s_RemovableDrivesMenuCallback;
  354. fmc.lParam = (LPARAM)this; // not reference counted
  355. FileMenu_Compose(_hmenu, FMCM_APPEND, &fmc);
  356. fmc.psf->Release();
  357. }
  358. }
  359. else if (_hmenu != (HMENU)wParam)
  360. {
  361. // secondary cascade menu
  362. FileMenu_InitMenuPopup((HMENU)wParam);
  363. }
  364. break;
  365. case WM_DRAWITEM:
  366. {
  367. DRAWITEMSTRUCT *pdi = (DRAWITEMSTRUCT *)lParam;
  368. if (pdi->CtlType == ODT_MENU && pdi->itemID == _idCmdFirst)
  369. {
  370. lRes = FileMenu_DrawItem(NULL, pdi);
  371. }
  372. }
  373. break;
  374. case WM_MEASUREITEM:
  375. {
  376. MEASUREITEMSTRUCT *pmi = (MEASUREITEMSTRUCT *)lParam;
  377. if (pmi->CtlType == ODT_MENU && pmi->itemID == _idCmdFirst)
  378. {
  379. lRes = FileMenu_MeasureItem(NULL, pmi);
  380. }
  381. }
  382. break;
  383. case WM_MENUCHAR:
  384. {
  385. TCHAR ch = (TCHAR)LOWORD(wParam);
  386. HMENU hmenu = (HMENU)lParam;
  387. lRes = FileMenu_HandleMenuChar(hmenu, ch);
  388. }
  389. break;
  390. default:
  391. hr = E_NOTIMPL;
  392. break;
  393. }
  394. if (plResult)
  395. *plResult = lRes;
  396. return hr;
  397. }
  398. HRESULT CSendToMenu::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
  399. {
  400. IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
  401. return S_OK;
  402. }
  403. #define TARGETMENU
  404. #ifdef TARGETMENU
  405. class CTargetMenu : public IShellExtInit, public IContextMenu3
  406. {
  407. // IUnknown
  408. STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
  409. STDMETHOD_(ULONG,AddRef)(void);
  410. STDMETHOD_(ULONG,Release)(void);
  411. // IContextMenu
  412. STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  413. STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpici);
  414. STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax);
  415. // IContextMenu2
  416. STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam);
  417. // IContextMenu3
  418. STDMETHOD(HandleMenuMsg2)(UINT uMsg, WPARAM wParam, LPARAM lParam,LRESULT *lResult);
  419. // IShellExtInit
  420. STDMETHOD(Initialize)(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);
  421. HRESULT GetTargetMenu();
  422. ~CTargetMenu();
  423. CTargetMenu();
  424. LONG _cRef;
  425. HMENU _hmenu;
  426. UINT _idCmdFirst;
  427. BOOL _bFirstTime;
  428. IDataObject *_pdtobj;
  429. LPITEMIDLIST _pidlTarget;
  430. IContextMenu *_pcmTarget;
  431. friend HRESULT CTargetMenu_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvOut);
  432. };
  433. CTargetMenu::CTargetMenu() : _cRef(1)
  434. {
  435. }
  436. CTargetMenu::~CTargetMenu()
  437. {
  438. ILFree(_pidlTarget);
  439. if (_pcmTarget)
  440. _pcmTarget->Release();
  441. if (_pdtobj)
  442. _pdtobj->Release();
  443. }
  444. STDMETHODIMP CTargetMenu::QueryInterface(REFIID riid, void **ppvObj)
  445. {
  446. static const QITAB qit[] = {
  447. QITABENT(CTargetMenu, IShellExtInit), // IID_IShellExtInit
  448. QITABENT(CTargetMenu, IContextMenu3), // IID_IContextMenu3
  449. QITABENTMULTI(CTargetMenu, IContextMenu2, IContextMenu3), // IID_IContextMenu2
  450. QITABENTMULTI(CTargetMenu, IContextMenu, IContextMenu3), // IID_IContextMenu
  451. { 0 }
  452. };
  453. return QISearch(this, qit, riid, ppvObj);
  454. }
  455. STDMETHODIMP_(ULONG) CTargetMenu::AddRef()
  456. {
  457. return InterlockedIncrement(&_cRef);
  458. }
  459. STDMETHODIMP_(ULONG) CTargetMenu::Release()
  460. {
  461. if (InterlockedDecrement(&_cRef))
  462. return _cRef;
  463. delete this;
  464. return 0;
  465. }
  466. STDMETHODIMP CTargetMenu::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
  467. {
  468. IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
  469. return S_OK;
  470. }
  471. HRESULT CTargetMenu::GetTargetMenu()
  472. {
  473. HRESULT hr = E_FAIL;
  474. STGMEDIUM medium;
  475. LPIDA pida = DataObj_GetHIDA(_pdtobj, &medium);
  476. if (pida)
  477. {
  478. IShellFolder *psf;
  479. hr = SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, IDA_GetIDListPtr(pida, -1), &psf));
  480. if (SUCCEEDED(hr))
  481. {
  482. IShellLink *psl;
  483. LPCITEMIDLIST pidl = IDA_GetIDListPtr(pida, 0);
  484. hr = psf->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST *)&pidl, IID_X_PPV_ARG(IShellLink, NULL, &psl));
  485. if (SUCCEEDED(hr))
  486. {
  487. ASSERT(NULL == _pidlTarget);
  488. hr = psl->GetIDList(&_pidlTarget);
  489. if (SUCCEEDED(hr) && _pidlTarget)
  490. {
  491. hr = SHGetUIObjectFromFullPIDL(_pidlTarget, NULL, IID_PPV_ARG(IContextMenu, &_pcmTarget));
  492. }
  493. else
  494. hr = E_FAIL;
  495. psl->Release();
  496. }
  497. psf->Release();
  498. }
  499. HIDA_ReleaseStgMedium(pida, &medium);
  500. }
  501. return hr;
  502. }
  503. #define IDS_CONTENTSMENU 3
  504. #define IDS_TARGETMENU 4
  505. #define IDC_OPENCONTAINER 1
  506. #define IDC_TARGET_LAST 1
  507. #define NUM_TARGET_CMDS 0x40
  508. STDMETHODIMP CTargetMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  509. {
  510. if (uFlags & CMF_DEFAULTONLY)
  511. return S_OK;
  512. UINT idMax = idCmdFirst;
  513. _hmenu = CreatePopupMenu();
  514. if (_hmenu)
  515. {
  516. TCHAR szString[80];
  517. MENUITEMINFO mii;
  518. // Add an open container menu item...
  519. lstrcpyn(szString, TEXT("Open Container"), ARRAYSIZE(szString));
  520. mii.cbSize = sizeof(MENUITEMINFO);
  521. mii.fMask = MIIM_ID | MIIM_TYPE;
  522. mii.fType = MFT_STRING;
  523. mii.dwTypeData = szString;
  524. mii.wID = idCmdFirst + IDC_OPENCONTAINER;
  525. if (InsertMenuItem(_hmenu, 0, TRUE, &mii))
  526. {
  527. _idCmdFirst = idCmdFirst;
  528. SetMenuDefaultItem(_hmenu, 0, TRUE);
  529. InsertMenu(hmenu, 1, MF_BYPOSITION | MF_SEPARATOR, (UINT)-1, NULL);
  530. // Insert our context menu....
  531. lstrcpyn(szString, TEXT("Target"), ARRAYSIZE(szString));
  532. mii.cbSize = sizeof(MENUITEMINFO);
  533. mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
  534. mii.fType = MFT_STRING;
  535. mii.dwTypeData = szString;
  536. mii.wID = idCmdFirst;
  537. mii.fState = MF_DISABLED|MF_GRAYED;
  538. mii.fMask = MIIM_TYPE | MIIM_SUBMENU;
  539. mii.hSubMenu = _hmenu;
  540. if (InsertMenuItem(hmenu, indexMenu, TRUE, &mii))
  541. {
  542. idMax += NUM_TARGET_CMDS; // reserve space for this many items
  543. _bFirstTime = TRUE; // fill this at WM_INITMENUPOPUP time
  544. }
  545. else
  546. {
  547. DestroyMenu(_hmenu);
  548. _hmenu = NULL;
  549. }
  550. }
  551. }
  552. return ResultFromShort(idMax - idCmdFirst);
  553. }
  554. STDMETHODIMP CTargetMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
  555. {
  556. UINT idCmd = LOWORD(lpici->lpVerb);
  557. switch (idCmd)
  558. {
  559. case IDC_OPENCONTAINER:
  560. SHOpenFolderAndSelectItems(_pidlTarget, 0, NULL, 0);
  561. break;
  562. default:
  563. CMINVOKECOMMANDINFO ici = {
  564. sizeof(ici),
  565. lpici->fMask,
  566. lpici->hwnd,
  567. (LPCSTR)MAKEINTRESOURCE(idCmd - IDC_OPENCONTAINER),
  568. lpici->lpParameters,
  569. lpici->lpDirectory,
  570. lpici->nShow,
  571. };
  572. return _pcmTarget->InvokeCommand(&ici);
  573. }
  574. return S_OK;
  575. }
  576. HRESULT CTargetMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax)
  577. {
  578. return E_NOTIMPL;
  579. }
  580. STDMETHODIMP CTargetMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
  581. {
  582. return HandleMenuMsg2(uMsg, wParam, lParam, NULL);
  583. }
  584. STDMETHODIMP CTargetMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
  585. {
  586. HRESULT hr = S_OK;
  587. LRESULT lResult = 0;
  588. switch (uMsg)
  589. {
  590. case WM_INITMENUPOPUP:
  591. if (_hmenu == (HMENU)wParam)
  592. {
  593. if (_bFirstTime)
  594. {
  595. _bFirstTime = FALSE;
  596. if (SUCCEEDED(GetTargetMenu()))
  597. {
  598. _pcmTarget->QueryContextMenu(_hmenu, IDC_TARGET_LAST,
  599. _idCmdFirst + IDC_TARGET_LAST,
  600. _idCmdFirst - IDC_TARGET_LAST + NUM_TARGET_CMDS, CMF_NOVERBS);
  601. }
  602. }
  603. break;
  604. }
  605. // fall through... to pass on sub menu WM_INITMENUPOPUPs
  606. case WM_DRAWITEM:
  607. case WM_MEASUREITEM:
  608. hr = SHForwardContextMenuMsg(_pcmTarget, uMsg, wParam, lParam, &lResult, TRUE);
  609. break;
  610. default:
  611. hr = E_FAIL;
  612. break;
  613. }
  614. if (plResult)
  615. *plResult = lResult;
  616. return hr;
  617. }
  618. HRESULT CTargetMenu_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvOut)
  619. {
  620. HRESULT hr = E_OUTOFMEMORY;
  621. CTargetMenu *ptm = new CTargetMenu();
  622. if (ptm)
  623. {
  624. hr = ptm->QueryInterface(riid, ppvOut);
  625. ptm->Release();
  626. }
  627. return hr;
  628. }
  629. #endif // TARGETMENU
  630. #ifdef CONTENT
  631. #define IDC_ITEMFIRST (IDC_PRESSMOD + 10)
  632. #define MENU_TIMEOUT (1000)
  633. #define DPA_LAST 0x7fffffff
  634. class CHIDA
  635. {
  636. public:
  637. CHIDA(HGLOBAL hIDA, IUnknown *punk)
  638. {
  639. _punk = punk;
  640. _hIDA = hIDA;
  641. _pIDA = (LPIDA)GlobalLock(hIDA);
  642. }
  643. ~CHIDA()
  644. {
  645. GlobalUnlock(_hObj);
  646. if (_punk)
  647. _punk->Release();
  648. else
  649. GlobalFree(_hIDA);
  650. }
  651. LPCITEMIDLIST operator [](UINT nIndex)
  652. {
  653. if (nIndex > _pIDA->cidl)
  654. return(NULL);
  655. return (LPCITEMIDLIST)(((BYTE *)_pIDA) + _pIDA->aoffset[nIndex]);
  656. }
  657. private:
  658. HGLOBAL _hIDA;
  659. LPIDA _pIDA;
  660. IUnknown *_punk;
  661. };
  662. class CVoidArray
  663. {
  664. public:
  665. CVoidArray() : _dpa(NULL), _dsa(NULL) {}
  666. ~CVoidArray()
  667. {
  668. if (_dpa)
  669. {
  670. DPA_Destroy(_dpa);
  671. }
  672. if (_dsa)
  673. {
  674. DSA_Destroy(_dsa);
  675. }
  676. }
  677. void *operator[](int i);
  678. BOOL Init(UINT uSize, UINT uJump);
  679. BOOL Add(void *pv);
  680. void Sort(PFNDPACOMPARE pfnCompare, LPARAM lParam)
  681. {
  682. _pfnCompare = pfnCompare;
  683. _lParam = lParam;
  684. DPA_Sort(_dpa, ArrayCompare, (LPARAM)this);
  685. }
  686. private:
  687. static int CALLBACK ArrayCompare(void *pv1, void *pv2, LPARAM lParam);
  688. HDPA _dpa;
  689. HDSA _dsa;
  690. PFNDPACOMPARE _pfnCompare;
  691. LPARAM void *;
  692. };
  693. _lParam CVoidArray::operator[](int i)
  694. {
  695. if (!_dpa || i>=DPA_GetPtrCount(_dpa))
  696. {
  697. return(NULL);
  698. }
  699. return(DSA_GetItemPtr(_dsa, (int)DPA_GetPtr(_dpa, i)));
  700. }
  701. BOOL CVoidArray::Init(UINT uSize, UINT uJump)
  702. {
  703. _dpa = DPA_Create(uJump);
  704. _dsa = DSA_Create(uSize, uJump);
  705. return(_dpa && _dsa);
  706. }
  707. BOOL CVoidArray::Add(void *pv)
  708. {
  709. int iItem = DSA_InsertItem(_dsa, DPA_LAST, pv);
  710. if (iItem < 0)
  711. {
  712. return(FALSE);
  713. }
  714. if (DPA_InsertPtr(_dpa, DPA_LAST, (void *)iItem) < 0)
  715. {
  716. DSA_DeleteItem(_dsa, iItem);
  717. return(FALSE);
  718. }
  719. return(TRUE);
  720. }
  721. int CALLBACK CVoidArray::ArrayCompare(void *pv1, void *pv2, LPARAM lParam)
  722. {
  723. CVoidArray *pThis = (CVoidArray *)lParam;
  724. return(pThis->_pfnCompare(DSA_GetItemPtr(pThis->_dsa, (int)pv1),
  725. DSA_GetItemPtr(pThis->_dsa, (int)pv2), pThis->_lParam));
  726. }
  727. class CContentItemData
  728. {
  729. public:
  730. CContentItemData() : _dwDummy(0) {Empty();}
  731. ~CContentItemData() {Free();}
  732. void Free();
  733. void Empty() {_pidl=NULL; _hbm=NULL;}
  734. // Here to work around a Tray menu bug
  735. DWORD _dwDummy;
  736. LPITEMIDLIST _pidl;
  737. HBITMAP _hbm;
  738. };
  739. void CContentItemData::Free()
  740. {
  741. if (_pidl) ILFree(_pidl);
  742. if (_hbm) DeleteObject(_hbm);
  743. Empty();
  744. }
  745. class CContentItemDataArray : public CVoidArray
  746. {
  747. public:
  748. CContentItemDataArray(LPCITEMIDLIST pidlFolder) : _pidlFolder(pidlFolder) {}
  749. ~CContentItemDataArray();
  750. CContentItemData * operator[](int i) {return((CContentItemData*)(*(CVoidArray*)this)[i]);}
  751. HRESULT Init();
  752. BOOL Add(CContentItemData *pv) {return(CVoidArray::Add((void *)pv));}
  753. private:
  754. static int CALLBACK DefaultSort(void *pv1, void *pv2, LPARAM lParam);
  755. HRESULT GetShellFolder(IShellFolder **ppsf);
  756. BOOL IsLocal();
  757. LPCITEMIDLIST CContentItemDataArray;
  758. };
  759. CContentItemDataArray::~CContentItemDataArray()
  760. {
  761. for (int i=0;; ++i)
  762. {
  763. CContentItemData *pID = (*this)[i];
  764. if (!pID)
  765. break;
  766. pID->Free();
  767. }
  768. }
  769. int CALLBACK CContentItemDataArray::DefaultSort(void *pv1, void *pv2, LPARAM lParam)
  770. {
  771. IShellFolder *psfFolder = (IShellFolder *)lParam;
  772. CContentItemData *pID1 = (CContentItemData *)pv1;
  773. CContentItemData *pID2 = (CContentItemData *)pv2;
  774. HRESULT hRes = psfFolder->CompareIDs(0, pID1->_pidl, pID2->_pidl);
  775. if (FAILED(hRes))
  776. {
  777. return(0);
  778. }
  779. return((short)ShortFromResult(hRes));
  780. }
  781. HRESULT CContentItemDataArray::GetShellFolder(IShellFolder **ppsf)
  782. {
  783. IShellFolder *psfDesktop;
  784. HRESULT hRes = CoCreateInstance(CLSID_ShellDesktop, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellFolder, &psfDesktop));
  785. if (FAILED(hRes))
  786. {
  787. return hRes;
  788. }
  789. CEnsureRelease erDesktop(psfDesktop);
  790. return psfDesktop->BindToObject(_pidlFolder, NULL, IID_PPV_ARG(IShellFolder, ppsf));
  791. }
  792. BOOL CContentItemDataArray::IsLocal()
  793. {
  794. TCHAR szPath[MAX_PATH];
  795. if (!SHGetPathFromIDList(_pidlFolder, szPath))
  796. {
  797. return(FALSE);
  798. }
  799. CharUpper(szPath);
  800. return(DriveType(szPath[0]-'A') == DRIVE_FIXED);
  801. }
  802. HRESULT CContentItemDataArray::Init()
  803. {
  804. if (!IsLocal() && !(GetKeyState(VK_SHIFT)&0x8000))
  805. {
  806. return(S_FALSE);
  807. }
  808. if (!CVoidArray::Init(sizeof(CContentItemData), 16))
  809. {
  810. return(E_OUTOFMEMORY);
  811. }
  812. IShellFolder *psfFolder;
  813. HRESULT hRes = GetShellFolder(&psfFolder);
  814. if (FAILED(hRes))
  815. {
  816. return(hRes);
  817. }
  818. CEnsureRelease erFolder(psfFolder);
  819. IEnumIDList *penumFolder;
  820. hRes = psfFolder->EnumObjects(NULL,
  821. SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN, &penumFolder);
  822. if (S_OK != hRes)
  823. {
  824. return FAILED(hRes) ? hRes : E_FAIL; //in case of S_FALSE enumerator is NULL so nothing will be in the menu, just return failure
  825. }
  826. CEnsureRelease erEnumFolder(penumFolder);
  827. ULONG cNum;
  828. DWORD dwStart = 0;
  829. hRes = S_OK;
  830. for (;;)
  831. {
  832. CContentItemData cID;
  833. if (penumFolder->Next(1, &cID._pidl, &cNum)!=S_OK || cNum!=1)
  834. {
  835. // Just in case
  836. cID.Empty();
  837. break;
  838. }
  839. if (!dwStart)
  840. {
  841. dwStart = GetTickCount();
  842. }
  843. else if (!(GetAsyncKeyState(VK_SHIFT)&0x8000)
  844. && GetTickCount()-dwStart>MENU_TIMEOUT)
  845. {
  846. // Only go for 2 seconds after the first Next call
  847. hRes = S_FALSE;
  848. break;
  849. }
  850. CMenuDraw mdItem(_pidlFolder, cID._pidl);
  851. cID._hbm = mdItem.CreateBitmap(TRUE);
  852. if (!cID._hbm)
  853. {
  854. continue;
  855. }
  856. if (!Add(&cID))
  857. {
  858. break;
  859. }
  860. // Like a Detach(); Make sure we do not free stuff
  861. cID.Empty();
  862. }
  863. Sort(DefaultSort, (LPARAM)psfFolder);
  864. return(hRes);
  865. }
  866. class CContentItemInfo : public tagMENUITEMINFOA
  867. {
  868. public:
  869. CContentItemInfo(UINT fMsk) {fMask=fMsk; cbSize=sizeof(MENUITEMINFO);}
  870. ~CContentItemInfo() {}
  871. BOOL GetMenuItemInfo(HMENU hm, int nID, BOOL bByPos)
  872. {
  873. return(::GetMenuItemInfo(hm, nID, bByPos, this));
  874. }
  875. CContentItemData *GetItemData() {return((CContentItemData*)dwItemData);}
  876. void SetItemData(CContentItemData *pd) {dwItemData=(DWORD)pd; fMask|=MIIM_DATA;}
  877. HBITMAP GetBitmap() {return(fType&MFT_BITMAP ? dwTypeData : NULL);}
  878. void SetBitmap(HBITMAP hb) {dwTypeData=(LPSTR)hb; fType|=MFT_BITMAP;}
  879. };
  880. #define CXIMAGEGAP 6
  881. #define CYIMAGEGAP 4
  882. class CWindowDC
  883. {
  884. public:
  885. CWindowDC(HWND hWnd) : _hWnd(hWnd) {_hDC=GetDC(hWnd);}
  886. ~CWindowDC() {ReleaseDC(_hWnd, _hDC);}
  887. operator HDC() {return(_hDC);}
  888. private:
  889. HDC _hDC;
  890. HWND _hWnd;
  891. };
  892. class CDCTemp
  893. {
  894. public:
  895. CDCTemp(HDC hDC) : _hDC(hDC) {}
  896. ~CDCTemp() {if (_hDC) DeleteDC(_hDC);}
  897. operator HDC() {return(_hDC);}
  898. private:
  899. HDC _hDC;
  900. };
  901. class CRefMenuFont
  902. {
  903. public:
  904. CRefMenuFont(CMenuDraw *pmd) {_pmd = pmd->InitMenuFont() ? pmd : NULL;}
  905. ~CRefMenuFont() {if (_pmd) _pmd->ReleaseMenuFont();}
  906. operator BOOL() {return(_pmd != NULL);}
  907. private:
  908. CMenuDraw *_pmd;
  909. };
  910. BOOL CMenuDraw::InitMenuFont()
  911. {
  912. if (_cRefFont.GetRef())
  913. {
  914. _cRefFont.AddRef();
  915. return(TRUE);
  916. }
  917. NONCLIENTMETRICS ncm;
  918. ncm.cbSize = sizeof(ncm);
  919. SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm),
  920. (void far *)(LPNONCLIENTMETRICS)&ncm, FALSE);
  921. _hfMenu = CreateFontIndirect(&ncm.lfMenuFont);
  922. if (_hfMenu)
  923. {
  924. _cRefFont.AddRef();
  925. return TRUE;
  926. }
  927. return FALSE;
  928. }
  929. void CMenuDraw::ReleaseMenuFont()
  930. {
  931. if (_cRefFont.Release())
  932. {
  933. return;
  934. }
  935. DeleteObject(_hfMenu);
  936. _hfMenu = NULL;
  937. }
  938. BOOL CMenuDraw::InitStringAndIcon()
  939. {
  940. if (!_pidlAbs)
  941. {
  942. return(FALSE);
  943. }
  944. if (_pszString)
  945. {
  946. return(TRUE);
  947. }
  948. SHFILEINFO sfi;
  949. if (!SHGetFileInfo((LPCSTR)_pidlAbs, 0, &sfi, sizeof(sfi),
  950. SHGFI_DISPLAYNAME | SHGFI_ICON | SHGFI_SMALLICON | SHGFI_PIDL))
  951. {
  952. return(FALSE);
  953. }
  954. if (!Str_SetPtr(&_pszString, sfi.szDisplayName))
  955. {
  956. DestroyIcon(sfi.hIcon);
  957. }
  958. _hiItem = sfi.hIcon;
  959. return(TRUE);
  960. }
  961. BOOL CMenuDraw::GetName(LPSTR szName)
  962. {
  963. if (!InitStringAndIcon())
  964. {
  965. return(FALSE);
  966. }
  967. lstrcpyn(szName, _pszString, MAX_PATH);
  968. return(TRUE);
  969. }
  970. BOOL CMenuDraw::GetExtent(SIZE *pSize, BOOL bFull)
  971. {
  972. if (!InitStringAndIcon())
  973. {
  974. return(FALSE);
  975. }
  976. CRefMenuFont crFont(this);
  977. if (!(BOOL)crFont)
  978. {
  979. return(FALSE);
  980. }
  981. HDC hDC = GetDC(NULL);
  982. HFONT hfOld = (HFONT)SelectObject(hDC, _hfMenu);
  983. BOOL bRet = GetTextExtentPoint32(hDC, _pszString, lstrlen(_pszString), pSize);
  984. if (hfOld)
  985. {
  986. SelectObject(hDC, hfOld);
  987. }
  988. ReleaseDC(NULL, hDC);
  989. if (bRet)
  990. {
  991. int cxIcon = GetSystemMetrics(SM_CXSMICON);
  992. int cyIcon = GetSystemMetrics(SM_CYSMICON);
  993. pSize->cy = max(pSize->cy, cyIcon);
  994. if (bFull)
  995. {
  996. pSize->cx += CXIMAGEGAP + GetSystemMetrics(SM_CXSMICON) + CXIMAGEGAP + CXIMAGEGAP;
  997. pSize->cy += CYIMAGEGAP;
  998. }
  999. else
  1000. {
  1001. pSize->cx = cxIcon;
  1002. }
  1003. }
  1004. return(bRet);
  1005. }
  1006. BOOL CMenuDraw::DrawItem(HDC hDC, RECT *prc, BOOL bFull)
  1007. {
  1008. RECT rc = *prc;
  1009. int cxIcon = GetSystemMetrics(SM_CXSMICON);
  1010. int cyIcon = GetSystemMetrics(SM_CYSMICON);
  1011. FillRect(hDC, prc, GetSysColorBrush(COLOR_MENU));
  1012. if (!InitStringAndIcon())
  1013. {
  1014. return(FALSE);
  1015. }
  1016. if (bFull)
  1017. {
  1018. rc.left += CXIMAGEGAP;
  1019. }
  1020. DrawIconEx(hDC, rc.left, rc.top + (rc.bottom-rc.top-cyIcon)/2, _hiItem,
  1021. 0, 0, 0, NULL, DI_NORMAL);
  1022. if (!bFull)
  1023. {
  1024. // All done
  1025. return(TRUE);
  1026. }
  1027. CRefMenuFont crFont(this);
  1028. if (!(BOOL)crFont)
  1029. {
  1030. return(FALSE);
  1031. }
  1032. rc.left += cxIcon + CXIMAGEGAP;
  1033. HFONT hfOld = SelectObject(hDC, _hfMenu);
  1034. COLORREF crOldBk = SetBkColor (hDC, GetSysColor(COLOR_MENU));
  1035. COLORREF crOldTx = SetTextColor(hDC, GetSysColor(COLOR_MENUTEXT));
  1036. DrawText(hDC, _pszString, -1, &rc,
  1037. DT_VCENTER|DT_SINGLELINE|DT_LEFT|DT_NOCLIP);
  1038. SetBkColor (hDC, GetSysColor(crOldBk));
  1039. SetTextColor(hDC, GetSysColor(crOldTx));
  1040. if (hfOld)
  1041. {
  1042. SelectObject(hDC, hfOld);
  1043. }
  1044. return(TRUE);
  1045. }
  1046. HBITMAP CMenuDraw::CreateBitmap(BOOL bFull)
  1047. {
  1048. SIZE size;
  1049. // Reference font here so we do not create it twice
  1050. CRefMenuFont crFont(this);
  1051. if (!(BOOL)crFont)
  1052. {
  1053. return(FALSE);
  1054. }
  1055. if (!GetExtent(&size, bFull))
  1056. {
  1057. return(NULL);
  1058. }
  1059. CWindowDC wdcScreen(NULL);
  1060. CDCTemp cdcTemp(CreateCompatibleDC(wdcScreen));
  1061. if (!(HDC)cdcTemp)
  1062. {
  1063. return(NULL);
  1064. }
  1065. HBITMAP hbmItem = CreateCompatibleBitmap(wdcScreen, size.cx, size.cy);
  1066. if (!hbmItem)
  1067. {
  1068. return(NULL);
  1069. }
  1070. HBITMAP hbmOld = (HBITMAP)SelectObject(cdcTemp, hbmItem);
  1071. RECT rc = { 0, 0, size.cx, size.cy };
  1072. BOOL bDrawn = DrawItem(cdcTemp, &rc, bFull);
  1073. SelectObject(cdcTemp, hbmOld);
  1074. if (!bDrawn)
  1075. {
  1076. DeleteObject(hbmItem);
  1077. hbmItem = NULL;
  1078. }
  1079. return(hbmItem);
  1080. }
  1081. class CContentMenu : public IShellExtInit, public IContextMenu2
  1082. {
  1083. CContentMenu();
  1084. ~CContentMenu();
  1085. // IUnknown
  1086. STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
  1087. STDMETHOD_(ULONG,AddRef)(void);
  1088. STDMETHOD_(ULONG,Release)(void);
  1089. // IContextMenu
  1090. STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  1091. STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpici);
  1092. STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax);
  1093. // IContextMenu2
  1094. STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam);
  1095. // IShellExtInit
  1096. STDMETHOD(Initialize)(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);
  1097. static HMENU LoadPopupMenu(UINT id, UINT uSubMenu);
  1098. HRESULT InitMenu();
  1099. LPITEMIDLIST _pidlFolder;
  1100. HMENU _hmItems;
  1101. friend STDAPI CContentMenu_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi);
  1102. class CContentItemDataArray *_pIDs;
  1103. };
  1104. CContentMenu::CContentMenu() : _pidlFolder(0), _hmItems(NULL), _pIDs(NULL)
  1105. {
  1106. }
  1107. CContentMenu::~CContentMenu()
  1108. {
  1109. if (_pidlFolder)
  1110. ILFree(_pidlFolder);
  1111. if (_hmItems)
  1112. DestroyMenu(_hmItems);
  1113. if (_pIDs)
  1114. delete _pIDs;
  1115. }
  1116. HRESULT CContentMenu::QueryInterface(REFIID riid, void **ppvObj)
  1117. {
  1118. static const QITAB qit[] = {
  1119. QITABENT(CContentMenu, IShellExtInit), // IID_IShellExtInit
  1120. QITABENT(CContentMenu, IContextMenu3), // IID_IContextMenu3
  1121. QITABENTMULTI(CContentMenu, IContextMenu2, IContextMenu3), // IID_IContextMenu2
  1122. QITABENTMULTI(CSendToMenu, IContextMenu, IContextMenu3), // IID_IContextMenu
  1123. { 0 }
  1124. };
  1125. return QISearch(this, qit, riid, ppvObj);
  1126. }
  1127. ULONG CContentMenu::AddRef()
  1128. {
  1129. return InterlockedIncrement(&_cRef);
  1130. }
  1131. ULONG CContentMenu::Release()
  1132. {
  1133. if (InterlockedDecrement(&_cRef))
  1134. return _cRef;
  1135. delete this;
  1136. return 0;
  1137. }
  1138. STDMETHODIMP CContentMenu::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
  1139. {
  1140. STGMEDIUM medium;
  1141. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  1142. if (pida)
  1143. {
  1144. _pidlFolder = IDA_FullIDList(pida, 0);
  1145. HIDA_ReleaseStgMedium(pida, &medium);
  1146. }
  1147. return _pidlFolder ? S_OK : E_OUTOFMEMORY;
  1148. }
  1149. STDMETHODIMP CContentMenu::QueryContextMenu(HMENU hmenu,
  1150. UINT indexMenu,
  1151. UINT idCmdFirst,
  1152. UINT idCmdLast,
  1153. UINT uFlags)
  1154. {
  1155. if (uFlags & CMF_DEFAULTONLY)
  1156. return S_OK;
  1157. HRESULE hr = E_OUTOFMEMORY;
  1158. HMENU hmenuSub = CreatePopupMenu();
  1159. if (hmenuSub)
  1160. {
  1161. char szTitle[80];
  1162. if (LoadString(g_hinst, IDS_CONTENTSMENU, szTitle, sizeof(szTitle)))
  1163. {
  1164. MENUITEMINFO mii = {0};
  1165. mii.cbSize = sizeof(MENUITEMINFO);
  1166. mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
  1167. mii.fType = MFT_STRING;
  1168. mii.dwTypeData = szTitle;
  1169. mii.wID = idCmdFirst + IDC_PRESSMOD;
  1170. mii.fState = MF_DISABLED|MF_GRAYED;
  1171. UINT idMax = mii.wID + 1;
  1172. if (SUCCEEDED(InitMenu()))
  1173. {
  1174. UINT idM = Shell_MergeMenus(hmenuSub, _hmItems, 0, idCmdFirst, idCmdLast, 0);
  1175. if (GetMenuItemCount(hmenuSub) > 0)
  1176. {
  1177. mii.fMask = MIIM_TYPE | MIIM_SUBMENU;
  1178. mii.hSubMenu = hmenuSub;
  1179. idMax = idM;
  1180. }
  1181. }
  1182. if (InsertMenuItem(hmenu, indexMenu, TRUE, &mii))
  1183. {
  1184. if (mii.fMask & MIIM_SUBMENU)
  1185. {
  1186. }
  1187. hr = ResultFromShort(idMax - idCmdFirst);
  1188. }
  1189. }
  1190. if (FAILED(hr))
  1191. DestroyMenu(hmenuSub);
  1192. }
  1193. return hr;
  1194. }
  1195. STDMETHODIMP CContentMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
  1196. {
  1197. if (HIWORD(lpici->lpVerb))
  1198. {
  1199. // Deal with string commands
  1200. return E_INVALIDARG;
  1201. }
  1202. UINT uID = (UINT)LOWORD((DWORD)lpici->lpVerb);
  1203. switch (uID)
  1204. {
  1205. case IDC_PRESSMOD:
  1206. ShellMessageBox(g_hinst, lpici->hwnd, MAKEINTRESOURCE(IDS_PRESSMOD),
  1207. MAKEINTRESOURCE(IDS_THISDLL), MB_OK|MB_ICONINFORMATION);
  1208. break;
  1209. case IDC_SHIFTMORE:
  1210. ShellMessageBox(g_hinst, lpici->hwnd, MAKEINTRESOURCE(IDS_SHIFTMORE),
  1211. MAKEINTRESOURCE(IDS_THISDLL), MB_OK|MB_ICONINFORMATION);
  1212. break;
  1213. default:
  1214. if (_hmItems)
  1215. {
  1216. CContentItemInfo mii(MIIM_DATA);
  1217. if (!mii.GetMenuItemInfo(_hmItems, uID, FALSE))
  1218. return E_INVALIDARG;
  1219. CContentItemData *pData = mii.GetItemData();
  1220. if (!pData || !pData->_pidl)
  1221. return E_INVALIDARG;
  1222. LPITEMIDLIST pidlAbs = ILCombine(_pidlFolder, pData->_pidl);
  1223. if (!pidlAbs)
  1224. return E_OUTOFMEMORY;
  1225. SHELLEXECUTEINFO sei;
  1226. sei.cbSize = sizeof(sei);
  1227. sei.fMask = SEE_MASK_INVOKEIDLIST;
  1228. sei.lpVerb = NULL;
  1229. sei.hwnd = lpici->hwnd;
  1230. sei.lpParameters = lpici->lpParameters;
  1231. sei.lpDirectory = lpici->lpDirectory;
  1232. sei.nShow = lpici->nShow;
  1233. sei.lpIDList = (void *)pidlAbs;
  1234. ShellExecuteEx(&sei);
  1235. ILFree(pidlAbs);
  1236. break;
  1237. }
  1238. return(E_UNEXPECTED);
  1239. }
  1240. return S_OK;
  1241. }
  1242. STDMETHODIMP CContentMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax)
  1243. {
  1244. return E_NOTIMPL;
  1245. }
  1246. STDMETHODIMP CContentMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
  1247. {
  1248. return E_NOTIMPL;
  1249. }
  1250. HRESULT CContentMenu::InitMenu()
  1251. {
  1252. if (!_pidlFolder)
  1253. return E_UNEXPECTED;
  1254. if (_hmItems)
  1255. return S_OK;
  1256. if (_pIDs)
  1257. return E_UNEXPECTED;
  1258. _pIDs = new CContentItemDataArray(_pidlFolder);
  1259. if (!_pIDs)
  1260. return E_OUTOFMEMORY;
  1261. HRESULT hRes = _pIDs->Init();
  1262. if (FAILED(hRes))
  1263. return hRes;
  1264. BOOL bGotAll = (hRes == S_OK);
  1265. _hmItems = CreatePopupMenu();
  1266. if (!_hmItems)
  1267. return E_OUTOFMEMORY;
  1268. UINT cy = 0;
  1269. BOOL bBitmaps = TRUE;
  1270. if (!bGotAll)
  1271. {
  1272. HMENU hmenuMerge = LoadPopupMenu(MENU_ITEMCONTEXT, 1);
  1273. if (hmenuMerge)
  1274. {
  1275. Shell_MergeMenus(_hmItems, hmenuMerge, 0, 0, 1000, 0);
  1276. DestroyMenu(hmenuMerge);
  1277. }
  1278. else
  1279. return E_OUTOFMEMORY;
  1280. }
  1281. for (int i=0;; ++i)
  1282. {
  1283. CContentItemData * pID = (*_pIDs)[i];
  1284. if (!pID)
  1285. {
  1286. // All done
  1287. break;
  1288. }
  1289. UINT id = IDC_ITEMFIRST + i;
  1290. BITMAP bm;
  1291. GetObject(pID->_hbm, sizeof(bm), &bm);
  1292. cy += bm.bmHeight;
  1293. if (i==0 && !bGotAll)
  1294. {
  1295. // Account for the menu item we added above
  1296. cy += cy;
  1297. }
  1298. CContentItemInfo mii(MIIM_ID | MIIM_TYPE | MIIM_DATA);
  1299. mii.fType = 0;
  1300. mii.SetItemData(pID);
  1301. mii.wID = id;
  1302. if (cy >= (UINT)GetSystemMetrics(SM_CYSCREEN)*4/5)
  1303. {
  1304. // Put in a menu break when we fill 80% the screen
  1305. mii.fType |= MFT_MENUBARBREAK;
  1306. cy = bm.bmHeight;
  1307. // bBitmaps = FALSE;
  1308. }
  1309. mii.SetBitmap(pID->_hbm);
  1310. if (!InsertMenuItem(_hmItems, DPA_LAST, TRUE, &mii))
  1311. {
  1312. return(E_OUTOFMEMORY);
  1313. }
  1314. }
  1315. return(_hmItems ? S_OK: HMENU);
  1316. }
  1317. HMENU CContentMenu::LoadPopupMenu(UINT id, UINT uSubMenu)
  1318. {
  1319. HMENU hmParent = LoadMenu(g_hinst, MAKEINTRESOURCE(id));
  1320. if (!hmParent)
  1321. return(NULL);
  1322. HMENU hmPopup = GetSubMenu(hmParent, uSubMenu);
  1323. RemoveMenu(hmParent, uSubMenu, MF_BYPOSITION);
  1324. DestroyMenu(hmParent);
  1325. return(hmPopup);
  1326. }
  1327. // 57D5ECC0-A23F-11CE-AE65-08002B2E1262
  1328. DEFINE_GUID(CLSID_ContentMenu, 0x57D5ECC0L, 0xA23F, 0x11CE, 0xAE, 0x65, 0x08, 0x00, 0x2B, 0x2E, 0x12, 0x62);
  1329. STDAPI CContentMenu_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
  1330. {
  1331. // aggregation checking is handled in class factory
  1332. CContentMenu *pmenu = new CContentMenu();
  1333. if (pmenu)
  1334. {
  1335. *ppunk = SAFECAST(pmenu, IContextMenu2 *);
  1336. return S_OK;
  1337. }
  1338. return E_OUTOFMEMORY;
  1339. }
  1340. #endif // CONTENT
  1341. #define CXIMAGEGAP 6
  1342. //
  1343. //This is included by shell32/shellprv.h I'm not sure where this is in shdocvw
  1344. #define CCH_KEYMAX 64
  1345. typedef struct
  1346. {
  1347. // Accessibility info must be first
  1348. MSAAMENUINFO msaa;
  1349. TCHAR chPrefix;
  1350. TCHAR szMenuText[CCH_KEYMAX];
  1351. TCHAR szExt[MAX_PATH];
  1352. TCHAR szClass[CCH_KEYMAX];
  1353. DWORD dwFlags;
  1354. int iImage;
  1355. TCHAR szUserFile[CCH_KEYMAX];
  1356. } NEWOBJECTINFO;
  1357. typedef struct
  1358. {
  1359. int type;
  1360. void *lpData;
  1361. DWORD cbData;
  1362. HKEY hkeyNew;
  1363. } NEWFILEINFO;
  1364. typedef struct
  1365. {
  1366. ULONG cbStruct;
  1367. ULONG ver;
  1368. SYSTEMTIME lastupdate;
  1369. } SHELLNEW_CACHE_STAMP;
  1370. // ShellNew config flags
  1371. #define SNCF_DEFAULT 0x0000
  1372. #define SNCF_NOEXT 0x0001
  1373. #define SNCF_USERFILES 0x0002
  1374. #define NEWTYPE_DATA 0x0003
  1375. #define NEWTYPE_FILE 0x0004
  1376. #define NEWTYPE_NULL 0x0005
  1377. #define NEWTYPE_COMMAND 0x0006
  1378. #define NEWTYPE_FOLDER 0x0007
  1379. #define NEWTYPE_LINK 0x0008
  1380. #define NEWITEM_FOLDER 0
  1381. #define NEWITEM_LINK 1
  1382. #define NEWITEM_MAX 2
  1383. class CNewMenu : public CObjectWithSite,
  1384. public IContextMenu3,
  1385. public IShellExtInit
  1386. {
  1387. // IUnknown
  1388. STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
  1389. STDMETHOD_(ULONG,AddRef)(void);
  1390. STDMETHOD_(ULONG,Release)(void);
  1391. // IContextMenu
  1392. STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  1393. STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpici);
  1394. STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax);
  1395. // IContextMenu2
  1396. STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam);
  1397. // IContextMenu3
  1398. STDMETHOD(HandleMenuMsg2)(UINT uMsg, WPARAM wParam, LPARAM lParam,LRESULT *lResult);
  1399. // IShellExtInit
  1400. STDMETHOD(Initialize)(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);
  1401. LONG _cRef;
  1402. HMENU _hmenu;
  1403. UINT _idCmdFirst;
  1404. HIMAGELIST _himlSystemImageList;
  1405. IDataObject *_pdtobj;
  1406. LPITEMIDLIST _pidlFolder;
  1407. POINT _ptNewItem; // from the view, point of click
  1408. NEWOBJECTINFO *_pnoiLast;
  1409. HDPA _hdpaMenuInfo;
  1410. CNewMenu();
  1411. ~CNewMenu();
  1412. friend HRESULT CNewMenu_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvOut);
  1413. private:
  1414. //Handle Menu messages submitted to HandleMenuMsg
  1415. BOOL DrawItem(DRAWITEMSTRUCT *lpdi);
  1416. LRESULT MeasureItem(MEASUREITEMSTRUCT *pmi);
  1417. BOOL InitMenuPopup(HMENU hMenu);
  1418. //Internal Helpers
  1419. NEWOBJECTINFO *GetItemData(HMENU hmenu, UINT iItem);
  1420. HRESULT RunCommand(HWND hwnd, LPCTSTR pszPath, LPCTSTR pszRun);
  1421. HRESULT CopyTemplate(IStream *pStream, NEWFILEINFO *pnfi);
  1422. // Generates it from the Fragment and _pidlFolder
  1423. BOOL _GeneratePidlFromName(LPTSTR pszName, LPITEMIDLIST* ppidl);
  1424. HRESULT _GetItemName(IUnknown *punkFolder, LPWSTR pszItemName, LPWSTR pszPath, UINT cchPath);
  1425. HRESULT _MatchMenuItem(TCHAR ch, LRESULT* plRes);
  1426. BOOL _InsertNewMenuItem(HMENU hmenu, UINT idCmd, NEWOBJECTINFO *pnoiClone);
  1427. HRESULT ConsolidateMenuItems(BOOL bForce);
  1428. HANDLE _hMutex, _hEvent;
  1429. };
  1430. void GetConfigFlags(HKEY hkey, DWORD * pdwFlags)
  1431. {
  1432. TCHAR szTemp[MAX_PATH];
  1433. DWORD cbData = ARRAYSIZE(szTemp);
  1434. *pdwFlags = SNCF_DEFAULT;
  1435. if (SHQueryValueEx(hkey, TEXT("NoExtension"), 0, NULL, (BYTE *)szTemp, &cbData) == ERROR_SUCCESS)
  1436. {
  1437. *pdwFlags |= SNCF_NOEXT;
  1438. }
  1439. }
  1440. BOOL GetNewFileInfoForKey(HKEY hkeyExt, NEWFILEINFO *pnfi, DWORD * pdwFlags)
  1441. {
  1442. BOOL fRet = FALSE;
  1443. HKEY hKey; // this gets the \\.ext\progid key
  1444. HKEY hkeyNew;
  1445. TCHAR szProgID[80];
  1446. LONG lSize = sizeof(szProgID);
  1447. // open the Newcommand
  1448. if (SHRegQueryValue(hkeyExt, NULL, szProgID, &lSize) != ERROR_SUCCESS)
  1449. {
  1450. return FALSE;
  1451. }
  1452. if (RegOpenKey(hkeyExt, szProgID, &hKey) != ERROR_SUCCESS)
  1453. {
  1454. hKey = hkeyExt;
  1455. }
  1456. if (RegOpenKey(hKey, TEXT("ShellNew"), &hkeyNew) == ERROR_SUCCESS)
  1457. {
  1458. DWORD dwType, cbData;
  1459. TCHAR szTemp[MAX_PATH];
  1460. HKEY hkeyConfig;
  1461. // Are there any config flags?
  1462. if (pdwFlags)
  1463. {
  1464. if (RegOpenKey(hkeyNew, TEXT("Config"), &hkeyConfig) == ERROR_SUCCESS)
  1465. {
  1466. GetConfigFlags(hkeyConfig, pdwFlags);
  1467. RegCloseKey(hkeyConfig);
  1468. }
  1469. else
  1470. {
  1471. *pdwFlags = 0;
  1472. }
  1473. }
  1474. if (cbData = sizeof(szTemp), (SHQueryValueEx(hkeyNew, TEXT("FileName"), 0, &dwType, (LPBYTE)szTemp, &cbData) == ERROR_SUCCESS)
  1475. && ((dwType == REG_SZ) || (dwType == REG_EXPAND_SZ)))
  1476. {
  1477. fRet = TRUE;
  1478. if (pnfi)
  1479. {
  1480. pnfi->type = NEWTYPE_FILE;
  1481. pnfi->hkeyNew = hkeyNew; // store this away so we can find out which one held the file easily
  1482. ASSERT((LPTSTR*)pnfi->lpData == NULL);
  1483. pnfi->lpData = StrDup(szTemp);
  1484. hkeyNew = NULL;
  1485. }
  1486. }
  1487. else if (cbData = sizeof(szTemp), (SHQueryValueEx(hkeyNew, TEXT("command"), 0, &dwType, (LPBYTE)szTemp, &cbData) == ERROR_SUCCESS)
  1488. && ((dwType == REG_SZ) || (dwType == REG_EXPAND_SZ)))
  1489. {
  1490. fRet = TRUE;
  1491. if (pnfi)
  1492. {
  1493. pnfi->type = NEWTYPE_COMMAND;
  1494. pnfi->hkeyNew = hkeyNew; // store this away so we can find out which one held the command easily
  1495. ASSERT((LPTSTR*)pnfi->lpData == NULL);
  1496. pnfi->lpData = StrDup(szTemp);
  1497. hkeyNew = NULL;
  1498. }
  1499. }
  1500. else if ((SHQueryValueEx(hkeyNew, TEXT("Data"), 0, &dwType, NULL, &cbData) == ERROR_SUCCESS) && cbData)
  1501. {
  1502. // yes! the data for a new file is stored in the registry
  1503. fRet = TRUE;
  1504. // do they want the data?
  1505. if (pnfi)
  1506. {
  1507. pnfi->type = NEWTYPE_DATA;
  1508. pnfi->cbData = cbData;
  1509. pnfi->lpData = (void*)LocalAlloc(LPTR, cbData);
  1510. if (pnfi->lpData)
  1511. {
  1512. if (dwType == REG_SZ)
  1513. {
  1514. // Get the Unicode data from the registry.
  1515. LPWSTR pszTemp = (LPWSTR)LocalAlloc(LPTR, cbData);
  1516. if (pszTemp)
  1517. {
  1518. SHQueryValueEx(hkeyNew, TEXT("Data"), 0, &dwType, (LPBYTE)pszTemp, &cbData);
  1519. pnfi->cbData = SHUnicodeToAnsi(pszTemp, (LPSTR)pnfi->lpData, cbData);
  1520. if (pnfi->cbData == 0)
  1521. {
  1522. LocalFree(pnfi->lpData);
  1523. pnfi->lpData = NULL;
  1524. }
  1525. LocalFree(pszTemp);
  1526. }
  1527. else
  1528. {
  1529. LocalFree(pnfi->lpData);
  1530. pnfi->lpData = NULL;
  1531. }
  1532. }
  1533. else
  1534. {
  1535. SHQueryValueEx(hkeyNew, TEXT("Data"), 0, &dwType, (BYTE*)pnfi->lpData, &cbData);
  1536. }
  1537. }
  1538. }
  1539. }
  1540. else if (cbData = sizeof(szTemp), (SHQueryValueEx(hkeyNew, TEXT("NullFile"), 0, &dwType, (LPBYTE)szTemp, &cbData) == ERROR_SUCCESS))
  1541. {
  1542. fRet = TRUE;
  1543. if (pnfi)
  1544. {
  1545. pnfi->type = NEWTYPE_NULL;
  1546. pnfi->cbData = 0;
  1547. pnfi->lpData = NULL;
  1548. }
  1549. }
  1550. if (hkeyNew)
  1551. RegCloseKey(hkeyNew);
  1552. }
  1553. if (hKey != hkeyExt)
  1554. {
  1555. RegCloseKey(hKey);
  1556. }
  1557. return fRet;
  1558. }
  1559. BOOL GetNewFileInfoForExtension(NEWOBJECTINFO *pnoi, NEWFILEINFO *pnfi, HKEY* phKey, LPINT piIndex)
  1560. {
  1561. TCHAR szValue[80];
  1562. LONG lSize = sizeof(szValue);
  1563. HKEY hkeyNew;
  1564. BOOL fRet = FALSE;;
  1565. if (phKey && ((*phKey) == (HKEY)-1))
  1566. {
  1567. // we're done
  1568. return FALSE;
  1569. }
  1570. // do the ShellNew key stuff if there's no phKey passed in (which means
  1571. // use the info in pnoi to get THE one) and there's no UserFile specified.
  1572. //
  1573. // if there IS a UserFile specified, then it's a file, and that szUserFile points to it..
  1574. if (!phKey && !pnoi->szUserFile[0] ||
  1575. (phKey && !*phKey))
  1576. {
  1577. // check the new keys under the class id (if any)
  1578. TCHAR szSubKey[128];
  1579. wnsprintf(szSubKey, ARRAYSIZE(szSubKey), TEXT("%s\\CLSID"), pnoi->szClass);
  1580. lSize = sizeof(szValue);
  1581. if (SHRegQueryValue(HKEY_CLASSES_ROOT, szSubKey, szValue, &lSize) == ERROR_SUCCESS)
  1582. {
  1583. wnsprintf(szSubKey, ARRAYSIZE(szSubKey), TEXT("CLSID\\%s"), szValue);
  1584. lSize = sizeof(szValue);
  1585. if (RegOpenKey(HKEY_CLASSES_ROOT, szSubKey, &hkeyNew) == ERROR_SUCCESS)
  1586. {
  1587. fRet = GetNewFileInfoForKey(hkeyNew, pnfi, &pnoi->dwFlags);
  1588. RegCloseKey(hkeyNew);
  1589. }
  1590. }
  1591. // otherwise check under the type extension... do the extension, not the type
  1592. // so that multi-ext to 1 type will work right
  1593. if (!fRet && (RegOpenKey(HKEY_CLASSES_ROOT, pnoi->szExt, &hkeyNew) == ERROR_SUCCESS))
  1594. {
  1595. fRet = GetNewFileInfoForKey(hkeyNew, pnfi, &pnoi->dwFlags);
  1596. RegCloseKey(hkeyNew);
  1597. }
  1598. if (phKey)
  1599. {
  1600. // if we're iterating, then we've got to open the key now...
  1601. wnsprintf(szSubKey, ARRAYSIZE(szSubKey), TEXT("%s\\%s\\ShellNew\\FileName"), pnoi->szExt, pnoi->szClass);
  1602. if (RegOpenKey(HKEY_CLASSES_ROOT, szSubKey, phKey) == ERROR_SUCCESS)
  1603. {
  1604. *piIndex = 0;
  1605. // if we didn't find one of the default ones above,
  1606. // try it now
  1607. // otherwise just return success or failure on fRet
  1608. if (!fRet)
  1609. {
  1610. goto Iterate;
  1611. }
  1612. }
  1613. else
  1614. {
  1615. *phKey = (HKEY)-1;
  1616. }
  1617. }
  1618. }
  1619. else if (!phKey && pnoi->szUserFile[0])
  1620. {
  1621. // there's no key, so just return info about szUserFile
  1622. pnfi->type = NEWTYPE_FILE;
  1623. pnfi->lpData = StrDup(pnoi->szUserFile);
  1624. pnfi->hkeyNew = NULL;
  1625. fRet = TRUE;
  1626. }
  1627. else if (phKey)
  1628. {
  1629. DWORD dwSize;
  1630. DWORD dwData;
  1631. DWORD dwType;
  1632. // we're iterating through...
  1633. Iterate:
  1634. dwSize = ARRAYSIZE(pnoi->szUserFile);
  1635. dwData = ARRAYSIZE(pnoi->szMenuText);
  1636. if (RegEnumValue(*phKey, *piIndex, pnoi->szUserFile, &dwSize, NULL,
  1637. &dwType, (LPBYTE)pnoi->szMenuText, &dwData) == ERROR_SUCCESS)
  1638. {
  1639. (*piIndex)++;
  1640. // if there's something more than the null..
  1641. if (dwData <= 1)
  1642. {
  1643. lstrcpyn(pnoi->szMenuText, PathFindFileName(pnoi->szUserFile), ARRAYSIZE(pnoi->szMenuText));
  1644. PathRemoveExtension(pnoi->szMenuText);
  1645. }
  1646. fRet = TRUE;
  1647. }
  1648. else
  1649. {
  1650. RegCloseKey(*phKey);
  1651. *phKey = (HKEY)-1;
  1652. fRet = FALSE;
  1653. }
  1654. }
  1655. return fRet;
  1656. }
  1657. #define SHELLNEW_CONSOLIDATION_MUTEX TEXT("ShellNewConsolidationMutex")
  1658. #define SHELLNEW_CONSOLIDATION_EVENT TEXT("ShellNewConsolidationEvent")
  1659. CNewMenu::CNewMenu() :
  1660. _cRef(1),
  1661. _hMutex(CreateMutex(NULL, FALSE, SHELLNEW_CONSOLIDATION_MUTEX)),
  1662. _hEvent(CreateEvent(NULL, FALSE, FALSE, SHELLNEW_CONSOLIDATION_EVENT))
  1663. {
  1664. DllAddRef();
  1665. ASSERT(_pnoiLast == NULL);
  1666. }
  1667. CNewMenu::~CNewMenu()
  1668. {
  1669. if (_hdpaMenuInfo)
  1670. {
  1671. // we dont own the lifetime of _hmenu, and it gets destroyed before the destructor
  1672. // is called. thus maintain the lifetime of our NEWOBJECTINFO data in a dpa.
  1673. for (int i = 0; i < DPA_GetPtrCount(_hdpaMenuInfo); i++)
  1674. {
  1675. NEWOBJECTINFO *pNewObjInfo = (NEWOBJECTINFO *)DPA_GetPtr(_hdpaMenuInfo, i);
  1676. LocalFree(pNewObjInfo);
  1677. }
  1678. DPA_Destroy(_hdpaMenuInfo);
  1679. }
  1680. ILFree(_pidlFolder);
  1681. if (_pdtobj)
  1682. _pdtobj->Release();
  1683. if (_hMutex)
  1684. {
  1685. CloseHandle(_hMutex);
  1686. }
  1687. if (_hEvent)
  1688. {
  1689. CloseHandle(_hEvent);
  1690. }
  1691. DllRelease();
  1692. }
  1693. HRESULT CNewMenu_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvOut)
  1694. {
  1695. // aggregation checking is handled in class factory
  1696. *ppvOut = NULL;
  1697. HRESULT hr = E_OUTOFMEMORY;
  1698. CNewMenu * pShellNew = new CNewMenu();
  1699. if (pShellNew)
  1700. {
  1701. if (!pShellNew->_hMutex || !pShellNew->_hEvent)
  1702. {
  1703. hr = E_OUTOFMEMORY;
  1704. }
  1705. else
  1706. {
  1707. hr = pShellNew->QueryInterface(riid, ppvOut);
  1708. }
  1709. pShellNew->Release();
  1710. }
  1711. return hr;
  1712. }
  1713. HRESULT CNewMenu::QueryInterface(REFIID riid, void **ppvObj)
  1714. {
  1715. static const QITAB qit[] = {
  1716. QITABENT(CNewMenu, IShellExtInit), // IID_IShellExtInit
  1717. QITABENT(CNewMenu, IContextMenu3), // IID_IContextMenu3
  1718. QITABENTMULTI(CNewMenu, IContextMenu2, IContextMenu3), // IID_IContextMenu2
  1719. QITABENTMULTI(CNewMenu, IContextMenu, IContextMenu3), // IID_IContextMenu
  1720. QITABENT(CNewMenu, IObjectWithSite), // IID_IObjectWithSite
  1721. { 0 }
  1722. };
  1723. return QISearch(this, qit, riid, ppvObj);
  1724. }
  1725. ULONG CNewMenu::AddRef()
  1726. {
  1727. return InterlockedIncrement(&_cRef);
  1728. }
  1729. ULONG CNewMenu::Release()
  1730. {
  1731. if (InterlockedDecrement(&_cRef))
  1732. return _cRef;
  1733. delete this;
  1734. return 0;
  1735. }
  1736. HRESULT CNewMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  1737. {
  1738. // if they want the default menu only (CMF_DEFAULTONLY) OR
  1739. // this is being called for a shortcut (CMF_VERBSONLY)
  1740. // we don't want to be on the context menu
  1741. MENUITEMINFO mfi = {0};
  1742. if (uFlags & (CMF_DEFAULTONLY | CMF_VERBSONLY))
  1743. return S_OK;
  1744. ConsolidateMenuItems(FALSE);
  1745. _idCmdFirst = idCmdFirst+2;
  1746. TCHAR szNewMenu[80];
  1747. LoadString(g_hinst, IDS_NEWMENU, szNewMenu, ARRAYSIZE(szNewMenu));
  1748. // HACK: I assume that they are querying during a WM_INITMENUPOPUP or equivalent
  1749. GetCursorPos(&_ptNewItem);
  1750. _hmenu = CreatePopupMenu();
  1751. mfi.cbSize = sizeof(MENUITEMINFO);
  1752. mfi.fMask = MIIM_ID | MIIM_TYPE;
  1753. mfi.wID = idCmdFirst+1;
  1754. mfi.fType = MFT_STRING;
  1755. mfi.dwTypeData = szNewMenu;
  1756. InsertMenuItem(_hmenu, 0, TRUE, &mfi);
  1757. ZeroMemory(&mfi, sizeof (mfi));
  1758. mfi.cbSize = sizeof(MENUITEMINFO);
  1759. mfi.fMask = MIIM_ID | MIIM_SUBMENU | MIIM_TYPE | MIIM_DATA;
  1760. mfi.fType = MFT_STRING;
  1761. mfi.wID = idCmdFirst;
  1762. mfi.hSubMenu = _hmenu;
  1763. mfi.dwTypeData = szNewMenu;
  1764. mfi.dwItemData = 0;
  1765. InsertMenuItem(hmenu, indexMenu, TRUE, &mfi);
  1766. _hmenu = NULL;
  1767. return ResultFromShort(_idCmdFirst - idCmdFirst + 1);
  1768. }
  1769. // This is almost the same as ILCreatePidlFromPath, but
  1770. // uses only the filename from the full path pszPath and
  1771. // the _pidlFolder to generate the pidl. This is used because
  1772. // when creating an item in Desktop\My Documents, it used to create a
  1773. // full pidl c:\documents and Settings\lamadio\My Documents\New folder
  1774. // instead of the pidl desktop\my documents\New Folder.
  1775. BOOL CNewMenu::_GeneratePidlFromName(LPTSTR pszFile, LPITEMIDLIST* ppidl)
  1776. {
  1777. *ppidl = NULL; // Out param
  1778. IShellFolder* psf;
  1779. if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, _pidlFolder, &psf))))
  1780. {
  1781. LPITEMIDLIST pidlItem;
  1782. if (SUCCEEDED(psf->ParseDisplayName(NULL, NULL, pszFile, NULL, &pidlItem, NULL)))
  1783. {
  1784. *ppidl = ILCombine(_pidlFolder, pidlItem);
  1785. ILFree(pidlItem);
  1786. }
  1787. psf->Release();
  1788. }
  1789. return BOOLFROMPTR(*ppidl);
  1790. }
  1791. HRESULT CNewMenu::_GetItemName(IUnknown *punkFolder, LPWSTR pszItemName, LPWSTR pszPath, UINT cchPath)
  1792. {
  1793. // we need to pick up the name by asking the folder about the item,
  1794. // not by pathappending to the folder's path.
  1795. IShellFolder *psf;
  1796. HRESULT hr = punkFolder->QueryInterface(IID_PPV_ARG(IShellFolder, &psf));
  1797. if (SUCCEEDED(hr))
  1798. {
  1799. LPITEMIDLIST pidlFile;
  1800. hr = psf->ParseDisplayName(NULL, NULL, pszItemName, NULL, &pidlFile, NULL);
  1801. if (SUCCEEDED(hr))
  1802. {
  1803. hr = DisplayNameOf(psf, pidlFile, SHGDN_FORPARSING, pszPath, cchPath);
  1804. ILFree(pidlFile);
  1805. }
  1806. psf->Release();
  1807. }
  1808. return hr;
  1809. }
  1810. const ICIVERBTOIDMAP c_IDMap[] =
  1811. {
  1812. { L"NewFolder", "NewFolder", NEWITEM_FOLDER, NEWITEM_FOLDER, },
  1813. { L"link", "link", NEWITEM_LINK, NEWITEM_LINK, },
  1814. };
  1815. HRESULT CNewMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
  1816. {
  1817. HRESULT hr = E_FAIL;
  1818. DWORD dwFlags;
  1819. if (IS_INTRESOURCE(pici->lpVerb) && _pnoiLast)
  1820. dwFlags = _pnoiLast->dwFlags;
  1821. else
  1822. {
  1823. UINT uID;
  1824. if (SUCCEEDED(SHMapICIVerbToCmdID(pici, c_IDMap, ARRAYSIZE(c_IDMap), &uID)))
  1825. {
  1826. switch (uID)
  1827. {
  1828. case NEWITEM_FOLDER:
  1829. dwFlags = NEWTYPE_FOLDER;
  1830. break;
  1831. case NEWITEM_LINK:
  1832. dwFlags = NEWTYPE_LINK;
  1833. break;
  1834. default:
  1835. ASSERTMSG(0, "should not get what we don't put on the menu");
  1836. return E_FAIL;
  1837. }
  1838. }
  1839. }
  1840. TCHAR szFileSpec[MAX_PATH+80]; // Add some slop incase we overflow
  1841. TCHAR szTemp[MAX_PATH+80]; // Add some slop incase we overflow
  1842. //See if the pidl is folder shortcut and if so get the target path.
  1843. SHGetTargetFolderPath(_pidlFolder, szTemp, ARRAYSIZE(szTemp));
  1844. BOOL fLFN = IsLFNDrive(szTemp);
  1845. NEWFILEINFO nfi;
  1846. DWORD dwErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  1847. nfi.lpData = NULL;
  1848. nfi.hkeyNew = NULL;
  1849. switch (dwFlags)
  1850. {
  1851. case NEWTYPE_FOLDER:
  1852. LoadString(g_hinst, fLFN ? IDS_FOLDERLONGPLATE : IDS_FOLDERTEMPLATE, szFileSpec, ARRAYSIZE(szFileSpec));
  1853. break;
  1854. case NEWTYPE_LINK:
  1855. LoadString(g_hinst, IDS_NEWLINKTEMPLATE, szFileSpec, ARRAYSIZE(szFileSpec));
  1856. break;
  1857. default:
  1858. LoadString(g_hinst, IDS_NEWFILEPREFIX, szFileSpec, ARRAYSIZE(szFileSpec));
  1859. //
  1860. // If we are running on a mirrored BiDi localized system,
  1861. // then flip the order of concatenation so that the
  1862. // string is read properly for Arabic. [samera]
  1863. //
  1864. if (IS_BIDI_LOCALIZED_SYSTEM())
  1865. {
  1866. lstrcpyn(szTemp, szFileSpec, ARRAYSIZE(szTemp));
  1867. wnsprintf(szFileSpec, ARRAYSIZE(szFileSpec), TEXT("%s %s"), _pnoiLast->szMenuText, szTemp);
  1868. }
  1869. else
  1870. {
  1871. StrCatBuff(szFileSpec, _pnoiLast->szMenuText, ARRAYSIZE(szFileSpec));
  1872. }
  1873. SHStripMneumonic(szFileSpec);
  1874. if (!(dwFlags & SNCF_NOEXT))
  1875. StrCatBuff(szFileSpec, _pnoiLast->szExt, ARRAYSIZE(szFileSpec));
  1876. break;
  1877. }
  1878. BOOL fCreateStorage = (dwFlags == NEWTYPE_FOLDER);
  1879. //See if the pidl is folder shortcut and if so get the target pidl.
  1880. LPITEMIDLIST pidlTarget;
  1881. hr = SHGetTargetFolderIDList(_pidlFolder, &pidlTarget);
  1882. if (SUCCEEDED(hr))
  1883. {
  1884. IStorage * pStorage;
  1885. hr = StgBindToObject(pidlTarget, STGM_READWRITE, IID_PPV_ARG(IStorage, &pStorage));
  1886. if (SUCCEEDED(hr))
  1887. {
  1888. IStream *pStreamCreated = NULL;
  1889. IStorage *pStorageCreated = NULL;
  1890. STATSTG statstg = { 0 };
  1891. if (fCreateStorage)
  1892. {
  1893. hr = StgMakeUniqueName(pStorage, szFileSpec, IID_PPV_ARG(IStorage, &pStorageCreated));
  1894. if (SUCCEEDED(hr))
  1895. pStorageCreated->Stat(&statstg, STATFLAG_DEFAULT);
  1896. }
  1897. else
  1898. {
  1899. hr = StgMakeUniqueName(pStorage, szFileSpec, IID_PPV_ARG(IStream, &pStreamCreated));
  1900. if (SUCCEEDED(hr))
  1901. pStreamCreated->Stat(&statstg, STATFLAG_DEFAULT);
  1902. }
  1903. if (SUCCEEDED(hr))
  1904. {
  1905. switch (dwFlags)
  1906. {
  1907. case NEWTYPE_FOLDER:
  1908. // we're already done.
  1909. break;
  1910. case NEWTYPE_LINK:
  1911. if (statstg.pwcsName)
  1912. {
  1913. // Lookup Command in Registry under key HKCR/.lnk/ShellNew/Command
  1914. TCHAR szCommand[MAX_PATH];
  1915. DWORD dwLength = sizeof(szCommand);
  1916. if (ERROR_SUCCESS == SHGetValue(HKEY_CLASSES_ROOT,
  1917. TEXT(".lnk\\ShellNew"), TEXT("Command"), NULL, szCommand, &dwLength))
  1918. {
  1919. TCHAR szPath[MAX_PATH];
  1920. hr = _GetItemName(SAFECAST(pStorage, IUnknown*), statstg.pwcsName, szPath, ARRAYSIZE(szPath));
  1921. if (SUCCEEDED(hr))
  1922. {
  1923. hr = RunCommand(pici->hwnd, szPath, szCommand);
  1924. }
  1925. }
  1926. }
  1927. break;
  1928. default:
  1929. if (GetNewFileInfoForExtension(_pnoiLast, &nfi, NULL, NULL))
  1930. {
  1931. switch (nfi.type)
  1932. {
  1933. case NEWTYPE_FILE:
  1934. hr = CopyTemplate(pStreamCreated, &nfi);
  1935. break;
  1936. case NEWTYPE_NULL:
  1937. // already created a zero-length file.
  1938. break;
  1939. case NEWTYPE_DATA:
  1940. ULONG ulWritten;
  1941. hr = pStreamCreated->Write(nfi.lpData, nfi.cbData, &ulWritten);
  1942. if (SUCCEEDED(hr))
  1943. {
  1944. hr = pStreamCreated->Commit(STGC_DEFAULT);
  1945. }
  1946. break;
  1947. case NEWTYPE_COMMAND:
  1948. if (statstg.pwcsName)
  1949. {
  1950. TCHAR szPath[MAX_PATH];
  1951. hr = _GetItemName(SAFECAST(pStorage, IUnknown*), statstg.pwcsName, szPath, ARRAYSIZE(szPath));
  1952. if (SUCCEEDED(hr))
  1953. {
  1954. // oops, we already created the stream, but we actually
  1955. // just wanted the filename for the RunCommand, so we
  1956. // have to delete it first.
  1957. ATOMICRELEASE(pStreamCreated);
  1958. hr = pStorage->DestroyElement(statstg.pwcsName);
  1959. // flush out any notifications from the destroy (the
  1960. // destroy causes notifications).
  1961. SHChangeNotifyHandleEvents();
  1962. if (SUCCEEDED(hr))
  1963. {
  1964. hr = RunCommand(pici->hwnd, szPath, (LPTSTR)nfi.lpData);
  1965. if (hr == S_FALSE)
  1966. hr = S_OK;
  1967. }
  1968. }
  1969. }
  1970. break;
  1971. default:
  1972. hr = E_FAIL;
  1973. break;
  1974. }
  1975. }
  1976. else
  1977. {
  1978. hr = HRESULT_FROM_WIN32(ERROR_BADKEY);
  1979. }
  1980. break;
  1981. }
  1982. // these have to be released before _GeneratePidlFromName, since that opens
  1983. // the storage in exclusive mode for other reasons. but we can't release
  1984. // them earlier because CopyTemplate might need them.
  1985. if (pStorageCreated)
  1986. pStorageCreated->Release();
  1987. if (pStreamCreated)
  1988. pStreamCreated->Release();
  1989. if (SUCCEEDED(hr))
  1990. hr = pStorage->Commit(STGC_DEFAULT);
  1991. pStorage->Release();
  1992. LPITEMIDLIST pidlCreatedItem;
  1993. if (SUCCEEDED(hr) &&
  1994. _GeneratePidlFromName(statstg.pwcsName, &pidlCreatedItem))
  1995. {
  1996. SHChangeNotifyHandleEvents();
  1997. IShellView2 *psv2;
  1998. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IShellView2, &psv2))))
  1999. {
  2000. DWORD dwFlagsSelFlags = SVSI_SELECT | SVSI_POSITIONITEM;
  2001. if (!(dwFlags & NEWTYPE_LINK))
  2002. dwFlagsSelFlags |= SVSI_EDIT;
  2003. psv2->SelectAndPositionItem(ILFindLastID(pidlCreatedItem), dwFlagsSelFlags, NULL);
  2004. psv2->Release();
  2005. }
  2006. ILFree(pidlCreatedItem);
  2007. }
  2008. CoTaskMemFree(statstg.pwcsName);
  2009. }
  2010. else
  2011. {
  2012. pStorage->Release();
  2013. }
  2014. }
  2015. ILFree(pidlTarget);
  2016. }
  2017. if (nfi.lpData)
  2018. LocalFree((HLOCAL)nfi.lpData);
  2019. if (nfi.hkeyNew)
  2020. RegCloseKey(nfi.hkeyNew);
  2021. if (FAILED_AND_NOT_CANCELED(hr) && !(pici->fMask & CMIC_MASK_FLAG_NO_UI))
  2022. {
  2023. TCHAR szTitle[MAX_PATH];
  2024. LoadString(g_hinst, (fCreateStorage ? IDS_DIRCREATEFAILED_TITLE : IDS_FILECREATEFAILED_TITLE), szTitle, ARRAYSIZE(szTitle));
  2025. SHSysErrorMessageBox(pici->hwnd, szTitle, fCreateStorage ? IDS_CANNOTCREATEFOLDER : IDS_CANNOTCREATEFILE,
  2026. HRESULT_CODE(hr), szFileSpec, MB_OK | MB_ICONEXCLAMATION);
  2027. }
  2028. SetErrorMode(dwErrorMode);
  2029. return hr;
  2030. }
  2031. HRESULT CNewMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax)
  2032. {
  2033. switch (uType)
  2034. {
  2035. case GCS_HELPTEXT:
  2036. if (idCmd < NEWITEM_MAX)
  2037. {
  2038. LoadString(g_hinst, (UINT)(IDS_NEWHELP_FIRST + idCmd), (LPTSTR)pszName, cchMax);
  2039. return S_OK;
  2040. }
  2041. break;
  2042. case GCS_HELPTEXTA:
  2043. if (idCmd < NEWITEM_MAX)
  2044. {
  2045. LoadStringA(g_hinst, (UINT)(IDS_NEWHELP_FIRST + idCmd), pszName, cchMax);
  2046. return S_OK;
  2047. }
  2048. break;
  2049. case GCS_VERBW:
  2050. case GCS_VERBA:
  2051. return SHMapCmdIDToVerb(idCmd, c_IDMap, ARRAYSIZE(c_IDMap), pszName, cchMax, (GCS_VERBW == uType));
  2052. }
  2053. return E_NOTIMPL;
  2054. }
  2055. //Defined in fsmenu.obj
  2056. BOOL _MenuCharMatch(LPCTSTR lpsz, TCHAR ch, BOOL fIgnoreAmpersand);
  2057. HRESULT CNewMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
  2058. {
  2059. return HandleMenuMsg2(uMsg,wParam,lParam,NULL);
  2060. }
  2061. HRESULT CNewMenu::_MatchMenuItem(TCHAR ch, LRESULT* plRes)
  2062. {
  2063. // If plRes is NULL we're being called on HandleMenuMsg() which
  2064. // doesn't support returning an LRESULT, which is needed for WM_MENUCHAR...
  2065. if (plRes == NULL)
  2066. return S_FALSE;
  2067. int iLastSelectedItem = -1;
  2068. int iNextMatch = -1;
  2069. BOOL fMoreThanOneMatch = FALSE;
  2070. int c = GetMenuItemCount(_hmenu);
  2071. // Pass 1: Locate the Selected Item
  2072. for (int i = 0; i < c; i++)
  2073. {
  2074. MENUITEMINFO mii = {0};
  2075. mii.cbSize = sizeof(mii);
  2076. mii.fMask = MIIM_STATE;
  2077. if (GetMenuItemInfo(_hmenu, i, MF_BYPOSITION, &mii))
  2078. {
  2079. if (mii.fState & MFS_HILITE)
  2080. {
  2081. iLastSelectedItem = i;
  2082. break;
  2083. }
  2084. }
  2085. }
  2086. // Pass 2: Starting from the selected item, locate the first item with the matching name.
  2087. for (i = iLastSelectedItem + 1; i < c; i++)
  2088. {
  2089. MENUITEMINFO mii = {0};
  2090. mii.cbSize = sizeof(mii);
  2091. mii.fMask = MIIM_DATA | MIIM_STATE;
  2092. if (GetMenuItemInfo(_hmenu, i, MF_BYPOSITION, &mii))
  2093. {
  2094. NEWOBJECTINFO *pnoi = (NEWOBJECTINFO *)mii.dwItemData;
  2095. if (pnoi && _MenuCharMatch(pnoi->szMenuText, ch, FALSE))
  2096. {
  2097. _pnoiLast = pnoi;
  2098. if (iNextMatch != -1)
  2099. {
  2100. fMoreThanOneMatch = TRUE;
  2101. break; // We found all the info we need
  2102. }
  2103. else
  2104. {
  2105. iNextMatch = i;
  2106. }
  2107. }
  2108. }
  2109. }
  2110. // Pass 3: If we did not find a match, or if there was only one match
  2111. // Search from the first item, to the Selected Item
  2112. if (iNextMatch == -1 || fMoreThanOneMatch == FALSE)
  2113. {
  2114. for (i = 0; i <= iLastSelectedItem; i++)
  2115. {
  2116. MENUITEMINFO mii = {0};
  2117. mii.cbSize = sizeof(mii);
  2118. mii.fMask = MIIM_DATA | MIIM_STATE;
  2119. if (GetMenuItemInfo(_hmenu, i, MF_BYPOSITION, &mii))
  2120. {
  2121. NEWOBJECTINFO *pnoi = (NEWOBJECTINFO *)mii.dwItemData;
  2122. if (pnoi && _MenuCharMatch(pnoi->szMenuText, ch, FALSE))
  2123. {
  2124. _pnoiLast = pnoi;
  2125. if (iNextMatch != -1)
  2126. {
  2127. fMoreThanOneMatch = TRUE;
  2128. break;
  2129. }
  2130. else
  2131. {
  2132. iNextMatch = i;
  2133. }
  2134. }
  2135. }
  2136. }
  2137. }
  2138. if (iNextMatch != -1)
  2139. {
  2140. *plRes = MAKELONG(iNextMatch, fMoreThanOneMatch? MNC_SELECT : MNC_EXECUTE);
  2141. }
  2142. else
  2143. {
  2144. *plRes = MAKELONG(0, MNC_IGNORE);
  2145. }
  2146. return S_OK;
  2147. }
  2148. HRESULT CNewMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam,LRESULT *plResult)
  2149. {
  2150. HRESULT hr = S_OK;
  2151. LRESULT lRes = 0;
  2152. switch (uMsg)
  2153. {
  2154. case WM_INITMENUPOPUP:
  2155. if (_hmenu == NULL)
  2156. {
  2157. _hmenu = (HMENU)wParam;
  2158. }
  2159. InitMenuPopup(_hmenu);
  2160. break;
  2161. case WM_DRAWITEM:
  2162. DrawItem((DRAWITEMSTRUCT *)lParam);
  2163. break;
  2164. case WM_MEASUREITEM:
  2165. lRes = MeasureItem((MEASUREITEMSTRUCT *)lParam);
  2166. break;
  2167. case WM_MENUCHAR:
  2168. hr = _MatchMenuItem((TCHAR)LOWORD(wParam), &lRes);
  2169. break;
  2170. default:
  2171. hr = E_NOTIMPL;
  2172. break;
  2173. }
  2174. if (plResult)
  2175. *plResult = lRes;
  2176. return hr;
  2177. }
  2178. HRESULT CNewMenu::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
  2179. {
  2180. ASSERT(_pidlFolder == NULL);
  2181. _pidlFolder = ILClone(pidlFolder);
  2182. IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
  2183. return S_OK;
  2184. }
  2185. BOOL CNewMenu::DrawItem(DRAWITEMSTRUCT *lpdi)
  2186. {
  2187. BOOL fFlatMenu = FALSE;
  2188. BOOL fFrameRect = FALSE;
  2189. SystemParametersInfo(SPI_GETFLATMENU, 0, (PVOID)&fFlatMenu, 0);
  2190. if ((lpdi->itemAction & ODA_SELECT) || (lpdi->itemAction & ODA_DRAWENTIRE))
  2191. {
  2192. int x, y;
  2193. SIZE sz;
  2194. NEWOBJECTINFO *pnoi = (NEWOBJECTINFO *)lpdi->itemData;
  2195. // Draw the image (if there is one).
  2196. GetTextExtentPoint(lpdi->hDC, pnoi->szMenuText, lstrlen(pnoi->szMenuText), &sz);
  2197. if (lpdi->itemState & ODS_SELECTED)
  2198. {
  2199. // REVIEW HACK - keep track of the last selected item.
  2200. _pnoiLast = pnoi;
  2201. if (fFlatMenu)
  2202. {
  2203. fFrameRect = TRUE;
  2204. SetBkColor(lpdi->hDC, GetSysColor(COLOR_MENUHILIGHT));
  2205. SetTextColor(lpdi->hDC, GetSysColor(COLOR_MENUTEXT));
  2206. FillRect(lpdi->hDC,&lpdi->rcItem,GetSysColorBrush(COLOR_MENUHILIGHT));
  2207. }
  2208. else
  2209. {
  2210. SetBkColor(lpdi->hDC, GetSysColor(COLOR_HIGHLIGHT));
  2211. SetTextColor(lpdi->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
  2212. FillRect(lpdi->hDC,&lpdi->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
  2213. }
  2214. }
  2215. else
  2216. {
  2217. SetTextColor(lpdi->hDC, GetSysColor(COLOR_MENUTEXT));
  2218. FillRect(lpdi->hDC,&lpdi->rcItem,GetSysColorBrush(COLOR_MENU));
  2219. }
  2220. RECT rc = lpdi->rcItem;
  2221. rc.left += +2*CXIMAGEGAP+g_cxSmIcon;
  2222. DrawText(lpdi->hDC,pnoi->szMenuText,lstrlen(pnoi->szMenuText),
  2223. &rc,DT_SINGLELINE|DT_VCENTER);
  2224. if (pnoi->iImage != -1)
  2225. {
  2226. x = lpdi->rcItem.left+CXIMAGEGAP;
  2227. y = (lpdi->rcItem.bottom+lpdi->rcItem.top-g_cySmIcon)/2;
  2228. HIMAGELIST himlSmall;
  2229. Shell_GetImageLists(NULL, &himlSmall);
  2230. ImageList_Draw(himlSmall, pnoi->iImage, lpdi->hDC, x, y, ILD_TRANSPARENT);
  2231. }
  2232. else
  2233. {
  2234. x = lpdi->rcItem.left+CXIMAGEGAP;
  2235. y = (lpdi->rcItem.bottom+lpdi->rcItem.top-g_cySmIcon)/2;
  2236. }
  2237. if (fFrameRect)
  2238. {
  2239. HBRUSH hbrFill = (HBRUSH)GetSysColorBrush(COLOR_HIGHLIGHT);
  2240. HBRUSH hbrSave = (HBRUSH)SelectObject(lpdi->hDC, hbrFill);
  2241. int x = lpdi->rcItem.left;
  2242. int y = lpdi->rcItem.top;
  2243. int cx = lpdi->rcItem.right - x - 1;
  2244. int cy = lpdi->rcItem.bottom - y - 1;
  2245. PatBlt(lpdi->hDC, x, y, 1, cy, PATCOPY);
  2246. PatBlt(lpdi->hDC, x + 1, y, cx, 1, PATCOPY);
  2247. PatBlt(lpdi->hDC, x, y + cy, cx, 1, PATCOPY);
  2248. PatBlt(lpdi->hDC, x + cx, y + 1, 1, cy, PATCOPY);
  2249. SelectObject(lpdi->hDC, hbrSave);
  2250. }
  2251. return TRUE;
  2252. }
  2253. return FALSE;
  2254. }
  2255. LRESULT CNewMenu::MeasureItem(MEASUREITEMSTRUCT *pmi)
  2256. {
  2257. LRESULT lres = FALSE;
  2258. NEWOBJECTINFO *pnoi = (NEWOBJECTINFO *)pmi->itemData;
  2259. if (pnoi)
  2260. {
  2261. // Get the rough height of an item so we can work out when to break the
  2262. // menu. User should really do this for us but that would be useful.
  2263. HDC hdc = GetDC(NULL);
  2264. if (hdc)
  2265. {
  2266. // REVIEW cache out the menu font?
  2267. NONCLIENTMETRICS ncm;
  2268. ncm.cbSize = sizeof(ncm);
  2269. if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE))
  2270. {
  2271. HFONT hfont = CreateFontIndirect(&ncm.lfMenuFont);
  2272. if (hfont)
  2273. {
  2274. SIZE sz;
  2275. HFONT hfontOld = (HFONT)SelectObject(hdc, hfont);
  2276. GetTextExtentPoint(hdc, pnoi->szMenuText, lstrlen(pnoi->szMenuText), &sz);
  2277. pmi->itemHeight = max (g_cySmIcon+CXIMAGEGAP/2, ncm.iMenuHeight);
  2278. pmi->itemWidth = g_cxSmIcon + 2*CXIMAGEGAP + sz.cx;
  2279. //pmi->itemWidth = 2*CXIMAGEGAP + sz.cx;
  2280. SelectObject(hdc, hfontOld);
  2281. DeleteObject(hfont);
  2282. lres = TRUE;
  2283. }
  2284. }
  2285. ReleaseDC(NULL, hdc);
  2286. }
  2287. }
  2288. return lres;
  2289. }
  2290. BOOL GetClassDisplayName(LPTSTR pszClass,LPTSTR pszDisplayName,DWORD cchDisplayName)
  2291. {
  2292. DWORD cch;
  2293. return SUCCEEDED(AssocQueryString(0, ASSOCSTR_COMMAND, pszClass, TEXT("open"), NULL, &cch)) &&
  2294. SUCCEEDED(AssocQueryString(0, ASSOCSTR_FRIENDLYDOCNAME, pszClass, NULL, pszDisplayName, &cchDisplayName));
  2295. }
  2296. // New Menu item consolidation worker task
  2297. class CNewMenuConsolidator : public CRunnableTask
  2298. {
  2299. public:
  2300. virtual STDMETHODIMP RunInitRT(void);
  2301. static const GUID _taskid;
  2302. static HRESULT CreateInstance(REFIID riid, void **ppv);
  2303. private:
  2304. CNewMenuConsolidator();
  2305. ~CNewMenuConsolidator();
  2306. HANDLE _hMutex, _hEvent;
  2307. };
  2308. CNewMenuConsolidator::CNewMenuConsolidator() :
  2309. CRunnableTask(RTF_DEFAULT),
  2310. _hMutex(CreateMutex(NULL, FALSE, SHELLNEW_CONSOLIDATION_MUTEX)),
  2311. _hEvent(CreateEvent(NULL, FALSE, FALSE, SHELLNEW_CONSOLIDATION_EVENT))
  2312. {
  2313. DllAddRef();
  2314. }
  2315. CNewMenuConsolidator::~CNewMenuConsolidator()
  2316. {
  2317. if (_hMutex)
  2318. {
  2319. CloseHandle(_hMutex);
  2320. }
  2321. if (_hEvent)
  2322. {
  2323. CloseHandle(_hEvent);
  2324. }
  2325. DllRelease();
  2326. }
  2327. //
  2328. // Instance generator.
  2329. //
  2330. HRESULT CNewMenuConsolidator::CreateInstance(REFIID riid, void **ppv)
  2331. {
  2332. HRESULT hr = E_OUTOFMEMORY;
  2333. CNewMenuConsolidator *pnmc = new CNewMenuConsolidator();
  2334. if (pnmc)
  2335. {
  2336. if (!pnmc->_hMutex || !pnmc->_hEvent)
  2337. {
  2338. hr = E_FAIL;
  2339. }
  2340. else
  2341. {
  2342. hr = pnmc->QueryInterface(riid, ppv);
  2343. }
  2344. pnmc->Release();
  2345. }
  2346. return hr;
  2347. }
  2348. const GUID CNewMenuConsolidator::_taskid =
  2349. { 0xf87a1f28, 0xc7f, 0x11d2, { 0xbe, 0x1d, 0x0, 0xa0, 0xc9, 0xa8, 0x3d, 0xa1 } };
  2350. #define REGSTR_SESSION_SHELLNEW STRREG_DISCARDABLE STRREG_POSTSETUP TEXT("\\ShellNew")
  2351. #define REGVAL_SESSION_SHELLNEW_TIMESTAMP TEXT("~reserved~")
  2352. #define REGVAL_SESSION_SHELLNEW_LANG TEXT("Language")
  2353. #define SHELLNEW_CACHE_CURRENTVERSION MAKELONG(1, 1)
  2354. // Constructs a current New submenu cache stamp.
  2355. void CNewMenu_MakeCacheStamp(SHELLNEW_CACHE_STAMP* pStamp)
  2356. {
  2357. pStamp->cbStruct = sizeof(*pStamp);
  2358. pStamp->ver = SHELLNEW_CACHE_CURRENTVERSION;
  2359. GetLocalTime(&pStamp->lastupdate);
  2360. }
  2361. // Determines whether the New submenu cache needs to be rebuilt.
  2362. BOOL CNewMenu_ShouldUpdateCache(SHELLNEW_CACHE_STAMP* pStamp)
  2363. {
  2364. // Correct version?
  2365. return !(sizeof(*pStamp) == pStamp->cbStruct &&
  2366. SHELLNEW_CACHE_CURRENTVERSION == pStamp->ver);
  2367. }
  2368. // Gathers up shellnew entries from HKCR into a distinct registry location
  2369. // for faster enumeration of the New submenu items.
  2370. //
  2371. // We'll do a first time cache initialization only if we have to before showing
  2372. // the menu, but will always rebuild the cache following display of the menu.
  2373. HRESULT CNewMenu::ConsolidateMenuItems(BOOL bForce)
  2374. {
  2375. HKEY hkeyShellNew = NULL;
  2376. BOOL bUpdate = TRUE; // unless we discover otherwise
  2377. HRESULT hr = S_OK;
  2378. // make sure that a worker thread isnt currently slamming the registry info we're inspecting.
  2379. // if we timeout, then do nothing, since the worker thread is already working on it.
  2380. if (WAIT_OBJECT_0 == WaitForSingleObject(_hMutex, 0))
  2381. {
  2382. // If we're not being told to unconditionally update the cache and
  2383. // we validate that we've already established one, then we get out of doing any
  2384. // work.
  2385. if (!bForce &&
  2386. ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_SESSION_SHELLNEW,
  2387. 0, KEY_ALL_ACCESS, &hkeyShellNew))
  2388. {
  2389. SHELLNEW_CACHE_STAMP stamp;
  2390. ULONG cbVal = sizeof(stamp);
  2391. if (ERROR_SUCCESS == SHQueryValueEx(hkeyShellNew, REGVAL_SESSION_SHELLNEW_TIMESTAMP, NULL,
  2392. NULL, (LPBYTE)&stamp, &cbVal) &&
  2393. sizeof(stamp) == cbVal)
  2394. {
  2395. bUpdate = CNewMenu_ShouldUpdateCache(&stamp);
  2396. }
  2397. LCID lcid;
  2398. ULONG cblcid = sizeof(lcid);
  2399. if (!bUpdate &&
  2400. ERROR_SUCCESS == SHQueryValueEx(hkeyShellNew, REGVAL_SESSION_SHELLNEW_LANG, NULL,
  2401. NULL, (LPBYTE)&lcid, &cblcid) &&
  2402. sizeof(lcid) == cblcid)
  2403. {
  2404. bUpdate = (GetUserDefaultUILanguage() != lcid); // if the languages are different, then update
  2405. }
  2406. RegCloseKey(hkeyShellNew);
  2407. }
  2408. // end synchronize
  2409. ReleaseMutex(_hMutex);
  2410. if (bUpdate)
  2411. {
  2412. IShellTaskScheduler* pScheduler;
  2413. hr = CoCreateInstance(CLSID_SharedTaskScheduler, NULL, CLSCTX_INPROC,
  2414. IID_PPV_ARG(IShellTaskScheduler, &pScheduler));
  2415. if (SUCCEEDED(hr))
  2416. {
  2417. IRunnableTask *pTask;
  2418. hr = CNewMenuConsolidator::CreateInstance(IID_PPV_ARG(IRunnableTask, &pTask));
  2419. if (SUCCEEDED(hr))
  2420. {
  2421. // the background task will set _hEvent for us when it's done.
  2422. hr = pScheduler->AddTask(pTask, CNewMenuConsolidator::_taskid, NULL, ITSAT_DEFAULT_PRIORITY);
  2423. pTask->Release();
  2424. }
  2425. pScheduler->Release();
  2426. }
  2427. }
  2428. if (!bUpdate || FAILED(hr))
  2429. {
  2430. // if the scheduler wont activate the event, we do it ourselves.
  2431. SetEvent(_hEvent);
  2432. }
  2433. }
  2434. return hr;
  2435. }
  2436. // Consolidation worker.
  2437. STDMETHODIMP CNewMenuConsolidator::RunInitRT()
  2438. {
  2439. ULONG dwErr = ERROR_SUCCESS;
  2440. // the possible owners of the mutex are
  2441. // - nobody, we'll own it
  2442. // - other worker threads like this one
  2443. // - the guy who checks to see if the cached info is in the registry.
  2444. // if there's another worker thread which owns this mutex, then bail, since that one
  2445. // will do all the work we're going to do.
  2446. // if the guy who checks the cached info has it, then bail, since it'll spawn
  2447. // another one of these soon enough.
  2448. // so use a 0 timeout.
  2449. if (WAIT_OBJECT_0 == WaitForSingleObject(_hMutex, 0))
  2450. {
  2451. HKEY hkeyShellNew = NULL;
  2452. TCHAR szExt[MAX_PATH];
  2453. ULONG dwDisposition;
  2454. // Delete the existing cache; we'll build it from scratch each time.
  2455. while (ERROR_SUCCESS == (dwErr = RegCreateKeyEx(HKEY_CURRENT_USER, REGSTR_SESSION_SHELLNEW,
  2456. 0, NULL, 0, KEY_ALL_ACCESS, NULL,
  2457. &hkeyShellNew, &dwDisposition)) &&
  2458. REG_CREATED_NEW_KEY != dwDisposition)
  2459. {
  2460. // Key already existed, so delete it, and loop to reopen.
  2461. RegCloseKey(hkeyShellNew);
  2462. SHDeleteKey(HKEY_CURRENT_USER, REGSTR_SESSION_SHELLNEW);
  2463. hkeyShellNew = NULL;
  2464. }
  2465. if (ERROR_SUCCESS == dwErr)
  2466. {
  2467. // Enumerate each subkey of HKCR, looking for New menu items.
  2468. for (int i = 0; RegEnumKey(HKEY_CLASSES_ROOT, i, szExt, ARRAYSIZE(szExt)) == ERROR_SUCCESS; i++)
  2469. {
  2470. TCHAR szClass[CCH_KEYMAX];
  2471. TCHAR szDisplayName[CCH_KEYMAX];
  2472. LONG cbVal = sizeof(szClass);
  2473. // find .ext that have proper class descriptions with them.
  2474. if ((szExt[0] == TEXT('.')) &&
  2475. SHRegQueryValue(HKEY_CLASSES_ROOT, szExt, szClass, &cbVal) == ERROR_SUCCESS
  2476. && (cbVal > 0)
  2477. && GetClassDisplayName(szClass, szDisplayName, ARRAYSIZE(szDisplayName)))
  2478. {
  2479. NEWOBJECTINFO noi = {0};
  2480. HKEY hkeyIterate = NULL;
  2481. int iIndex = 0;
  2482. lstrcpyn(noi.szExt, szExt, ARRAYSIZE(noi.szExt));
  2483. lstrcpyn(noi.szClass, szClass, ARRAYSIZE(noi.szClass));
  2484. lstrcpyn(noi.szMenuText, szDisplayName, ARRAYSIZE(noi.szMenuText));
  2485. noi.iImage = -1;
  2486. // Retrieve all additional information for the key.
  2487. while (GetNewFileInfoForExtension(&noi, NULL, &hkeyIterate, &iIndex))
  2488. {
  2489. // Stick it in the cache.
  2490. RegSetValueEx(hkeyShellNew, noi.szMenuText, NULL, REG_BINARY,
  2491. (LPBYTE)&noi, sizeof(noi));
  2492. }
  2493. }
  2494. }
  2495. // Stamp the cache.
  2496. SHELLNEW_CACHE_STAMP stamp;
  2497. CNewMenu_MakeCacheStamp(&stamp);
  2498. RegSetValueEx(hkeyShellNew, REGVAL_SESSION_SHELLNEW_TIMESTAMP,
  2499. NULL, REG_BINARY, (LPBYTE)&stamp, sizeof(stamp));
  2500. LCID lcid = GetUserDefaultUILanguage();
  2501. RegSetValueEx(hkeyShellNew, REGVAL_SESSION_SHELLNEW_LANG,
  2502. NULL, REG_DWORD, (LPBYTE)&lcid, sizeof(lcid));
  2503. }
  2504. if (NULL != hkeyShellNew)
  2505. RegCloseKey(hkeyShellNew);
  2506. // signal the event so InitMenuPopup can proceed.
  2507. SetEvent(_hEvent);
  2508. ReleaseMutex(_hMutex);
  2509. }
  2510. return HRESULT_FROM_WIN32(dwErr);
  2511. }
  2512. BOOL CNewMenu::_InsertNewMenuItem(HMENU hmenu, UINT idCmd, NEWOBJECTINFO *pnoiClone)
  2513. {
  2514. if (pnoiClone->szMenuText[0])
  2515. {
  2516. NEWOBJECTINFO *pnoi = (NEWOBJECTINFO *)LocalAlloc(LPTR, sizeof(NEWOBJECTINFO));
  2517. if (pnoi)
  2518. {
  2519. *pnoi = *pnoiClone;
  2520. pnoi->msaa.dwMSAASignature = MSAA_MENU_SIG;
  2521. if (StrChr(pnoi->szMenuText, TEXT('&')) == NULL)
  2522. {
  2523. pnoi->chPrefix = TEXT('&');
  2524. pnoi->msaa.pszWText = &pnoi->chPrefix;
  2525. }
  2526. else
  2527. {
  2528. pnoi->msaa.pszWText = pnoi->szMenuText;
  2529. }
  2530. pnoi->msaa.cchWText = lstrlen(pnoi->msaa.pszWText);
  2531. MENUITEMINFO mii = {0};
  2532. mii.cbSize = sizeof(mii);
  2533. mii.fMask = MIIM_TYPE | MIIM_DATA | MIIM_ID;
  2534. mii.fType = MFT_OWNERDRAW;
  2535. mii.fState = MFS_ENABLED;
  2536. mii.wID = idCmd;
  2537. mii.dwItemData = (DWORD_PTR)pnoi;
  2538. mii.dwTypeData = (LPTSTR)pnoi;
  2539. if (-1 != DPA_AppendPtr(_hdpaMenuInfo, pnoi))
  2540. {
  2541. InsertMenuItem(hmenu, -1, TRUE, &mii);
  2542. }
  2543. else
  2544. {
  2545. LocalFree(pnoi);
  2546. return FALSE;
  2547. }
  2548. }
  2549. }
  2550. return TRUE;
  2551. }
  2552. // WM_INITMENUPOPUP handler
  2553. BOOL CNewMenu::InitMenuPopup(HMENU hmenu)
  2554. {
  2555. UINT iStart = 3;
  2556. NEWOBJECTINFO noi = {0};
  2557. if (GetItemData(hmenu, iStart)) //Position 0 is New Folder, 1 shortcut, 2 sep
  2558. return FALSE; //already initialized. No need to do anything
  2559. _hdpaMenuInfo = DPA_Create(4);
  2560. if (!_hdpaMenuInfo)
  2561. return FALSE;
  2562. //Remove the place holder.
  2563. DeleteMenu(hmenu,0,MF_BYPOSITION);
  2564. //Insert New Folder menu item
  2565. LoadString(g_hinst, IDS_NEWFOLDER, noi.szMenuText, ARRAYSIZE(noi.szMenuText));
  2566. noi.dwFlags = NEWTYPE_FOLDER;
  2567. noi.iImage = Shell_GetCachedImageIndex(TEXT("shell32.dll"), II_FOLDER, 0); //Shange to indicate Folder
  2568. _InsertNewMenuItem(hmenu, _idCmdFirst-NEWITEM_MAX+NEWITEM_FOLDER, &noi);
  2569. TCHAR szTemp[MAX_PATH+80]; // Add some slop incase we overflow
  2570. //See if the pidl is folder shortcut and if so get the target path.
  2571. SHGetTargetFolderPath(_pidlFolder, szTemp, ARRAYSIZE(szTemp));
  2572. if (IsLFNDrive(szTemp)) //for short filename servers we don't support anything but new folder
  2573. {
  2574. //Insert New Shortcut menu item
  2575. LoadString(g_hinst, IDS_NEWLINK, noi.szMenuText, ARRAYSIZE(noi.szMenuText));
  2576. noi.iImage = Shell_GetCachedImageIndex(TEXT("shell32.dll"), II_LINK, 0); //Shange to indicate Link
  2577. noi.dwFlags = NEWTYPE_LINK;
  2578. _InsertNewMenuItem(hmenu, _idCmdFirst-NEWITEM_MAX+NEWITEM_LINK, &noi);
  2579. //Insert menu item separator
  2580. AppendMenu(hmenu, MF_SEPARATOR, 0, NULL);
  2581. // This may take a while, so put up the hourglass
  2582. DECLAREWAITCURSOR;
  2583. SetWaitCursor();
  2584. // Retrieve extension menu items from cache:
  2585. // begin synchronize.
  2586. //
  2587. if (WAIT_OBJECT_0 == WaitForSingleObject(_hEvent, INFINITE))
  2588. {
  2589. HKEY hkeyShellNew;
  2590. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_SESSION_SHELLNEW,
  2591. 0, KEY_ALL_ACCESS, &hkeyShellNew))
  2592. {
  2593. TCHAR szVal[CCH_KEYMAX];
  2594. ULONG cbVal = ARRAYSIZE(szVal);
  2595. ULONG cbData = sizeof(noi);
  2596. ULONG dwType = REG_BINARY;
  2597. for (int i = 0;
  2598. ERROR_SUCCESS == RegEnumValue(hkeyShellNew, i, szVal, &cbVal, 0,
  2599. &dwType, (LPBYTE)&noi, &cbData);
  2600. i++)
  2601. {
  2602. if (lstrcmp(szVal, REGVAL_SESSION_SHELLNEW_TIMESTAMP) != 0 &&
  2603. sizeof(noi) == cbData &&
  2604. REG_BINARY == dwType)
  2605. {
  2606. SHFILEINFO sfi;
  2607. _himlSystemImageList = (HIMAGELIST)SHGetFileInfo(noi.szExt, FILE_ATTRIBUTE_NORMAL,
  2608. &sfi, sizeof(SHFILEINFO),
  2609. SHGFI_USEFILEATTRIBUTES |
  2610. SHGFI_SYSICONINDEX |
  2611. SHGFI_SMALLICON);
  2612. if (_himlSystemImageList)
  2613. {
  2614. //pnoi->himlSmallIcons = sfi.hIcon;
  2615. noi.iImage = sfi.iIcon;
  2616. }
  2617. else
  2618. {
  2619. //pnoi->himlSmallIcons = INVALID_HANDLE_VALUE;
  2620. noi.iImage = -1;
  2621. }
  2622. _InsertNewMenuItem(hmenu, _idCmdFirst, &noi);
  2623. }
  2624. cbVal = ARRAYSIZE(szVal);
  2625. cbData = sizeof(noi);
  2626. dwType = REG_BINARY;
  2627. }
  2628. RegCloseKey(hkeyShellNew);
  2629. }
  2630. // consolidate menu items following display.
  2631. ConsolidateMenuItems(TRUE);
  2632. }
  2633. ResetWaitCursor();
  2634. }
  2635. return TRUE;
  2636. }
  2637. NEWOBJECTINFO *CNewMenu::GetItemData(HMENU hmenu, UINT iItem)
  2638. {
  2639. MENUITEMINFO mii;
  2640. mii.cbSize = sizeof(MENUITEMINFO);
  2641. mii.fMask = MIIM_DATA | MIIM_STATE;
  2642. mii.cch = 0; // just in case...
  2643. if (GetMenuItemInfo(hmenu, iItem, TRUE, &mii))
  2644. return (NEWOBJECTINFO *)mii.dwItemData;
  2645. return NULL;
  2646. }
  2647. LPTSTR ProcessArgs(LPTSTR szArgs,...)
  2648. {
  2649. LPTSTR szRet;
  2650. va_list ArgList;
  2651. va_start(ArgList,szArgs);
  2652. if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
  2653. szArgs, 0, 0, (LPTSTR)&szRet, 0, &ArgList))
  2654. {
  2655. return NULL;
  2656. }
  2657. va_end(ArgList);
  2658. return szRet;
  2659. }
  2660. HRESULT CNewMenu::RunCommand(HWND hwnd, LPCTSTR pszPath, LPCTSTR pszRun)
  2661. {
  2662. SHELLEXECUTEINFO ei = { 0 };
  2663. TCHAR szCommand[MAX_PATH];
  2664. TCHAR szRun[MAX_PATH];
  2665. SHExpandEnvironmentStrings(pszRun, szCommand, ARRAYSIZE(szCommand));
  2666. lstrcpyn(szRun, szCommand, ARRAYSIZE(szRun));
  2667. PathRemoveArgs(szCommand);
  2668. //
  2669. // Mondo hackitude-o-rama.
  2670. //
  2671. // Win95, IE3, SDK: %1 - filename
  2672. //
  2673. // IE4: %1 - hwnd, %2 = filename
  2674. //
  2675. // So IE4 broken Win95 compat and broke compat with the SDK.
  2676. // For IE5 we restore compat with Win95 and the SDK, while
  2677. // still generating an IE4-style command if we detect that the
  2678. // registry key owner tested with IE4 rather than following the
  2679. // instructions in the SDK.
  2680. //
  2681. // The algorithm is like this:
  2682. //
  2683. // If we see a "%2", then use %1 - hwnd, %2 - filename
  2684. // Otherwise, use %1 - filename, %2 - hwnd
  2685. //
  2686. LPTSTR pszArgs = PathGetArgs(szRun);
  2687. LPTSTR ptszPercent2 = StrStr(pszArgs, TEXT("%2"));
  2688. if (ptszPercent2 && ptszPercent2[2] != TEXT('!'))
  2689. {
  2690. // App wants %1 = hwnd and %2 = filename
  2691. pszArgs = ProcessArgs(pszArgs, (DWORD_PTR)hwnd, pszPath);
  2692. }
  2693. else
  2694. {
  2695. // App wants %2 = hwnd and %1 = filename
  2696. pszArgs = ProcessArgs(pszArgs, pszPath, (DWORD_PTR)hwnd);
  2697. }
  2698. HRESULT hr;
  2699. if (pszArgs)
  2700. {
  2701. HMONITOR hMon = MonitorFromPoint(_ptNewItem, MONITOR_DEFAULTTONEAREST);
  2702. if (hMon)
  2703. {
  2704. ei.fMask |= SEE_MASK_HMONITOR;
  2705. ei.hMonitor = (HANDLE)hMon;
  2706. }
  2707. ei.hwnd = hwnd;
  2708. ei.lpFile = szCommand;
  2709. ei.lpParameters = pszArgs;
  2710. ei.nShow = SW_SHOWNORMAL;
  2711. ei.cbSize = sizeof(ei);
  2712. if (ShellExecuteEx(&ei))
  2713. hr = S_FALSE; // Return S_FALSE because ShellExecuteEx is not atomic
  2714. else
  2715. hr = E_FAIL;
  2716. LocalFree(pszArgs);
  2717. }
  2718. else
  2719. hr = E_OUTOFMEMORY;
  2720. return hr;
  2721. }
  2722. HRESULT CNewMenu::CopyTemplate(IStream *pStream, NEWFILEINFO *pnfi)
  2723. {
  2724. TCHAR szSrcFolder[MAX_PATH], szSrc[MAX_PATH];
  2725. szSrc[0] = 0;
  2726. // failure here is OK, we will try CSIDL_COMMON_TEMPLATES too.
  2727. if (SHGetSpecialFolderPath(NULL, szSrcFolder, CSIDL_TEMPLATES, FALSE))
  2728. {
  2729. PathCombine(szSrc, szSrcFolder, (LPTSTR)pnfi->lpData);
  2730. if (!PathFileExistsAndAttributes(szSrc, NULL))
  2731. szSrc[0] = 0;
  2732. }
  2733. if (szSrc[0] == 0)
  2734. {
  2735. if (SHGetSpecialFolderPath(NULL, szSrcFolder, CSIDL_COMMON_TEMPLATES, FALSE))
  2736. {
  2737. PathCombine(szSrc, szSrcFolder, (LPTSTR)pnfi->lpData);
  2738. if (!PathFileExistsAndAttributes(szSrc, NULL))
  2739. szSrc[0] = 0;
  2740. }
  2741. }
  2742. if (szSrc[0] == 0)
  2743. {
  2744. // work around CSIDL_TEMPLATES not being setup right or
  2745. // templates that are left in the old %windir%\shellnew location
  2746. GetWindowsDirectory(szSrcFolder, ARRAYSIZE(szSrcFolder));
  2747. PathAppend(szSrcFolder, TEXT("ShellNew"));
  2748. // note: if the file spec is fully qualified szSrcFolder is ignored
  2749. PathCombine(szSrc, szSrcFolder, (LPTSTR)pnfi->lpData);
  2750. }
  2751. //
  2752. // we just allow a null file to be created when the copy fails.
  2753. // this is for appcompat with office97. they fail to copy winword8.doc
  2754. // anywhere on the system. on win2k we succeed anyway with an empty file.
  2755. //
  2756. return SUCCEEDED(StgCopyFileToStream(szSrc, pStream)) ? S_OK : S_FALSE;
  2757. }