Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2971 lines
93 KiB

  1. #include "stdafx.h"
  2. #pragma hdrstop
  3. #include <shlobjp.h>
  4. #include <initguid.h>
  5. #include "apithk.h"
  6. #include "resource.h"
  7. #include <runtask.h>
  8. #include <msi.h>
  9. #include <msip.h>
  10. #define REGSTR_EXPLORER_WINUPDATE REGSTR_PATH_EXPLORER TEXT("\\WindowsUpdate")
  11. #define IDM_TOPLEVELSTARTMENU 0
  12. // StartMenuInit Flags
  13. #define STARTMENU_DISPLAYEDBEFORE 0x00000001
  14. #define STARTMENU_CHEVRONCLICKED 0x00000002
  15. // New item counts for UEM stuff
  16. #define UEM_NEWITEMCOUNT 2
  17. // Menuband per pane user data
  18. typedef struct
  19. {
  20. BITBOOL _fInitialized;
  21. } SMUSERDATA;
  22. // for g_hdpaDarwinAds
  23. EXTERN_C CRITICAL_SECTION g_csDarwinAds = {0};
  24. #define ENTERCRITICAL_DARWINADS EnterCriticalSection(&g_csDarwinAds)
  25. #define LEAVECRITICAL_DARWINADS LeaveCriticalSection(&g_csDarwinAds)
  26. // The threading concern with this variable is create/delete/add/remove. We will only remove an item
  27. // and delete the hdpa on the main thread. We will however add and create on both threads.
  28. // We need to serialize access to the dpa, so we're going to grab the shell crisec.
  29. HDPA g_hdpaDarwinAds = NULL;
  30. class CDarwinAd
  31. {
  32. public:
  33. LPITEMIDLIST _pidl;
  34. LPTSTR _pszDescriptor;
  35. LPTSTR _pszLocalPath;
  36. INSTALLSTATE _state;
  37. CDarwinAd(LPITEMIDLIST pidl, LPTSTR psz)
  38. {
  39. // I take ownership of this pidl
  40. _pidl = pidl;
  41. Str_SetPtr(&_pszDescriptor, psz);
  42. }
  43. void CheckInstalled()
  44. {
  45. TCHAR szProduct[GUIDSTR_MAX];
  46. TCHAR szFeature[MAX_FEATURE_CHARS+1];
  47. TCHAR szComponent[GUIDSTR_MAX];
  48. if (MsiDecomposeDescriptor(_pszDescriptor, szProduct, szFeature, szComponent, NULL) == ERROR_SUCCESS)
  49. {
  50. _state = MsiQueryFeatureState(szProduct, szFeature);
  51. }
  52. else
  53. {
  54. _state = INSTALLSTATE_INVALIDARG;
  55. }
  56. // Note: Cannot use ParseDarwinID since that bumps the usage count
  57. // for the app and we're not running the app, just looking at it.
  58. // Also because ParseDarwinID tries to install the app (eek!)
  59. //
  60. // Must ignore INSTALLSTATE_SOURCE because MsiGetComponentPath will
  61. // try to install the app even though we're just querying...
  62. TCHAR szCommand[MAX_PATH];
  63. DWORD cch = ARRAYSIZE(szCommand);
  64. if (_state == INSTALLSTATE_LOCAL &&
  65. MsiGetComponentPath(szProduct, szComponent, szCommand, &cch) == _state)
  66. {
  67. PathUnquoteSpaces(szCommand);
  68. Str_SetPtr(&_pszLocalPath, szCommand);
  69. }
  70. else
  71. {
  72. Str_SetPtr(&_pszLocalPath, NULL);
  73. }
  74. }
  75. BOOL IsAd()
  76. {
  77. return _state == INSTALLSTATE_ADVERTISED;
  78. }
  79. ~CDarwinAd()
  80. {
  81. ILFree(_pidl);
  82. Str_SetPtr(&_pszDescriptor, NULL);
  83. Str_SetPtr(&_pszLocalPath, NULL);
  84. }
  85. };
  86. int GetDarwinIndex(LPCITEMIDLIST pidlFull, CDarwinAd** ppda);
  87. HRESULT GetMyPicsDisplayName(LPTSTR pszBuffer, UINT cchBuffer)
  88. {
  89. LPITEMIDLIST pidlMyPics = SHCloneSpecialIDList(NULL, CSIDL_MYPICTURES, FALSE);
  90. if (pidlMyPics)
  91. {
  92. HRESULT hRet = SHGetNameAndFlags(pidlMyPics, SHGDN_NORMAL, pszBuffer, cchBuffer, NULL);
  93. ILFree(pidlMyPics);
  94. return hRet;
  95. }
  96. return E_FAIL;
  97. }
  98. BOOL AreIntelliMenusEnabled()
  99. {
  100. DWORD dwRest = SHRestricted(REST_INTELLIMENUS);
  101. if (dwRest != RESTOPT_INTELLIMENUS_USER)
  102. return (dwRest == RESTOPT_INTELLIMENUS_ENABLED);
  103. return SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("IntelliMenus"),
  104. FALSE, TRUE); // Don't ignore HKCU, Enable Menus by default
  105. }
  106. BOOL FeatureEnabled(LPTSTR pszFeature)
  107. {
  108. return SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, pszFeature,
  109. FALSE, // Don't ignore HKCU
  110. FALSE); // Disable this cool feature.
  111. }
  112. // Since we can be presented with an Augmented shellfolder and we need a Full pidl,
  113. // we have been given the responsibility to unwrap it for perf reasons.
  114. LPITEMIDLIST FullPidlFromSMData(LPSMDATA psmd)
  115. {
  116. LPITEMIDLIST pidlItem;
  117. LPITEMIDLIST pidlFolder = NULL;
  118. LPITEMIDLIST pidlFull = NULL;
  119. IAugmentedShellFolder2* pasf2;
  120. if (SUCCEEDED(psmd->psf->QueryInterface(IID_PPV_ARG(IAugmentedShellFolder2, &pasf2))))
  121. {
  122. if (SUCCEEDED(pasf2->UnWrapIDList(psmd->pidlItem, 1, NULL, &pidlFolder, &pidlItem, NULL)))
  123. {
  124. pidlFull = ILCombine(pidlFolder, pidlItem);
  125. ILFree(pidlFolder);
  126. ILFree(pidlItem);
  127. }
  128. pasf2->Release();
  129. }
  130. if (!pidlFolder)
  131. {
  132. pidlFull = ILCombine(psmd->pidlFolder, psmd->pidlItem);
  133. }
  134. return pidlFull;
  135. }
  136. //
  137. // Determine whether a namespace pidl in a merged shellfolder came
  138. // from the specified object GUID.
  139. //
  140. BOOL IsMergedFolderGUID(IShellFolder *psf, LPCITEMIDLIST pidl, REFGUID rguid)
  141. {
  142. IAugmentedShellFolder* pasf;
  143. BOOL fMatch = FALSE;
  144. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IAugmentedShellFolder, &pasf))))
  145. {
  146. GUID guid;
  147. if (SUCCEEDED(pasf->GetNameSpaceID(pidl, &guid)))
  148. {
  149. fMatch = IsEqualGUID(guid, rguid);
  150. }
  151. pasf->Release();
  152. }
  153. return fMatch;
  154. }
  155. STDMETHODIMP_(int) s_DarwinAdsDestroyCallback(LPVOID pData1, LPVOID pData2)
  156. {
  157. CDarwinAd* pda = (CDarwinAd*)pData1;
  158. if (pda)
  159. delete pda;
  160. return TRUE;
  161. }
  162. // SHRegisterDarwinLink takes ownership of the pidl
  163. BOOL SHRegisterDarwinLink(LPITEMIDLIST pidlFull, LPWSTR pszDarwinID, BOOL fUpdate)
  164. {
  165. BOOL fRetVal = FALSE;
  166. ENTERCRITICAL_DARWINADS;
  167. if (pidlFull)
  168. {
  169. CDarwinAd *pda = NULL;
  170. if (GetDarwinIndex(pidlFull, &pda) != -1 && pda)
  171. {
  172. // We already know about this link; don't need to add it
  173. fRetVal = TRUE;
  174. }
  175. else
  176. {
  177. pda = new CDarwinAd(pidlFull, pszDarwinID);
  178. if (pda)
  179. {
  180. pidlFull = NULL; // take ownership
  181. // Do we have a global cache?
  182. if (g_hdpaDarwinAds == NULL)
  183. {
  184. // No; This is either the first time this is called, or we
  185. // failed the last time.
  186. g_hdpaDarwinAds = DPA_Create(5);
  187. }
  188. if (g_hdpaDarwinAds)
  189. {
  190. // DPA_AppendPtr returns the zero based index it inserted it at.
  191. if(DPA_AppendPtr(g_hdpaDarwinAds, (void*)pda) >= 0)
  192. {
  193. fRetVal = TRUE;
  194. }
  195. }
  196. }
  197. }
  198. if (!fRetVal)
  199. {
  200. // if we failed to create a dpa, delete this.
  201. delete pda;
  202. }
  203. else if (fUpdate)
  204. {
  205. // update the entry if requested
  206. pda->CheckInstalled();
  207. }
  208. ILFree(pidlFull);
  209. }
  210. else if (!pszDarwinID)
  211. {
  212. // NULL, NULL means "destroy darwin info, we're shutting down"
  213. HDPA hdpa = g_hdpaDarwinAds;
  214. g_hdpaDarwinAds = NULL;
  215. if (hdpa)
  216. DPA_DestroyCallback(hdpa, s_DarwinAdsDestroyCallback, NULL);
  217. }
  218. LEAVECRITICAL_DARWINADS;
  219. return fRetVal;
  220. }
  221. BOOL ProcessDarwinAd(IShellLinkDataList* psldl, LPCITEMIDLIST pidlFull)
  222. {
  223. // This function does not check for the existance of a member before adding it,
  224. // so it is entirely possible for there to be duplicates in the list....
  225. BOOL fIsLoaded = FALSE;
  226. BOOL fFreesldl = FALSE;
  227. BOOL fRetVal = FALSE;
  228. if (!psldl)
  229. {
  230. // We will detect failure of this at use time.
  231. if (FAILED(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellLinkDataList, &psldl))))
  232. {
  233. return FALSE;
  234. }
  235. fFreesldl = TRUE;
  236. IPersistFile* ppf;
  237. OLECHAR sz[MAX_PATH];
  238. if (SHGetPathFromIDListW(pidlFull, sz))
  239. {
  240. if (SUCCEEDED(psldl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf))))
  241. {
  242. // shelllink's job to load correctly.
  243. if (SUCCEEDED(ppf->Load(sz, 0)))
  244. {
  245. fIsLoaded = TRUE;
  246. }
  247. ppf->Release();
  248. }
  249. }
  250. }
  251. else
  252. fIsLoaded = TRUE;
  253. CDarwinAd* pda = NULL;
  254. if (fIsLoaded)
  255. {
  256. EXP_DARWIN_LINK* pexpDarwin;
  257. if (SUCCEEDED(psldl->CopyDataBlock(EXP_DARWIN_ID_SIG, (void**)&pexpDarwin)))
  258. {
  259. fRetVal = SHRegisterDarwinLink(ILClone(pidlFull), pexpDarwin->szwDarwinID, TRUE);
  260. LocalFree(pexpDarwin);
  261. }
  262. }
  263. if (fFreesldl)
  264. psldl->Release();
  265. return fRetVal;
  266. }
  267. // This routine creates the IShellFolder and pidl for one of the many
  268. // merged folders on the Start Menu / Start Panel.
  269. typedef struct {
  270. UINT csidl;
  271. UINT uANSFlags; // Flags for AddNameSpace
  272. LPCGUID pguidObj; // optional object tag
  273. } MERGEDFOLDERINFO, *LPMERGEDFOLDERINFO;
  274. typedef const MERGEDFOLDERINFO *LPCMERGEDFOLDERINFO;
  275. HRESULT GetMergedFolder(IShellFolder **ppsf, LPITEMIDLIST *ppidl,
  276. LPCMERGEDFOLDERINFO rgmfi, UINT cmfi)
  277. {
  278. *ppidl = NULL;
  279. *ppsf = NULL;
  280. IShellFolder2 *psf;
  281. IAugmentedShellFolder2 *pasf;
  282. HRESULT hr = CoCreateInstance(CLSID_MergedFolder, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IAugmentedShellFolder2, &pasf));
  283. for (UINT imfi = 0; SUCCEEDED(hr) && imfi < cmfi; imfi++)
  284. {
  285. // If this is a common group and common groups are restricted, then
  286. // skip this item
  287. if ((rgmfi[imfi].uANSFlags & ASFF_COMMON) &&
  288. SHRestricted(REST_NOCOMMONGROUPS))
  289. {
  290. continue;
  291. }
  292. psf = NULL; // in/out param below
  293. hr = SHCacheTrackingFolder(MAKEINTIDLIST(rgmfi[imfi].csidl), rgmfi[imfi].csidl, &psf);
  294. if (SUCCEEDED(hr))
  295. {
  296. // If this is a Start Menu folder, then apply the
  297. // "do not enumerate subfolders" restriction if the policy says so.
  298. // In which case, we cannot use the tracking folder cache.
  299. // (Perf note: We compare pointers directly.)
  300. if (rgmfi[imfi].pguidObj == &CLSID_StartMenu)
  301. {
  302. if (SHRestricted(REST_NOSTARTMENUSUBFOLDERS))
  303. {
  304. ISetFolderEnumRestriction *prest;
  305. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(ISetFolderEnumRestriction, &prest))))
  306. {
  307. prest->SetEnumRestriction(0, SHCONTF_FOLDERS); // disallow subfolders
  308. prest->Release();
  309. }
  310. }
  311. }
  312. else
  313. {
  314. // If this assert fires, then our perf optimization above failed.
  315. ASSERT(rgmfi[imfi].pguidObj == NULL ||
  316. !IsEqualGUID(*rgmfi[imfi].pguidObj, CLSID_StartMenu));
  317. }
  318. hr = pasf->AddNameSpace(rgmfi[imfi].pguidObj, psf, NULL, rgmfi[imfi].uANSFlags);
  319. if (SUCCEEDED(hr))
  320. {
  321. if (rgmfi[imfi].uANSFlags & ASFF_DEFNAMESPACE_DISPLAYNAME)
  322. {
  323. // If this assert fires, it means somebody marked two
  324. // folders as ASFF_DEFNAMESPACE_DISPLAYNAME, which is
  325. // illegal (you can have only one default)
  326. ASSERT(*ppidl == NULL);
  327. hr = SHGetIDListFromUnk(psf, ppidl); // copy out the pidl for this guy
  328. }
  329. }
  330. psf->Release();
  331. }
  332. }
  333. if (SUCCEEDED(hr))
  334. *ppsf = pasf; // copy out the ref
  335. else
  336. ATOMICRELEASE(pasf);
  337. return hr;
  338. }
  339. HRESULT CreateMergedFolderHelper(LPCMERGEDFOLDERINFO rgmfi, UINT cmfi, REFIID riid, void **ppv)
  340. {
  341. IShellFolder *psf;
  342. LPITEMIDLIST pidl;
  343. HRESULT hr = GetMergedFolder(&psf, &pidl, rgmfi, cmfi);
  344. if (SUCCEEDED(hr))
  345. {
  346. hr = psf->QueryInterface(riid, ppv);
  347. if (SUCCEEDED(hr))
  348. {
  349. IPersistPropertyBag *pppb;
  350. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IPersistPropertyBag, &pppb))))
  351. {
  352. IPropertyBag *ppb;
  353. if (SUCCEEDED(SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_PPV_ARG(IPropertyBag, &ppb))))
  354. {
  355. // these merged folders have to be told to use new changenotification
  356. SHPropertyBag_WriteBOOL(ppb, L"MergedFolder\\ShellView", TRUE);
  357. pppb->Load(ppb, NULL);
  358. ppb->Release();
  359. }
  360. pppb->Release();
  361. }
  362. }
  363. psf->Release();
  364. ILFree(pidl);
  365. }
  366. return hr;
  367. }
  368. const MERGEDFOLDERINFO c_rgmfiStartMenu[] = {
  369. { CSIDL_STARTMENU | CSIDL_FLAG_CREATE, ASFF_DEFNAMESPACE_ALL, &CLSID_StartMenu },
  370. { CSIDL_COMMON_STARTMENU, ASFF_COMMON, &CLSID_StartMenu },
  371. };
  372. const MERGEDFOLDERINFO c_rgmfiProgramsFolder[] = {
  373. { CSIDL_PROGRAMS | CSIDL_FLAG_CREATE, ASFF_DEFNAMESPACE_ALL, NULL },
  374. { CSIDL_COMMON_PROGRAMS, ASFF_COMMON, NULL },
  375. };
  376. //
  377. // On the Start Panel, we want the fast items to sort above the Programs,
  378. // so we mark the Programs folders as ASFF_SORTDOWN so they go to the bottom.
  379. // We also list the Fast Items first so SMSET_SEPARATEMERGEFOLDER picks
  380. // them off properly. And we only want to let Start Menu merge with
  381. // Common Start Menu (and Programs with Common Programs) so pass
  382. // ASFF_MERGESAMEGUID.
  383. const MERGEDFOLDERINFO c_rgmfiProgramsFolderAndFastItems[] = {
  384. { CSIDL_STARTMENU | CSIDL_FLAG_CREATE, ASFF_DEFAULT | ASFF_MERGESAMEGUID, &CLSID_StartMenu},
  385. { CSIDL_COMMON_STARTMENU, ASFF_COMMON | ASFF_MERGESAMEGUID, &CLSID_StartMenu},
  386. { CSIDL_PROGRAMS | CSIDL_FLAG_CREATE, ASFF_DEFNAMESPACE_ALL | ASFF_MERGESAMEGUID | ASFF_SORTDOWN, NULL },
  387. { CSIDL_COMMON_PROGRAMS, ASFF_COMMON | ASFF_MERGESAMEGUID | ASFF_SORTDOWN, NULL },
  388. };
  389. STDAPI CStartMenuFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  390. {
  391. return CreateMergedFolderHelper(c_rgmfiStartMenu, ARRAYSIZE(c_rgmfiStartMenu), riid, ppv);
  392. }
  393. STDAPI CProgramsFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  394. {
  395. return CreateMergedFolderHelper(c_rgmfiProgramsFolder, ARRAYSIZE(c_rgmfiProgramsFolder), riid, ppv);
  396. }
  397. HRESULT GetFilesystemInfo(IShellFolder* psf, LPITEMIDLIST* ppidlRoot, int* pcsidl)
  398. {
  399. ASSERT(psf);
  400. IPersistFolder3* ppf;
  401. HRESULT hr = E_FAIL;
  402. *pcsidl = 0;
  403. *ppidlRoot = 0;
  404. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf))))
  405. {
  406. PERSIST_FOLDER_TARGET_INFO pfti = {0};
  407. if (SUCCEEDED(ppf->GetFolderTargetInfo(&pfti)))
  408. {
  409. *pcsidl = pfti.csidl;
  410. if (-1 != pfti.csidl)
  411. hr = S_OK;
  412. ILFree(pfti.pidlTargetFolder);
  413. }
  414. if (SUCCEEDED(hr))
  415. hr = ppf->GetCurFolder(ppidlRoot);
  416. ppf->Release();
  417. }
  418. return hr;
  419. }
  420. HRESULT ExecStaticStartMenuItem(int idCmd, BOOL fAllUsers, BOOL fOpen)
  421. {
  422. int csidl = -1;
  423. HRESULT hr = E_OUTOFMEMORY;
  424. SHELLEXECUTEINFO shei = {0};
  425. switch (idCmd)
  426. {
  427. case IDM_PROGRAMS: csidl = fAllUsers ? CSIDL_COMMON_PROGRAMS : CSIDL_PROGRAMS; break;
  428. case IDM_FAVORITES: csidl = CSIDL_FAVORITES; break;
  429. case IDM_MYDOCUMENTS: csidl = CSIDL_PERSONAL; break;
  430. case IDM_MYPICTURES: csidl = CSIDL_MYPICTURES; break;
  431. case IDM_CONTROLS: csidl = CSIDL_CONTROLS; break;
  432. case IDM_PRINTERS: csidl = CSIDL_PRINTERS; break;
  433. case IDM_NETCONNECT: csidl = CSIDL_CONNECTIONS; break;
  434. default:
  435. return E_FAIL;
  436. }
  437. if (csidl != -1)
  438. {
  439. SHGetFolderLocation(NULL, csidl, NULL, 0, (LPITEMIDLIST*)&shei.lpIDList);
  440. }
  441. if (shei.lpIDList)
  442. {
  443. shei.cbSize = sizeof(shei);
  444. shei.fMask = SEE_MASK_IDLIST;
  445. shei.nShow = SW_SHOWNORMAL;
  446. shei.lpVerb = fOpen ? TEXT("open") : TEXT("explore");
  447. hr = ShellExecuteEx(&shei) ? S_OK: E_FAIL; // only opening and exploring special folder pidl
  448. ILFree((LPITEMIDLIST)shei.lpIDList);
  449. }
  450. return hr;
  451. }
  452. //
  453. // Base class for Classic and Personal start menus.
  454. //
  455. class CStartMenuCallbackBase : public IShellMenuCallback,
  456. public CObjectWithSite
  457. {
  458. public:
  459. // *** IUnknown methods ***
  460. STDMETHODIMP QueryInterface (REFIID riid, void ** ppvObj);
  461. STDMETHODIMP_(ULONG) AddRef();
  462. STDMETHODIMP_(ULONG) Release();
  463. // derived class is expected to implement IShellMenuCallback
  464. // IObjectWithSite inherited from CObjectWithSite
  465. protected:
  466. CStartMenuCallbackBase(BOOL fIsStartPanel = FALSE);
  467. ~CStartMenuCallbackBase();
  468. void _InitializePrograms();
  469. HRESULT _FilterPidl(UINT uParent, IShellFolder* psf, LPCITEMIDLIST pidl);
  470. HRESULT _Promote(LPSMDATA psmd, DWORD dwFlags);
  471. BOOL _IsTopLevelStartMenu(UINT uParent, IShellFolder *psf, LPCITEMIDLIST pidl);
  472. HRESULT _HandleNew(LPSMDATA psmd);
  473. HRESULT _GetSFInfo(SMDATA* psmd, SMINFO* psminfo);
  474. HRESULT _ProcessChangeNotify(SMDATA* psmd, LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  475. HRESULT InitializeProgramsShellMenu(IShellMenu* psm);
  476. virtual DWORD _GetDemote(SMDATA* psmd) { return 0; }
  477. BOOL _IsDarwinAdvertisement(LPCITEMIDLIST pidlFull);
  478. void _RefreshSettings();
  479. protected:
  480. int _cRef;
  481. DEBUG_CODE( DWORD _dwThreadID; ) // Cache the thread of the object
  482. LPTSTR _pszPrograms;
  483. LPTSTR _pszWindowsUpdate;
  484. LPTSTR _pszConfigurePrograms;
  485. LPTSTR _pszAdminTools;
  486. ITrayPriv2* _ptp2;
  487. BOOL _fExpandoMenus;
  488. BOOL _fShowAdminTools;
  489. BOOL _fIsStartPanel;
  490. BOOL _fInitPrograms;
  491. };
  492. // IShellMenuCallback implementation
  493. class CStartMenuCallback : public CStartMenuCallbackBase
  494. {
  495. public:
  496. // *** IUnknown methods *** inherited from CStartMenuBase
  497. // *** IShellMenuCallback methods ***
  498. STDMETHODIMP CallbackSM(LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  499. // *** IObjectWithSite methods *** (overriding CObjectWithSite)
  500. STDMETHODIMP SetSite(IUnknown* punk);
  501. STDMETHODIMP GetSite(REFIID riid, void** ppvOut);
  502. CStartMenuCallback();
  503. private:
  504. virtual ~CStartMenuCallback();
  505. IContextMenu* _pcmFind;
  506. ITrayPriv* _ptp;
  507. IUnknown* _punkSite;
  508. IOleCommandTarget* _poct;
  509. BITBOOL _fAddOpenFolder: 1;
  510. BITBOOL _fCascadeMyDocuments: 1;
  511. BITBOOL _fCascadePrinters: 1;
  512. BITBOOL _fCascadeControlPanel: 1;
  513. BITBOOL _fFindMenuInvalid: 1;
  514. BITBOOL _fCascadeNetConnections: 1;
  515. BITBOOL _fShowInfoTip: 1;
  516. BITBOOL _fInitedShowTopLevelStartMenu: 1;
  517. BITBOOL _fCascadeMyPictures: 1;
  518. BITBOOL _fHasMyDocuments: 1;
  519. BITBOOL _fHasMyPictures: 1;
  520. TCHAR _szFindMnemonic[2];
  521. HWND _hwnd;
  522. IMruDataList * _pmruRecent;
  523. DWORD _cRecentDocs;
  524. DWORD _dwFlags;
  525. DWORD _dwChevronCount;
  526. HRESULT _ExecHmenuItem(LPSMDATA psmdata);
  527. HRESULT _Init(SMDATA* psmdata);
  528. HRESULT _Create(SMDATA* psmdata, void** pvUserData);
  529. HRESULT _Destroy(SMDATA* psmdata);
  530. HRESULT _GetHmenuInfo(SMDATA* psmd, SMINFO*sminfo);
  531. HRESULT _GetObject(LPSMDATA psmd, REFIID riid, void** ppvObj);
  532. HRESULT _CheckRestricted(DWORD dwRestrict, BOOL* fRestricted);
  533. HRESULT _FilterRecentPidl(IShellFolder* psf, LPCITEMIDLIST pidl);
  534. HRESULT _Demote(LPSMDATA psmd);
  535. HRESULT _GetTip(LPWSTR pstrTitle, LPWSTR pstrTip);
  536. DWORD _GetDemote(SMDATA* psmd);
  537. HRESULT _HandleAccelerator(TCHAR ch, SMDATA* psmdata);
  538. HRESULT _GetDefaultIcon(LPWSTR psz, int* piIndex);
  539. void _GetStaticStartMenu(HMENU* phmenu, HWND* phwnd);
  540. HRESULT _GetStaticInfoTip(SMDATA* psmd, LPWSTR pszTip, int cch);
  541. // helper functions
  542. DWORD GetInitFlags();
  543. void SetInitFlags(DWORD dwFlags);
  544. HRESULT _InitializeFindMenu(IShellMenu* psm);
  545. HRESULT _ExecItem(LPSMDATA, UINT);
  546. HRESULT VerifyCSIDL(int idCmd, int csidl, IShellMenu* psm);
  547. HRESULT VerifyMergedGuy(BOOL fPrograms, IShellMenu* psm);
  548. void _UpdateDocsMenuItemNames(IShellMenu* psm);
  549. void _UpdateDocumentsShellMenu(IShellMenu* psm);
  550. public: // Make these public to this file. This is for the CreateInstance
  551. // Sub Menu creation
  552. HRESULT InitializeFastItemsShellMenu(IShellMenu* psm);
  553. HRESULT InitializeCSIDLShellMenu(int uId, int csidl, LPTSTR pszRoot, LPTSTR pszValue,
  554. DWORD dwPassInitFlags, DWORD dwSetFlags, BOOL fAddOpen,
  555. IShellMenu* psm);
  556. HRESULT InitializeDocumentsShellMenu(IShellMenu* psm);
  557. HRESULT InitializeSubShellMenu(int idCmd, IShellMenu* psm);
  558. };
  559. class CStartContextMenu : IContextMenu
  560. {
  561. public:
  562. // IUnknown
  563. STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
  564. STDMETHOD_(ULONG,AddRef)(void);
  565. STDMETHOD_(ULONG,Release)(void);
  566. // IContextMenu
  567. STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  568. STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpici);
  569. STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax);
  570. CStartContextMenu(int idCmd) : _idCmd(idCmd), _cRef(1) {};
  571. private:
  572. int _cRef;
  573. virtual ~CStartContextMenu() {};
  574. int _idCmd;
  575. };
  576. void CStartMenuCallbackBase::_RefreshSettings()
  577. {
  578. _fShowAdminTools = FeatureEnabled(TEXT("StartMenuAdminTools"));
  579. }
  580. CStartMenuCallbackBase::CStartMenuCallbackBase(BOOL fIsStartPanel)
  581. : _cRef(1), _fIsStartPanel(fIsStartPanel)
  582. {
  583. DEBUG_CODE( _dwThreadID = GetCurrentThreadId() );
  584. TCHAR szBuf[MAX_PATH];
  585. DWORD cbSize = sizeof(szBuf); // SHGetValue wants sizeof
  586. if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, REGSTR_EXPLORER_WINUPDATE, TEXT("ShortcutName"),
  587. NULL, szBuf, &cbSize))
  588. {
  589. // Add ".lnk" if the file doesn't have an extension
  590. PathAddExtension(szBuf, TEXT(".lnk"));
  591. Str_SetPtr(&_pszWindowsUpdate, szBuf);
  592. }
  593. cbSize = sizeof(szBuf); // SHGetValue wants sizeof
  594. if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, REGSTR_PATH_SETUP, TEXT("SM_ConfigureProgramsName"),
  595. NULL, szBuf, &cbSize))
  596. {
  597. PathAddExtension(szBuf, TEXT(".lnk"));
  598. Str_SetPtr(&_pszConfigurePrograms, szBuf);
  599. }
  600. if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_ADMINTOOLS | CSIDL_FLAG_CREATE, NULL, 0, szBuf)))
  601. {
  602. Str_SetPtr(&_pszAdminTools, PathFindFileName(szBuf));
  603. }
  604. _RefreshSettings();
  605. SHReValidateDarwinCache();
  606. }
  607. CStartMenuCallback::CStartMenuCallback() : _cRecentDocs(-1)
  608. {
  609. LoadString(g_hinst, IDS_FIND_MNEMONIC, _szFindMnemonic, ARRAYSIZE(_szFindMnemonic));
  610. }
  611. CStartMenuCallbackBase::~CStartMenuCallbackBase()
  612. {
  613. ASSERT( _dwThreadID == GetCurrentThreadId() );
  614. Str_SetPtr(&_pszWindowsUpdate, NULL);
  615. Str_SetPtr(&_pszConfigurePrograms, NULL);
  616. Str_SetPtr(&_pszAdminTools, NULL);
  617. Str_SetPtr(&_pszPrograms, NULL);
  618. ATOMICRELEASE(_ptp2);
  619. }
  620. CStartMenuCallback::~CStartMenuCallback()
  621. {
  622. ATOMICRELEASE(_pcmFind);
  623. ATOMICRELEASE(_ptp);
  624. ATOMICRELEASE(_pmruRecent);
  625. }
  626. // *** IUnknown methods ***
  627. STDMETHODIMP CStartMenuCallbackBase::QueryInterface(REFIID riid, void ** ppvObj)
  628. {
  629. static const QITAB qit[] =
  630. {
  631. QITABENT(CStartMenuCallbackBase, IShellMenuCallback),
  632. QITABENT(CStartMenuCallbackBase, IObjectWithSite),
  633. { 0 },
  634. };
  635. return QISearch(this, qit, riid, ppvObj);
  636. }
  637. STDMETHODIMP_(ULONG) CStartMenuCallbackBase::AddRef()
  638. {
  639. return ++_cRef;
  640. }
  641. STDMETHODIMP_(ULONG) CStartMenuCallbackBase::Release()
  642. {
  643. ASSERT(_cRef > 0);
  644. _cRef--;
  645. if( _cRef > 0)
  646. return _cRef;
  647. delete this;
  648. return 0;
  649. }
  650. STDMETHODIMP CStartMenuCallback::SetSite(IUnknown* punk)
  651. {
  652. ATOMICRELEASE(_punkSite);
  653. _punkSite = punk;
  654. if (punk)
  655. {
  656. _punkSite->AddRef();
  657. }
  658. return S_OK;
  659. }
  660. STDMETHODIMP CStartMenuCallback::GetSite(REFIID riid, void**ppvOut)
  661. {
  662. if (_ptp)
  663. return _ptp->QueryInterface(riid, ppvOut);
  664. else
  665. return E_NOINTERFACE;
  666. }
  667. #ifdef DEBUG
  668. void DBUEMQueryEvent(const IID *pguidGrp, int eCmd, WPARAM wParam, LPARAM lParam)
  669. {
  670. #if 1
  671. return;
  672. #else
  673. UEMINFO uei;
  674. uei.cbSize = sizeof(uei);
  675. uei.dwMask = ~0; // UEIM_HIT etc.
  676. UEMQueryEvent(pguidGrp, eCmd, wParam, lParam, &uei);
  677. TCHAR szBuf[20];
  678. wnsprintf(szBuf, ARRAYSIZE(szBuf), TEXT("hit=%d"), uei.cHit);
  679. MessageBox(NULL, szBuf, TEXT("UEM"), MB_OKCANCEL);
  680. return;
  681. #endif
  682. }
  683. #endif
  684. DWORD CStartMenuCallback::GetInitFlags()
  685. {
  686. DWORD dwType;
  687. DWORD cbSize = sizeof(DWORD);
  688. DWORD dwFlags = 0;
  689. SHGetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED, TEXT("StartMenuInit"),
  690. &dwType, (BYTE*)&dwFlags, &cbSize);
  691. return dwFlags;
  692. }
  693. void CStartMenuCallback::SetInitFlags(DWORD dwFlags)
  694. {
  695. SHSetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED, TEXT("StartMenuInit"), REG_DWORD, &dwFlags, sizeof(DWORD));
  696. }
  697. DWORD GetClickCount()
  698. {
  699. //This function retrieves the number of times the user has clicked on the chevron item.
  700. DWORD dwType;
  701. DWORD cbSize = sizeof(DWORD);
  702. DWORD dwCount = 1; // Default to three clicks before we give up.
  703. // PMs what it to 1 now. Leaving back end in case they change their mind.
  704. SHGetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED, TEXT("StartMenuChevron"),
  705. &dwType, (BYTE*)&dwCount, &cbSize);
  706. return dwCount;
  707. }
  708. void SetClickCount(DWORD dwClickCount)
  709. {
  710. SHSetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED, TEXT("StartMenuChevron"), REG_DWORD, &dwClickCount, sizeof(dwClickCount));
  711. }
  712. BOOL CStartMenuCallbackBase::_IsTopLevelStartMenu(UINT uParent, IShellFolder *psf, LPCITEMIDLIST pidl)
  713. {
  714. return uParent == IDM_TOPLEVELSTARTMENU ||
  715. (uParent == IDM_PROGRAMS && _fIsStartPanel && IsMergedFolderGUID(psf, pidl, CLSID_StartMenu));
  716. };
  717. STDMETHODIMP CStartMenuCallback::CallbackSM(LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  718. {
  719. HRESULT hr = S_FALSE;
  720. switch (uMsg)
  721. {
  722. case SMC_CREATE:
  723. hr = _Create(psmd, (void**)lParam);
  724. break;
  725. case SMC_DESTROY:
  726. hr = _Destroy(psmd);
  727. break;
  728. case SMC_INITMENU:
  729. hr = _Init(psmd);
  730. break;
  731. case SMC_SFEXEC:
  732. hr = _ExecItem(psmd, uMsg);
  733. break;
  734. case SMC_EXEC:
  735. hr = _ExecHmenuItem(psmd);
  736. break;
  737. case SMC_GETOBJECT:
  738. hr = _GetObject(psmd, (GUID)*((GUID*)wParam), (void**)lParam);
  739. break;
  740. case SMC_GETINFO:
  741. hr = _GetHmenuInfo(psmd, (SMINFO*)lParam);
  742. break;
  743. case SMC_GETSFINFOTIP:
  744. if (!_fShowInfoTip)
  745. hr = E_FAIL; // E_FAIL means don't show. S_FALSE means show default
  746. break;
  747. case SMC_GETINFOTIP:
  748. hr = _GetStaticInfoTip(psmd, (LPWSTR)wParam, (int)lParam);
  749. break;
  750. case SMC_GETSFINFO:
  751. hr = _GetSFInfo(psmd, (SMINFO*)lParam);
  752. break;
  753. case SMC_BEGINENUM:
  754. if (psmd->uIdParent == IDM_RECENT)
  755. {
  756. ASSERT(_cRecentDocs == -1);
  757. ASSERT(!_pmruRecent);
  758. CreateRecentMRUList(&_pmruRecent);
  759. _cRecentDocs = 0;
  760. hr = S_OK;
  761. }
  762. break;
  763. case SMC_ENDENUM:
  764. if (psmd->uIdParent == IDM_RECENT)
  765. {
  766. ASSERT(_cRecentDocs != -1);
  767. ATOMICRELEASE(_pmruRecent);
  768. _cRecentDocs = -1;
  769. hr = S_OK;
  770. }
  771. break;
  772. case SMC_DUMPONUPDATE:
  773. if (psmd->uIdParent == IDM_RECENT)
  774. {
  775. hr = S_OK;
  776. }
  777. break;
  778. case SMC_FILTERPIDL:
  779. ASSERT(psmd->dwMask & SMDM_SHELLFOLDER);
  780. if (psmd->uIdParent == IDM_RECENT)
  781. {
  782. // we need to filter out all but the first MAXRECENTITEMS
  783. // and no folders allowed!
  784. hr = _FilterRecentPidl(psmd->psf, psmd->pidlItem);
  785. }
  786. else
  787. {
  788. hr = _FilterPidl(psmd->uIdParent, psmd->psf, psmd->pidlItem);
  789. }
  790. break;
  791. case SMC_INSERTINDEX:
  792. ASSERT(lParam && IS_VALID_WRITE_PTR(lParam, int));
  793. *((int*)lParam) = 0;
  794. hr = S_OK;
  795. break;
  796. case SMC_SHCHANGENOTIFY:
  797. {
  798. PSMCSHCHANGENOTIFYSTRUCT pshf = (PSMCSHCHANGENOTIFYSTRUCT)lParam;
  799. hr = _ProcessChangeNotify(psmd, pshf->lEvent, pshf->pidl1, pshf->pidl2);
  800. }
  801. break;
  802. case SMC_REFRESH:
  803. if (psmd->uIdParent == IDM_TOPLEVELSTARTMENU)
  804. {
  805. hr = S_OK;
  806. // Refresh is only called on the top level.
  807. HMENU hmenu;
  808. IShellMenu* psm;
  809. _GetStaticStartMenu(&hmenu, &_hwnd);
  810. if (hmenu && psmd->punk && SUCCEEDED(psmd->punk->QueryInterface(IID_PPV_ARG(IShellMenu, &psm))))
  811. {
  812. hr = psm->SetMenu(hmenu, _hwnd, SMSET_BOTTOM | SMSET_MERGE);
  813. psm->Release();
  814. }
  815. _RefreshSettings();
  816. _fExpandoMenus = !_fIsStartPanel && AreIntelliMenusEnabled();
  817. _fCascadeMyDocuments = FeatureEnabled(TEXT("CascadeMyDocuments"));
  818. _fCascadePrinters = FeatureEnabled(TEXT("CascadePrinters"));
  819. _fCascadeControlPanel = FeatureEnabled(TEXT("CascadeControlPanel"));
  820. _fCascadeNetConnections = FeatureEnabled(TEXT("CascadeNetworkConnections"));
  821. _fAddOpenFolder = FeatureEnabled(TEXT("StartMenuOpen"));
  822. _fShowInfoTip = FeatureEnabled(TEXT("ShowInfoTip"));
  823. _fCascadeMyPictures = FeatureEnabled(TEXT("CascadeMyPictures"));
  824. _fFindMenuInvalid = TRUE;
  825. _dwFlags = GetInitFlags();
  826. }
  827. break;
  828. case SMC_DEMOTE:
  829. hr = _Demote(psmd);
  830. break;
  831. case SMC_PROMOTE:
  832. hr = _Promote(psmd, (DWORD)wParam);
  833. break;
  834. case SMC_NEWITEM:
  835. hr = _HandleNew(psmd);
  836. break;
  837. case SMC_MAPACCELERATOR:
  838. hr = _HandleAccelerator((TCHAR)wParam, (SMDATA*)lParam);
  839. break;
  840. case SMC_DEFAULTICON:
  841. ASSERT(psmd->uIdAncestor == IDM_FAVORITES); // This is only valid for the Favorites menu
  842. hr = _GetDefaultIcon((LPWSTR)wParam, (int*)lParam);
  843. break;
  844. case SMC_GETMINPROMOTED:
  845. // Only do this for the programs menu
  846. if (psmd->uIdParent == IDM_PROGRAMS)
  847. *((int*)lParam) = 4; // 4 was choosen by RichSt 9.15.98
  848. break;
  849. case SMC_CHEVRONEXPAND:
  850. // Has the user already seen the chevron tip enough times? (We set the bit when the count goes to zero.
  851. if (!(_dwFlags & STARTMENU_CHEVRONCLICKED))
  852. {
  853. // No; Then get the current count from the registry. We set a default of 3, but an admin can set this
  854. // to -1, that would make it so that they user sees it all the time.
  855. DWORD dwClickCount = GetClickCount();
  856. if (dwClickCount > 0)
  857. {
  858. // Since they clicked, take one off.
  859. dwClickCount--;
  860. // Set it back in.
  861. SetClickCount(dwClickCount);
  862. }
  863. if (dwClickCount == 0)
  864. {
  865. // Ah, the user has seen the chevron tip enought times... Stop being annoying.
  866. _dwFlags |= STARTMENU_CHEVRONCLICKED;
  867. SetInitFlags(_dwFlags);
  868. }
  869. }
  870. hr = S_OK;
  871. break;
  872. case SMC_DISPLAYCHEVRONTIP:
  873. // We only want to see the tip on the top level programs case, no where else. We also don't
  874. // want to see it if they've had enough.
  875. if (psmd->uIdParent == IDM_PROGRAMS &&
  876. !(_dwFlags & STARTMENU_CHEVRONCLICKED) &&
  877. !SHRestricted(REST_NOSMBALLOONTIP))
  878. {
  879. hr = S_OK;
  880. }
  881. break;
  882. case SMC_CHEVRONGETTIP:
  883. if (!SHRestricted(REST_NOSMBALLOONTIP))
  884. hr = _GetTip((LPWSTR)wParam, (LPWSTR)lParam);
  885. break;
  886. }
  887. return hr;
  888. }
  889. // For the Favorites menu, since their icon handler is SO slow, we're going to fake the icon
  890. // and have it get the real ones on the background thread...
  891. HRESULT CStartMenuCallback::_GetDefaultIcon(LPWSTR psz, int* piIndex)
  892. {
  893. DWORD cchSize = MAX_PATH;
  894. HRESULT hr = AssocQueryString(0, ASSOCSTR_DEFAULTICON, TEXT("InternetShortcut"), NULL, psz, &cchSize);
  895. if (SUCCEEDED(hr))
  896. {
  897. *piIndex = PathParseIconLocation(psz);
  898. }
  899. return hr;
  900. }
  901. HRESULT CStartMenuCallback::_ExecItem(LPSMDATA psmd, UINT uMsg)
  902. {
  903. ASSERT( _dwThreadID == GetCurrentThreadId() );
  904. return _ptp->ExecItem(psmd->psf, psmd->pidlItem);
  905. }
  906. HRESULT CStartMenuCallback::_Demote(LPSMDATA psmd)
  907. {
  908. //We want to for the UEM to demote pidlFolder,
  909. // then tell the Parent menuband (If there is one)
  910. // to invalidate this pidl.
  911. HRESULT hr = S_FALSE;
  912. if (_fExpandoMenus &&
  913. (psmd->uIdAncestor == IDM_PROGRAMS ||
  914. psmd->uIdAncestor == IDM_FAVORITES))
  915. {
  916. UEMINFO uei;
  917. uei.cbSize = sizeof(uei);
  918. uei.dwMask = UEIM_HIT;
  919. uei.cHit = 0;
  920. hr = UEMSetEvent(psmd->uIdAncestor == IDM_PROGRAMS? &UEMIID_SHELL : &UEMIID_BROWSER,
  921. UEME_RUNPIDL, (WPARAM)psmd->psf, (LPARAM)psmd->pidlItem, &uei);
  922. }
  923. return hr;
  924. }
  925. // Even if intellimenus are off, fire a UEM event if it was an Exec from
  926. // the More Programs menu of the Start Panel [SMINV_FORCE will be set]
  927. // so we can detect which are the user's most popular apps.
  928. HRESULT CStartMenuCallbackBase::_Promote(LPSMDATA psmd, DWORD dwFlags)
  929. {
  930. if ((_fExpandoMenus || (_fIsStartPanel && (dwFlags & SMINV_FORCE))) &&
  931. (psmd->uIdAncestor == IDM_PROGRAMS ||
  932. psmd->uIdAncestor == IDM_FAVORITES))
  933. {
  934. UEMFireEvent(psmd->uIdAncestor == IDM_PROGRAMS? &UEMIID_SHELL : &UEMIID_BROWSER,
  935. UEME_RUNPIDL, UEMF_XEVENT, (WPARAM)psmd->psf, (LPARAM)psmd->pidlItem);
  936. }
  937. return S_OK;
  938. }
  939. HRESULT CStartMenuCallbackBase::_HandleNew(LPSMDATA psmd)
  940. {
  941. HRESULT hr = S_FALSE;
  942. if (_fExpandoMenus &&
  943. (psmd->uIdAncestor == IDM_PROGRAMS ||
  944. psmd->uIdAncestor == IDM_FAVORITES))
  945. {
  946. UEMINFO uei;
  947. uei.cbSize = sizeof(uei);
  948. uei.dwMask = UEIM_HIT;
  949. uei.cHit = UEM_NEWITEMCOUNT;
  950. hr = UEMSetEvent(psmd->uIdAncestor == IDM_PROGRAMS? &UEMIID_SHELL : &UEMIID_BROWSER,
  951. UEME_RUNPIDL, (WPARAM)psmd->psf, (LPARAM)psmd->pidlItem, &uei);
  952. }
  953. if (psmd->uIdAncestor == IDM_PROGRAMS)
  954. {
  955. LPITEMIDLIST pidlFull = FullPidlFromSMData(psmd);
  956. if (pidlFull)
  957. {
  958. ProcessDarwinAd(NULL, pidlFull);
  959. ILFree(pidlFull);
  960. }
  961. }
  962. return hr;
  963. }
  964. HRESULT ShowFolder(UINT csidl)
  965. {
  966. LPITEMIDLIST pidl;
  967. if (SUCCEEDED(SHGetFolderLocation(NULL, csidl, NULL, 0, &pidl)))
  968. {
  969. SHELLEXECUTEINFO shei = { 0 };
  970. shei.cbSize = sizeof(shei);
  971. shei.fMask = SEE_MASK_IDLIST;
  972. shei.nShow = SW_SHOWNORMAL;
  973. shei.lpVerb = TEXT("open");
  974. shei.lpIDList = pidl;
  975. ShellExecuteEx(&shei); // only executing open on pidl
  976. ILFree(pidl);
  977. }
  978. return S_OK;
  979. }
  980. void _ExecRegValue(LPCTSTR pszValue)
  981. {
  982. TCHAR szPath[MAX_PATH];
  983. DWORD cbSize = sizeof(szPath);
  984. if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, REGSTR_EXPLORER_ADVANCED, pszValue,
  985. NULL, szPath, &cbSize))
  986. {
  987. SHELLEXECUTEINFO shei= { 0 };
  988. shei.cbSize = sizeof(shei);
  989. shei.nShow = SW_SHOWNORMAL;
  990. shei.lpParameters = PathGetArgs(szPath);
  991. PathRemoveArgs(szPath);
  992. shei.lpFile = szPath;
  993. ShellExecuteEx(&shei); // raw shellexec but info comes from HKLM
  994. }
  995. }
  996. HRESULT CStartMenuCallback::_ExecHmenuItem(LPSMDATA psmd)
  997. {
  998. HRESULT hr = S_FALSE;
  999. if (IsInRange(psmd->uId, TRAY_IDM_FINDFIRST, TRAY_IDM_FINDLAST) && _pcmFind)
  1000. {
  1001. CMINVOKECOMMANDINFOEX ici = { 0 };
  1002. ici.cbSize = sizeof(CMINVOKECOMMANDINFOEX);
  1003. ici.lpVerb = (LPSTR)MAKEINTRESOURCE(psmd->uId - TRAY_IDM_FINDFIRST);
  1004. ici.nShow = SW_NORMAL;
  1005. // record if shift or control was being held down
  1006. SetICIKeyModifiers(&ici.fMask);
  1007. _pcmFind->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
  1008. hr = S_OK;
  1009. }
  1010. else
  1011. {
  1012. switch (psmd->uId)
  1013. {
  1014. case IDM_OPEN_FOLDER:
  1015. switch(psmd->uIdParent)
  1016. {
  1017. case IDM_CONTROLS:
  1018. hr = ShowFolder(CSIDL_CONTROLS);
  1019. break;
  1020. case IDM_PRINTERS:
  1021. hr = ShowFolder(CSIDL_PRINTERS);
  1022. break;
  1023. case IDM_NETCONNECT:
  1024. hr = ShowFolder(CSIDL_CONNECTIONS);
  1025. break;
  1026. case IDM_MYPICTURES:
  1027. hr = ShowFolder(CSIDL_MYPICTURES);
  1028. break;
  1029. case IDM_MYDOCUMENTS:
  1030. hr = ShowFolder(CSIDL_PERSONAL);
  1031. break;
  1032. }
  1033. break;
  1034. case IDM_NETCONNECT:
  1035. hr = ShowFolder(CSIDL_CONNECTIONS);
  1036. break;
  1037. case IDM_MYDOCUMENTS:
  1038. hr = ShowFolder(CSIDL_PERSONAL);
  1039. break;
  1040. case IDM_MYPICTURES:
  1041. hr = ShowFolder(CSIDL_MYPICTURES);
  1042. break;
  1043. case IDM_CSC:
  1044. _ExecRegValue(TEXT("StartMenuSyncAll"));
  1045. break;
  1046. default:
  1047. hr = ExecStaticStartMenuItem(psmd->uId, FALSE, TRUE);
  1048. break;
  1049. }
  1050. }
  1051. return hr;
  1052. }
  1053. void CStartMenuCallback::_GetStaticStartMenu(HMENU* phmenu, HWND* phwnd)
  1054. {
  1055. *phmenu = NULL;
  1056. *phwnd = NULL;
  1057. IMenuPopup* pmp;
  1058. // The first one should be the bar that the start menu is sitting in.
  1059. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SMenuPopup, IID_PPV_ARG(IMenuPopup, &pmp))))
  1060. {
  1061. // Its site should be CStartMenuHost;
  1062. if (SUCCEEDED(IUnknown_GetSite(pmp, IID_PPV_ARG(ITrayPriv, &_ptp))))
  1063. {
  1064. // Don't get upset if this fails
  1065. _ptp->QueryInterface(IID_PPV_ARG(ITrayPriv2, &_ptp2));
  1066. _ptp->GetStaticStartMenu(phmenu);
  1067. IUnknown_GetWindow(_ptp, phwnd);
  1068. if (!_poct)
  1069. _ptp->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &_poct));
  1070. }
  1071. else
  1072. TraceMsg(TF_MENUBAND, "CStartMenuCallback::_SetSite : Failed to aquire CStartMenuHost");
  1073. pmp->Release();
  1074. }
  1075. }
  1076. HRESULT CStartMenuCallback::_Create(SMDATA* psmdata, void** ppvUserData)
  1077. {
  1078. *ppvUserData = new SMUSERDATA;
  1079. return S_OK;
  1080. }
  1081. HRESULT CStartMenuCallback::_Destroy(SMDATA* psmdata)
  1082. {
  1083. if (psmdata->pvUserData)
  1084. {
  1085. delete (SMUSERDATA*)psmdata->pvUserData;
  1086. psmdata->pvUserData = NULL;
  1087. }
  1088. return S_OK;
  1089. }
  1090. void CStartMenuCallbackBase::_InitializePrograms()
  1091. {
  1092. if (!_fInitPrograms)
  1093. {
  1094. // We're either initing these, or reseting them.
  1095. TCHAR szTemp[MAX_PATH];
  1096. SHGetFolderPath(NULL, CSIDL_PROGRAMS, NULL, 0, szTemp);
  1097. Str_SetPtr(&_pszPrograms, PathFindFileName(szTemp));
  1098. _fInitPrograms = TRUE;
  1099. }
  1100. }
  1101. // Given a CSIDL and a Shell menu, this will verify if the IShellMenu
  1102. // is pointing at the same place as the CSIDL is. If not, then it will
  1103. // update the shell menu to the new location.
  1104. HRESULT CStartMenuCallback::VerifyCSIDL(int idCmd, int csidl, IShellMenu* psm)
  1105. {
  1106. DWORD dwFlags;
  1107. LPITEMIDLIST pidl;
  1108. IShellFolder* psf;
  1109. HRESULT hr = S_OK;
  1110. if (SUCCEEDED(psm->GetShellFolder(&dwFlags, &pidl, IID_PPV_ARG(IShellFolder, &psf))))
  1111. {
  1112. psf->Release();
  1113. LPITEMIDLIST pidlCSIDL;
  1114. if (SUCCEEDED(SHGetFolderLocation(NULL, csidl, NULL, 0, &pidlCSIDL)))
  1115. {
  1116. // If the pidl of the IShellMenu is not equal to the
  1117. // SpecialFolder Location, then we need to update it so they are...
  1118. if (!ILIsEqual(pidlCSIDL, pidl))
  1119. {
  1120. hr = InitializeSubShellMenu(idCmd, psm);
  1121. }
  1122. ILFree(pidlCSIDL);
  1123. }
  1124. ILFree(pidl);
  1125. }
  1126. return hr;
  1127. }
  1128. // This code special cases the Programs and Fast items shell menus. It
  1129. // understands Merging and will check both shell folders in a merged case
  1130. // to verify that the shell folder is still pointing at that location
  1131. HRESULT CStartMenuCallback::VerifyMergedGuy(BOOL fPrograms, IShellMenu* psm)
  1132. {
  1133. DWORD dwFlags;
  1134. LPITEMIDLIST pidl;
  1135. HRESULT hr = S_OK;
  1136. IAugmentedShellFolder2* pasf;
  1137. if (SUCCEEDED(psm->GetShellFolder(&dwFlags, &pidl, IID_PPV_ARG(IAugmentedShellFolder2, &pasf))))
  1138. {
  1139. IShellFolder* psf;
  1140. // There are 2 things in the merged namespace: CSIDL_PROGRAMS and CSIDL_COMMON_PROGRAMS
  1141. for (int i = 0; i < 2; i++)
  1142. {
  1143. if (SUCCEEDED(pasf->QueryNameSpace(i, 0, &psf)))
  1144. {
  1145. int csidl;
  1146. LPITEMIDLIST pidlFolder;
  1147. if (SUCCEEDED(GetFilesystemInfo(psf, &pidlFolder, &csidl)))
  1148. {
  1149. LPITEMIDLIST pidlCSIDL;
  1150. if (SUCCEEDED(SHGetFolderLocation(NULL, csidl, NULL, 0, &pidlCSIDL)))
  1151. {
  1152. // If the pidl of the IShellMenu is not equal to the
  1153. // SpecialFolder Location, then we need to update it so they are...
  1154. if (!ILIsEqual(pidlCSIDL, pidlFolder))
  1155. {
  1156. // Since one of these things has changed,
  1157. // we need to update the string cache
  1158. // so that we do proper filtering of
  1159. // the programs item.
  1160. _fInitPrograms = FALSE;
  1161. if (fPrograms)
  1162. hr = InitializeProgramsShellMenu(psm);
  1163. else
  1164. hr = InitializeFastItemsShellMenu(psm);
  1165. i = 100; // break out of the loop.
  1166. }
  1167. ILFree(pidlCSIDL);
  1168. }
  1169. ILFree(pidlFolder);
  1170. }
  1171. psf->Release();
  1172. }
  1173. }
  1174. ILFree(pidl);
  1175. pasf->Release();
  1176. }
  1177. return hr;
  1178. }
  1179. void _FixMenuItemName(IShellMenu *psm, UINT uID, LPTSTR pszNewMenuName)
  1180. {
  1181. HMENU hMenu;
  1182. ASSERT(NULL != psm);
  1183. if (SUCCEEDED(psm->GetMenu(&hMenu, NULL, NULL)))
  1184. {
  1185. MENUITEMINFO mii = {0};
  1186. TCHAR szMenuName[256];
  1187. mii.cbSize = sizeof(mii);
  1188. mii.fMask = MIIM_TYPE;
  1189. mii.dwTypeData = szMenuName;
  1190. mii.cch = ARRAYSIZE(szMenuName);
  1191. szMenuName[0] = TEXT('\0');
  1192. if (::GetMenuItemInfo(hMenu, uID, FALSE, &mii))
  1193. {
  1194. if (0 != StrCmp(szMenuName, pszNewMenuName))
  1195. {
  1196. // The mydocs name has changed, update the menu item:
  1197. mii.dwTypeData = pszNewMenuName;
  1198. if (::SetMenuItemInfo(hMenu, uID, FALSE, &mii))
  1199. {
  1200. SMDATA smd;
  1201. smd.dwMask = SMDM_HMENU;
  1202. smd.uId = uID;
  1203. psm->InvalidateItem(&smd, SMINV_ID | SMINV_REFRESH);
  1204. }
  1205. }
  1206. }
  1207. }
  1208. }
  1209. void CStartMenuCallback::_UpdateDocumentsShellMenu(IShellMenu* psm)
  1210. {
  1211. // Add/Remove My Documents and My Pictures items of menu
  1212. BOOL fMyDocs = !SHRestricted(REST_NOSMMYDOCS);
  1213. if (fMyDocs)
  1214. {
  1215. LPITEMIDLIST pidl;
  1216. fMyDocs = SUCCEEDED(SHGetFolderLocation(NULL, CSIDL_PERSONAL, NULL, 0, &pidl));
  1217. if (fMyDocs)
  1218. ILFree(pidl);
  1219. }
  1220. BOOL fMyPics = !SHRestricted(REST_NOSMMYPICS);
  1221. if (fMyPics)
  1222. {
  1223. LPITEMIDLIST pidl;
  1224. fMyPics = SUCCEEDED(SHGetFolderLocation(NULL, CSIDL_MYPICTURES, NULL, 0, &pidl));
  1225. if (fMyPics)
  1226. ILFree(pidl);
  1227. }
  1228. // Do not update menu if not different than currently have
  1229. if (fMyDocs != (BOOL)_fHasMyDocuments || fMyPics != (BOOL)_fHasMyPictures)
  1230. {
  1231. HMENU hMenu = SHLoadMenuPopup(HINST_THISDLL, MENU_STARTMENU_MYDOCS);
  1232. if (hMenu)
  1233. {
  1234. if (!fMyDocs)
  1235. DeleteMenu(hMenu, IDM_MYDOCUMENTS, MF_BYCOMMAND);
  1236. if (!fMyPics)
  1237. DeleteMenu(hMenu, IDM_MYPICTURES, MF_BYCOMMAND);
  1238. // Reset section of menu
  1239. psm->SetMenu(hMenu, _hwnd, SMSET_TOP);
  1240. }
  1241. // Cache what folders are available
  1242. _fHasMyDocuments = fMyDocs;
  1243. _fHasMyPictures = fMyPics;
  1244. }
  1245. }
  1246. void CStartMenuCallback::_UpdateDocsMenuItemNames(IShellMenu* psm)
  1247. {
  1248. TCHAR szBuffer[MAX_PATH];
  1249. if (_fHasMyDocuments)
  1250. {
  1251. if ( SUCCEEDED(GetMyDocumentsDisplayName(szBuffer, ARRAYSIZE(szBuffer))) )
  1252. _FixMenuItemName(psm, IDM_MYDOCUMENTS, szBuffer);
  1253. }
  1254. if (_fHasMyPictures)
  1255. {
  1256. if ( SUCCEEDED(GetMyPicsDisplayName(szBuffer, ARRAYSIZE(szBuffer))) )
  1257. _FixMenuItemName(psm, IDM_MYPICTURES, szBuffer);
  1258. }
  1259. }
  1260. HRESULT CStartMenuCallback::_Init(SMDATA* psmdata)
  1261. {
  1262. HRESULT hr = S_FALSE;
  1263. IShellMenu* psm;
  1264. if (psmdata->punk && SUCCEEDED(hr = psmdata->punk->QueryInterface(IID_PPV_ARG(IShellMenu, &psm))))
  1265. {
  1266. switch(psmdata->uIdParent)
  1267. {
  1268. case IDM_TOPLEVELSTARTMENU:
  1269. {
  1270. if (psmdata->pvUserData && !((SMUSERDATA*)psmdata->pvUserData)->_fInitialized)
  1271. {
  1272. TraceMsg(TF_MENUBAND, "CStartMenuCallback::_Init : Initializing Toplevel Start Menu");
  1273. ((SMUSERDATA*)psmdata->pvUserData)->_fInitialized = TRUE;
  1274. HMENU hmenu;
  1275. TraceMsg(TF_MENUBAND, "CStartMenuCallback::_Init : First Time, and correct parameters");
  1276. _GetStaticStartMenu(&hmenu, &_hwnd);
  1277. if (hmenu)
  1278. {
  1279. HMENU hmenuOld = NULL;
  1280. HWND hwnd;
  1281. DWORD dwFlags;
  1282. psm->GetMenu(&hmenuOld, &hwnd, &dwFlags);
  1283. if (hmenuOld != NULL)
  1284. {
  1285. TBOOL(DestroyMenu(hmenuOld));
  1286. }
  1287. hr = psm->SetMenu(hmenu, _hwnd, SMSET_BOTTOM);
  1288. TraceMsg(TF_MENUBAND, "CStartMenuCallback::_Init : SetMenu(HMENU 0x%x, HWND 0x%x", hmenu, _hwnd);
  1289. }
  1290. _fExpandoMenus = !_fIsStartPanel && AreIntelliMenusEnabled();
  1291. _fCascadeMyDocuments = FeatureEnabled(TEXT("CascadeMyDocuments"));
  1292. _fCascadePrinters = FeatureEnabled(TEXT("CascadePrinters"));
  1293. _fCascadeControlPanel = FeatureEnabled(TEXT("CascadeControlPanel"));
  1294. _fCascadeNetConnections = FeatureEnabled(TEXT("CascadeNetworkConnections"));
  1295. _fAddOpenFolder = FeatureEnabled(TEXT("StartMenuOpen"));
  1296. _fShowInfoTip = FeatureEnabled(TEXT("ShowInfoTip"));
  1297. _fCascadeMyPictures = FeatureEnabled(TEXT("CascadeMyPictures"));
  1298. _dwFlags = GetInitFlags();
  1299. }
  1300. else if (!_fInitedShowTopLevelStartMenu)
  1301. {
  1302. _fInitedShowTopLevelStartMenu = TRUE;
  1303. psm->InvalidateItem(NULL, SMINV_REFRESH);
  1304. }
  1305. // Verify that the Fast items is still pointing to the right location
  1306. if (SUCCEEDED(hr))
  1307. {
  1308. hr = VerifyMergedGuy(FALSE, psm);
  1309. }
  1310. break;
  1311. }
  1312. case IDM_MENU_FIND:
  1313. if (_fFindMenuInvalid)
  1314. {
  1315. hr = _InitializeFindMenu(psm);
  1316. _fFindMenuInvalid = FALSE;
  1317. }
  1318. break;
  1319. case IDM_PROGRAMS:
  1320. // Verify the programs menu is still pointing to the right location
  1321. hr = VerifyMergedGuy(TRUE, psm);
  1322. break;
  1323. case IDM_FAVORITES:
  1324. hr = VerifyCSIDL(IDM_FAVORITES, CSIDL_FAVORITES, psm);
  1325. break;
  1326. case IDM_MYDOCUMENTS:
  1327. hr = VerifyCSIDL(IDM_MYDOCUMENTS, CSIDL_PERSONAL, psm);
  1328. break;
  1329. case IDM_MYPICTURES:
  1330. hr = VerifyCSIDL(IDM_MYPICTURES, CSIDL_MYPICTURES, psm);
  1331. break;
  1332. case IDM_RECENT:
  1333. _UpdateDocumentsShellMenu(psm);
  1334. _UpdateDocsMenuItemNames(psm);
  1335. hr = VerifyCSIDL(IDM_RECENT, CSIDL_RECENT, psm);
  1336. break;
  1337. case IDM_CONTROLS:
  1338. hr = VerifyCSIDL(IDM_CONTROLS, CSIDL_CONTROLS, psm);
  1339. break;
  1340. case IDM_PRINTERS:
  1341. hr = VerifyCSIDL(IDM_PRINTERS, CSIDL_PRINTERS, psm);
  1342. break;
  1343. }
  1344. psm->Release();
  1345. }
  1346. return hr;
  1347. }
  1348. HRESULT CStartMenuCallback::_GetStaticInfoTip(SMDATA* psmd, LPWSTR pszTip, int cch)
  1349. {
  1350. if (!_fShowInfoTip)
  1351. return E_FAIL;
  1352. HRESULT hr = E_FAIL;
  1353. const static struct
  1354. {
  1355. UINT idCmd;
  1356. UINT idInfoTip;
  1357. } s_mpcmdTip[] =
  1358. {
  1359. #if 0 // No tips for the Toplevel. Keep this here because I bet that someone will want them...
  1360. { IDM_PROGRAMS, IDS_PROGRAMS_TIP },
  1361. { IDM_FAVORITES, IDS_FAVORITES_TIP },
  1362. { IDM_RECENT, IDS_RECENT_TIP },
  1363. { IDM_SETTINGS, IDS_SETTINGS_TIP },
  1364. { IDM_MENU_FIND, IDS_FIND_TIP },
  1365. { IDM_HELPSEARCH, IDS_HELP_TIP }, // Redundant?
  1366. { IDM_FILERUN, IDS_RUN_TIP },
  1367. { IDM_LOGOFF, IDS_LOGOFF_TIP },
  1368. { IDM_EJECTPC, IDS_EJECT_TIP },
  1369. { IDM_EXITWIN, IDS_SHUTDOWN_TIP },
  1370. #endif
  1371. // Settings Submenu
  1372. { IDM_CONTROLS, IDS_CONTROL_TIP },
  1373. { IDM_PRINTERS, IDS_PRINTERS_TIP },
  1374. { IDM_TRAYPROPERTIES, IDS_TRAYPROP_TIP },
  1375. { IDM_NETCONNECT, IDS_NETCONNECT_TIP },
  1376. // Recent Folder
  1377. { IDM_MYDOCUMENTS, IDS_MYDOCS_TIP },
  1378. { IDM_MYPICTURES, IDS_MYPICS_TIP },
  1379. };
  1380. for (int i = 0; i < ARRAYSIZE(s_mpcmdTip); i++)
  1381. {
  1382. if (s_mpcmdTip[i].idCmd == psmd->uId)
  1383. {
  1384. TCHAR szTip[MAX_PATH];
  1385. if (LoadString(g_hinst, s_mpcmdTip[i].idInfoTip, szTip, ARRAYSIZE(szTip)))
  1386. {
  1387. SHTCharToUnicode(szTip, pszTip, cch);
  1388. hr = S_OK;
  1389. }
  1390. break;
  1391. }
  1392. }
  1393. return hr;
  1394. }
  1395. typedef struct
  1396. {
  1397. WCHAR wszMenuText[MAX_PATH];
  1398. WCHAR wszHelpText[MAX_PATH];
  1399. int iIcon;
  1400. } SEARCHEXTDATA, *LPSEARCHEXTDATA;
  1401. HRESULT CStartMenuCallback::_GetHmenuInfo(SMDATA* psmd, SMINFO* psminfo)
  1402. {
  1403. const static struct
  1404. {
  1405. UINT idCmd;
  1406. int iImage;
  1407. } s_mpcmdimg[] = { // Top level menu
  1408. { IDM_PROGRAMS, -IDI_CLASSICSM_PROGS },
  1409. { IDM_FAVORITES, -IDI_CLASSICSM_FAVORITES },
  1410. { IDM_RECENT, -IDI_CLASSICSM_RECENTDOCS },
  1411. { IDM_SETTINGS, -IDI_CLASSICSM_SETTINGS },
  1412. { IDM_MENU_FIND, -IDI_CLASSICSM_FIND },
  1413. { IDM_HELPSEARCH, -IDI_CLASSICSM_HELP },
  1414. { IDM_FILERUN, -IDI_CLASSICSM_RUN },
  1415. { IDM_LOGOFF, -IDI_CLASSICSM_LOGOFF },
  1416. { IDM_EJECTPC, -IDI_CLASSICSM_UNDOCK },
  1417. { IDM_EXITWIN, -IDI_CLASSICSM_SHUTDOWN },
  1418. { IDM_MU_SECURITY, II_MU_STSECURITY },
  1419. { IDM_MU_DISCONNECT, II_MU_STDISCONN },
  1420. { IDM_SETTINGSASSIST, -IDI_CLASSICSM_SETTINGS },
  1421. { IDM_CONTROLS, II_STCPANEL },
  1422. { IDM_PRINTERS, II_STPRNTRS },
  1423. { IDM_TRAYPROPERTIES, II_STTASKBR },
  1424. { IDM_MYDOCUMENTS, -IDI_MYDOCS},
  1425. { IDM_CSC, -IDI_CSC},
  1426. { IDM_NETCONNECT, -IDI_NETCONNECT},
  1427. };
  1428. ASSERT(IS_VALID_WRITE_PTR(psminfo, SMINFO));
  1429. int iIcon = -1;
  1430. DWORD dwFlags = psminfo->dwFlags;
  1431. MENUITEMINFO mii = {0};
  1432. HRESULT hr = S_FALSE;
  1433. if (psminfo->dwMask & SMIM_ICON)
  1434. {
  1435. if (IsInRange(psmd->uId, TRAY_IDM_FINDFIRST, TRAY_IDM_FINDLAST))
  1436. {
  1437. // The find menu extensions pack their icon into their data member of
  1438. // Menuiteminfo....
  1439. mii.cbSize = sizeof(mii);
  1440. mii.fMask = MIIM_DATA;
  1441. if (GetMenuItemInfo(psmd->hmenu, psmd->uId, MF_BYCOMMAND, &mii))
  1442. {
  1443. LPSEARCHEXTDATA psed = (LPSEARCHEXTDATA)mii.dwItemData;
  1444. if (psed)
  1445. psminfo->iIcon = psed->iIcon;
  1446. else
  1447. psminfo->iIcon = -1;
  1448. dwFlags |= SMIF_ICON;
  1449. hr = S_OK;
  1450. }
  1451. }
  1452. else
  1453. {
  1454. if (psmd->uId == IDM_MYPICTURES)
  1455. {
  1456. LPITEMIDLIST pidlMyPics = SHCloneSpecialIDList(NULL, CSIDL_MYPICTURES, FALSE);
  1457. if (pidlMyPics)
  1458. {
  1459. LPCITEMIDLIST pidlObject;
  1460. IShellFolder *psf;
  1461. hr = SHBindToParent(pidlMyPics, IID_PPV_ARG(IShellFolder, &psf), &pidlObject);
  1462. if (SUCCEEDED(hr))
  1463. {
  1464. SHMapPIDLToSystemImageListIndex(psf, pidlObject, &psminfo->iIcon);
  1465. dwFlags |= SMIF_ICON;
  1466. psf->Release();
  1467. }
  1468. ILFree(pidlMyPics);
  1469. }
  1470. }
  1471. else
  1472. {
  1473. UINT uIdLocal = psmd->uId;
  1474. if (uIdLocal == IDM_OPEN_FOLDER)
  1475. uIdLocal = psmd->uIdAncestor;
  1476. for (int i = 0; i < ARRAYSIZE(s_mpcmdimg); i++)
  1477. {
  1478. if (s_mpcmdimg[i].idCmd == uIdLocal)
  1479. {
  1480. iIcon = s_mpcmdimg[i].iImage;
  1481. break;
  1482. }
  1483. }
  1484. if (iIcon != -1)
  1485. {
  1486. dwFlags |= SMIF_ICON;
  1487. psminfo->iIcon = Shell_GetCachedImageIndex(TEXT("shell32.dll"), iIcon, 0);
  1488. hr = S_OK;
  1489. }
  1490. }
  1491. }
  1492. }
  1493. if (psminfo->dwMask & SMIM_FLAGS)
  1494. {
  1495. psminfo->dwFlags = dwFlags;
  1496. if ( (psmd->uId == IDM_CONTROLS && _fCascadeControlPanel ) ||
  1497. (psmd->uId == IDM_PRINTERS && _fCascadePrinters ) ||
  1498. (psmd->uId == IDM_MYDOCUMENTS && _fCascadeMyDocuments ) ||
  1499. (psmd->uId == IDM_NETCONNECT && _fCascadeNetConnections ) ||
  1500. (psmd->uId == IDM_MYPICTURES && _fCascadeMyPictures ) )
  1501. {
  1502. psminfo->dwFlags |= SMIF_SUBMENU;
  1503. hr = S_OK;
  1504. }
  1505. else switch (psmd->uId)
  1506. {
  1507. case IDM_FAVORITES:
  1508. case IDM_PROGRAMS:
  1509. psminfo->dwFlags |= SMIF_DROPCASCADE;
  1510. hr = S_OK;
  1511. break;
  1512. }
  1513. }
  1514. return hr;
  1515. }
  1516. DWORD CStartMenuCallback::_GetDemote(SMDATA* psmd)
  1517. {
  1518. UEMINFO uei;
  1519. DWORD dwFlags = 0;
  1520. uei.cbSize = sizeof(uei);
  1521. uei.dwMask = UEIM_HIT;
  1522. if (SUCCEEDED(UEMQueryEvent(psmd->uIdAncestor == IDM_PROGRAMS? &UEMIID_SHELL : &UEMIID_BROWSER,
  1523. UEME_RUNPIDL, (WPARAM)psmd->psf, (LPARAM)psmd->pidlItem, &uei)))
  1524. {
  1525. if (uei.cHit == 0)
  1526. {
  1527. dwFlags |= SMIF_DEMOTED;
  1528. }
  1529. }
  1530. return dwFlags;
  1531. }
  1532. //
  1533. // WARNING! Since this function returns a pointer from our Darwin cache,
  1534. // it must be called while the Darwin critical section is held.
  1535. //
  1536. int GetDarwinIndex(LPCITEMIDLIST pidlFull, CDarwinAd** ppda)
  1537. {
  1538. int iRet = -1;
  1539. if (g_hdpaDarwinAds)
  1540. {
  1541. int chdpa = DPA_GetPtrCount(g_hdpaDarwinAds);
  1542. for (int ihdpa = 0; ihdpa < chdpa; ihdpa++)
  1543. {
  1544. *ppda = (CDarwinAd*)DPA_FastGetPtr(g_hdpaDarwinAds, ihdpa);
  1545. if (*ppda)
  1546. {
  1547. if (ILIsEqual((*ppda)->_pidl, pidlFull))
  1548. {
  1549. iRet = ihdpa;
  1550. break;
  1551. }
  1552. }
  1553. }
  1554. }
  1555. return iRet;
  1556. }
  1557. BOOL CStartMenuCallbackBase::_IsDarwinAdvertisement(LPCITEMIDLIST pidlFull)
  1558. {
  1559. // What this is doing is comparing the passed in pidl with the
  1560. // list of pidls in g_hdpaDarwinAds. That hdpa contains a list of
  1561. // pidls that are darwin ads.
  1562. // If the background thread is not done, then we must assume that
  1563. // it has not processed the shortcut that we are on. That is why we process it
  1564. // in line.
  1565. ENTERCRITICAL_DARWINADS;
  1566. // NOTE: There can be two items in the hdpa. This is ok.
  1567. BOOL fAd = FALSE;
  1568. CDarwinAd* pda = NULL;
  1569. int iIndex = GetDarwinIndex(pidlFull, &pda);
  1570. // Are there any ads?
  1571. if (iIndex != -1 && pda != NULL)
  1572. {
  1573. //This is a Darwin pidl. Is it installed?
  1574. fAd = pda->IsAd();
  1575. }
  1576. LEAVECRITICAL_DARWINADS;
  1577. return fAd;
  1578. }
  1579. STDAPI SHParseDarwinIDFromCacheW(LPWSTR pszDarwinDescriptor, LPWSTR *ppwszOut)
  1580. {
  1581. HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  1582. if (g_hdpaDarwinAds)
  1583. {
  1584. ENTERCRITICAL_DARWINADS;
  1585. int chdpa = DPA_GetPtrCount(g_hdpaDarwinAds);
  1586. for (int ihdpa = 0; ihdpa < chdpa; ihdpa++)
  1587. {
  1588. CDarwinAd *pda = (CDarwinAd*)DPA_FastGetPtr(g_hdpaDarwinAds, ihdpa);
  1589. if (pda && pda->_pszLocalPath && pda->_pszDescriptor &&
  1590. StrCmpCW(pszDarwinDescriptor, pda->_pszDescriptor) == 0)
  1591. {
  1592. hr = SHStrDupW(pda->_pszLocalPath, ppwszOut);
  1593. break;
  1594. }
  1595. }
  1596. LEAVECRITICAL_DARWINADS;
  1597. }
  1598. return hr;
  1599. }
  1600. // REARCHITECT (lamadio): There is a duplicate of this helper in browseui\browmenu.cpp
  1601. // When modifying this, rev that one as well.
  1602. void UEMRenamePidl(const GUID *pguidGrp1, IShellFolder* psf1, LPCITEMIDLIST pidl1,
  1603. const GUID *pguidGrp2, IShellFolder* psf2, LPCITEMIDLIST pidl2)
  1604. {
  1605. UEMINFO uei;
  1606. uei.cbSize = sizeof(uei);
  1607. uei.dwMask = UEIM_HIT | UEIM_FILETIME;
  1608. if (SUCCEEDED(UEMQueryEvent(pguidGrp1,
  1609. UEME_RUNPIDL, (WPARAM)psf1,
  1610. (LPARAM)pidl1, &uei)) &&
  1611. uei.cHit > 0)
  1612. {
  1613. UEMSetEvent(pguidGrp2,
  1614. UEME_RUNPIDL, (WPARAM)psf2, (LPARAM)pidl2, &uei);
  1615. uei.cHit = 0;
  1616. UEMSetEvent(pguidGrp1,
  1617. UEME_RUNPIDL, (WPARAM)psf1, (LPARAM)pidl1, &uei);
  1618. }
  1619. }
  1620. // REARCHITECT (lamadio): There is a duplicate of this helper in browseui\browmenu.cpp
  1621. // When modifying this, rev that one as well.
  1622. void UEMDeletePidl(const GUID *pguidGrp, IShellFolder* psf, LPCITEMIDLIST pidl)
  1623. {
  1624. UEMINFO uei;
  1625. uei.cbSize = sizeof(uei);
  1626. uei.dwMask = UEIM_HIT;
  1627. uei.cHit = 0;
  1628. UEMSetEvent(pguidGrp, UEME_RUNPIDL, (WPARAM)psf, (LPARAM)pidl, &uei);
  1629. }
  1630. //
  1631. // Sortof safe version of ILIsParent which catches when pidlParent or
  1632. // pidlBelow is NULL. pidlParent can be NULL on systems that don't
  1633. // have a Common Program Files folder. pidlBelow should never be NULL
  1634. // but it doesn't hurt to check.
  1635. //
  1636. STDAPI_(BOOL) SMILIsAncestor(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlBelow)
  1637. {
  1638. if (pidlParent && pidlBelow)
  1639. return ILIsParent(pidlParent, pidlBelow, FALSE);
  1640. else
  1641. return FALSE;
  1642. }
  1643. HRESULT CStartMenuCallbackBase::_ProcessChangeNotify(SMDATA* psmd, LONG lEvent,
  1644. LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  1645. {
  1646. switch (lEvent)
  1647. {
  1648. case SHCNE_ASSOCCHANGED:
  1649. SHReValidateDarwinCache();
  1650. return S_OK;
  1651. case SHCNE_RENAMEFOLDER:
  1652. // NTRAID89654-2000/03/13 (lamadio): We should move the MenuOrder stream as well. 5.5.99
  1653. case SHCNE_RENAMEITEM:
  1654. {
  1655. LPITEMIDLIST pidlPrograms;
  1656. LPITEMIDLIST pidlProgramsCommon;
  1657. LPITEMIDLIST pidlFavorites;
  1658. SHGetFolderLocation(NULL, CSIDL_PROGRAMS, NULL, 0, &pidlPrograms);
  1659. SHGetFolderLocation(NULL, CSIDL_COMMON_PROGRAMS, NULL, 0, &pidlProgramsCommon);
  1660. SHGetFolderLocation(NULL, CSIDL_FAVORITES, NULL, 0, &pidlFavorites);
  1661. BOOL fPidl1InStartMenu = SMILIsAncestor(pidlPrograms, pidl1) ||
  1662. SMILIsAncestor(pidlProgramsCommon, pidl1);
  1663. BOOL fPidl1InFavorites = SMILIsAncestor(pidlFavorites, pidl1);
  1664. // If we're renaming something from the Start Menu
  1665. if ( fPidl1InStartMenu ||fPidl1InFavorites)
  1666. {
  1667. IShellFolder* psfFrom;
  1668. LPCITEMIDLIST pidlFrom;
  1669. if (SUCCEEDED(SHBindToParent(pidl1, IID_PPV_ARG(IShellFolder, &psfFrom), &pidlFrom)))
  1670. {
  1671. // Into the Start Menu
  1672. BOOL fPidl2InStartMenu = SMILIsAncestor(pidlPrograms, pidl2) ||
  1673. SMILIsAncestor(pidlProgramsCommon, pidl2);
  1674. BOOL fPidl2InFavorites = SMILIsAncestor(pidlFavorites, pidl2);
  1675. if (fPidl2InStartMenu || fPidl2InFavorites)
  1676. {
  1677. IShellFolder* psfTo;
  1678. LPCITEMIDLIST pidlTo;
  1679. if (SUCCEEDED(SHBindToParent(pidl2, IID_PPV_ARG(IShellFolder, &psfTo), &pidlTo)))
  1680. {
  1681. // Then we need to rename it
  1682. UEMRenamePidl(fPidl1InStartMenu ? &UEMIID_SHELL: &UEMIID_BROWSER,
  1683. psfFrom, pidlFrom,
  1684. fPidl2InStartMenu ? &UEMIID_SHELL: &UEMIID_BROWSER,
  1685. psfTo, pidlTo);
  1686. psfTo->Release();
  1687. }
  1688. }
  1689. else
  1690. {
  1691. // Otherwise, we delete it.
  1692. UEMDeletePidl(fPidl1InStartMenu ? &UEMIID_SHELL : &UEMIID_BROWSER,
  1693. psfFrom, pidlFrom);
  1694. }
  1695. psfFrom->Release();
  1696. }
  1697. }
  1698. ILFree(pidlPrograms);
  1699. ILFree(pidlProgramsCommon);
  1700. ILFree(pidlFavorites);
  1701. }
  1702. break;
  1703. case SHCNE_DELETE:
  1704. // NTRAID89654-2000/03/13 (lamadio): We should nuke the MenuOrder stream as well. 5.5.99
  1705. case SHCNE_RMDIR:
  1706. {
  1707. IShellFolder* psf;
  1708. LPCITEMIDLIST pidl;
  1709. if (SUCCEEDED(SHBindToParent(pidl1, IID_PPV_ARG(IShellFolder, &psf), &pidl)))
  1710. {
  1711. // NOTE favorites is the only that will be initialized
  1712. UEMDeletePidl(psmd->uIdAncestor == IDM_FAVORITES ? &UEMIID_BROWSER : &UEMIID_SHELL,
  1713. psf, pidl);
  1714. psf->Release();
  1715. }
  1716. }
  1717. break;
  1718. case SHCNE_CREATE:
  1719. case SHCNE_MKDIR:
  1720. {
  1721. IShellFolder* psf;
  1722. LPCITEMIDLIST pidl;
  1723. if (SUCCEEDED(SHBindToParent(pidl1, IID_PPV_ARG(IShellFolder, &psf), &pidl)))
  1724. {
  1725. UEMINFO uei;
  1726. uei.cbSize = sizeof(uei);
  1727. uei.dwMask = UEIM_HIT;
  1728. uei.cHit = UEM_NEWITEMCOUNT;
  1729. UEMSetEvent(psmd->uIdAncestor == IDM_FAVORITES? &UEMIID_BROWSER: &UEMIID_SHELL,
  1730. UEME_RUNPIDL, (WPARAM)psf, (LPARAM)pidl, &uei);
  1731. psf->Release();
  1732. }
  1733. }
  1734. break;
  1735. }
  1736. return S_FALSE;
  1737. }
  1738. HRESULT CStartMenuCallbackBase::_GetSFInfo(SMDATA* psmd, SMINFO* psminfo)
  1739. {
  1740. if (psminfo->dwMask & SMIM_FLAGS &&
  1741. (psmd->uIdAncestor == IDM_PROGRAMS ||
  1742. psmd->uIdAncestor == IDM_FAVORITES))
  1743. {
  1744. if (_fExpandoMenus)
  1745. {
  1746. psminfo->dwFlags |= _GetDemote(psmd);
  1747. }
  1748. // This is a little backwards. If the Restriction is On, Then we allow the feature.
  1749. if (SHRestricted(REST_GREYMSIADS) &&
  1750. psmd->uIdAncestor == IDM_PROGRAMS)
  1751. {
  1752. LPITEMIDLIST pidlFull = FullPidlFromSMData(psmd);
  1753. if (pidlFull)
  1754. {
  1755. if (_IsDarwinAdvertisement(pidlFull))
  1756. {
  1757. psminfo->dwFlags |= SMIF_ALTSTATE;
  1758. }
  1759. ILFree(pidlFull);
  1760. }
  1761. }
  1762. if (_ptp2)
  1763. {
  1764. _ptp2->ModifySMInfo(psmd, psminfo);
  1765. }
  1766. }
  1767. return S_OK;
  1768. }
  1769. STDAPI_(void) SHReValidateDarwinCache()
  1770. {
  1771. if (g_hdpaDarwinAds)
  1772. {
  1773. ENTERCRITICAL_DARWINADS;
  1774. int chdpa = DPA_GetPtrCount(g_hdpaDarwinAds);
  1775. for (int ihdpa = 0; ihdpa < chdpa; ihdpa++)
  1776. {
  1777. CDarwinAd* pda = (CDarwinAd*)DPA_FastGetPtr(g_hdpaDarwinAds, ihdpa);
  1778. if (pda)
  1779. {
  1780. pda->CheckInstalled();
  1781. }
  1782. }
  1783. LEAVECRITICAL_DARWINADS;
  1784. }
  1785. }
  1786. // Determines if a CSIDL is a child of another CSIDL
  1787. // e.g.
  1788. // CSIDL_STARTMENU = c:\foo\bar\Start Menu
  1789. // CSIDL_PROGRAMS = c:\foo\bar\Start Menu\Programs
  1790. // Return true
  1791. BOOL IsCSIDLChild(int csidlParent, int csidlChild)
  1792. {
  1793. BOOL fChild = FALSE;
  1794. TCHAR sz1[MAX_PATH];
  1795. if (SUCCEEDED(SHGetFolderPath(NULL, csidlParent, NULL, 0, sz1)))
  1796. {
  1797. TCHAR sz2[MAX_PATH];
  1798. if (SUCCEEDED(SHGetFolderPath(NULL, csidlChild, NULL, 0, sz2)))
  1799. {
  1800. TCHAR szCommonRoot[MAX_PATH];
  1801. if (PathCommonPrefix(sz1, sz2, szCommonRoot) ==
  1802. lstrlen(sz1))
  1803. {
  1804. fChild = TRUE;
  1805. }
  1806. }
  1807. }
  1808. return fChild;
  1809. }
  1810. //
  1811. // Now StartMenuChange value is stored seperately for classic startmenu and new startmenu.
  1812. // So, we need to check which one is currently on before we read the value.
  1813. //
  1814. BOOL IsStartMenuChangeNotAllowed(BOOL fStartPanel)
  1815. {
  1816. return(IsRestrictedOrUserSetting(HKEY_CURRENT_USER, REST_NOCHANGESTARMENU,
  1817. TEXT("Advanced"),
  1818. (fStartPanel ? TEXT("Start_EnableDragDrop") : TEXT("StartMenuChange")),
  1819. ROUS_DEFAULTALLOW | ROUS_KEYALLOWS));
  1820. }
  1821. // Creates the "Start Menu\\Programs" section of the start menu by
  1822. // generating a Merged Shell folder, setting the locations into that item
  1823. // then sets it into the passed IShellMenu.
  1824. HRESULT CStartMenuCallbackBase::InitializeProgramsShellMenu(IShellMenu* psm)
  1825. {
  1826. HKEY hkeyPrograms = NULL;
  1827. LPITEMIDLIST pidl = NULL;
  1828. DWORD dwInitFlags = SMINIT_VERTICAL;
  1829. if (!FeatureEnabled(_fIsStartPanel ? TEXT("Start_ScrollPrograms") : TEXT("StartMenuScrollPrograms")))
  1830. dwInitFlags |= SMINIT_MULTICOLUMN;
  1831. if (IsStartMenuChangeNotAllowed(_fIsStartPanel))
  1832. dwInitFlags |= SMINIT_RESTRICT_DRAGDROP | SMINIT_RESTRICT_CONTEXTMENU;
  1833. if (_fIsStartPanel)
  1834. dwInitFlags |= SMINIT_TOPLEVEL;
  1835. HRESULT hr = psm->Initialize(this, IDM_PROGRAMS, IDM_PROGRAMS, dwInitFlags);
  1836. if (SUCCEEDED(hr))
  1837. {
  1838. _InitializePrograms();
  1839. LPCTSTR pszOrderKey = _fIsStartPanel ?
  1840. STRREG_STARTMENU2 TEXT("\\Programs") :
  1841. STRREG_STARTMENU TEXT("\\Programs");
  1842. // setshellfolder calls need read and write key
  1843. RegCreateKeyEx(HKEY_CURRENT_USER, pszOrderKey, NULL, NULL,
  1844. REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
  1845. NULL, &hkeyPrograms, NULL);
  1846. IShellFolder* psf;
  1847. BOOL fOptimize = FALSE;
  1848. DWORD dwSmset = SMSET_TOP;
  1849. if (_fIsStartPanel)
  1850. {
  1851. // Start Panel: Menu: The Programs section is a merge of the
  1852. // Fast Items and Programs folders with a separator between them.
  1853. dwSmset |= SMSET_SEPARATEMERGEFOLDER;
  1854. hr = GetMergedFolder(&psf, &pidl, c_rgmfiProgramsFolderAndFastItems,
  1855. ARRAYSIZE(c_rgmfiProgramsFolderAndFastItems));
  1856. }
  1857. else
  1858. {
  1859. // Classic Start Menu: The Programs section is just the per-user
  1860. // and common Programs folders merged together
  1861. hr = GetMergedFolder(&psf, &pidl, c_rgmfiProgramsFolder,
  1862. ARRAYSIZE(c_rgmfiProgramsFolder));
  1863. // We used to register for change notify at CSIDL_STARTMENU and assumed
  1864. // that CSIDL_PROGRAMS was a child of CSIDL_STARTMENU. Since this wasn't always the
  1865. // case, I removed the optimization.
  1866. // Both panes are registered recursive. So, When CSIDL_PROGRAMS _IS_ a
  1867. // child of CSIDL_STARTMENU we can enter a code path where when destroying
  1868. // CSIDL_PROGRAMS, we unregister it. This will flush the change nofiy queue
  1869. // of CSIDL_STARTMENU, and blow away all of the children, including CSIDL_PROGRAMS,
  1870. // while we are in the middle of destroying it... See the problem? I have been adding
  1871. // reentrance "Blockers" but this only delayed where we crashed.
  1872. // What was needed was to determine if Programs was a child of the Start Menu directory.
  1873. // if it was we need to add the optmimization. If it's not we don't have a problem.
  1874. // WINDOWS BUG 135156(tybeam): If one of the two is redirected, then this will get optimized
  1875. // we can't do better than this because both are registed recursive, and this will fault...
  1876. fOptimize = IsCSIDLChild(CSIDL_STARTMENU, CSIDL_PROGRAMS)
  1877. || IsCSIDLChild(CSIDL_COMMON_STARTMENU, CSIDL_COMMON_PROGRAMS);
  1878. if (fOptimize)
  1879. {
  1880. dwSmset |= SMSET_DONTREGISTERCHANGENOTIFY;
  1881. }
  1882. }
  1883. if (SUCCEEDED(hr))
  1884. {
  1885. // We should have a pidl from CSIDL_Programs
  1886. ASSERT(pidl);
  1887. // We should have a shell folder from the bind.
  1888. ASSERT(psf);
  1889. hr = psm->SetShellFolder(psf, pidl, hkeyPrograms, dwSmset);
  1890. psf->Release();
  1891. ILFree(pidl);
  1892. }
  1893. if (FAILED(hr))
  1894. RegCloseKey(hkeyPrograms);
  1895. }
  1896. return hr;
  1897. }
  1898. HRESULT GetFolderAndPidl(UINT csidl, IShellFolder **ppsf, LPITEMIDLIST *ppidl)
  1899. {
  1900. *ppsf = NULL;
  1901. HRESULT hr = SHGetFolderLocation(NULL, csidl, NULL, 0, ppidl);
  1902. if (SUCCEEDED(hr))
  1903. {
  1904. hr = SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, *ppidl, ppsf));
  1905. if (FAILED(hr))
  1906. {
  1907. ILFree(*ppidl);
  1908. *ppidl = NULL;
  1909. }
  1910. }
  1911. return hr;
  1912. }
  1913. // Creates the "Start Menu\\<csidl>" section of the start menu by
  1914. // looking up the csidl, generating the Hkey from HKCU\pszRoot\pszValue,
  1915. // Initializing the IShellMenu with dwPassInitFlags, then setting the locations
  1916. // into the passed IShellMenu passing the flags dwSetFlags.
  1917. HRESULT CStartMenuCallback::InitializeCSIDLShellMenu(int uId, int csidl, LPTSTR pszRoot, LPTSTR pszValue,
  1918. DWORD dwPassInitFlags, DWORD dwSetFlags, BOOL fAddOpen,
  1919. IShellMenu* psm)
  1920. {
  1921. DWORD dwInitFlags = SMINIT_VERTICAL | dwPassInitFlags;
  1922. if (IsStartMenuChangeNotAllowed(_fIsStartPanel))
  1923. dwInitFlags |= SMINIT_RESTRICT_DRAGDROP | SMINIT_RESTRICT_CONTEXTMENU;
  1924. psm->Initialize(this, uId, uId, dwInitFlags);
  1925. LPITEMIDLIST pidl;
  1926. IShellFolder* psfFolder;
  1927. HRESULT hr = GetFolderAndPidl(csidl, &psfFolder, &pidl);
  1928. if (SUCCEEDED(hr))
  1929. {
  1930. HKEY hKey = NULL;
  1931. if (pszRoot)
  1932. {
  1933. TCHAR szPath[MAX_PATH];
  1934. StrCpyN(szPath, pszRoot, ARRAYSIZE(szPath));
  1935. if (pszValue)
  1936. {
  1937. PathAppend(szPath, pszValue);
  1938. }
  1939. // setshellfolder calls need read and write key
  1940. RegCreateKeyEx(HKEY_CURRENT_USER, szPath, NULL, NULL,
  1941. REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
  1942. NULL, &hKey, NULL);
  1943. }
  1944. // Point the menu to the shellfolder
  1945. hr = psm->SetShellFolder(psfFolder, pidl, hKey, dwSetFlags);
  1946. if (SUCCEEDED(hr))
  1947. {
  1948. if (fAddOpen && _fAddOpenFolder)
  1949. {
  1950. HMENU hMenu = SHLoadMenuPopup(HINST_THISDLL, MENU_STARTMENU_OPENFOLDER);
  1951. if (hMenu)
  1952. {
  1953. psm->SetMenu(hMenu, _hwnd, SMSET_BOTTOM);
  1954. }
  1955. }
  1956. }
  1957. else
  1958. RegCloseKey(hKey);
  1959. psfFolder->Release();
  1960. ILFree(pidl);
  1961. }
  1962. return hr;
  1963. }
  1964. // This generates the Recent | My Documents, My Pictures sub menu.
  1965. HRESULT CStartMenuCallback::InitializeDocumentsShellMenu(IShellMenu* psm)
  1966. {
  1967. HRESULT hr = InitializeCSIDLShellMenu(IDM_RECENT, CSIDL_RECENT, NULL, NULL,
  1968. SMINIT_RESTRICT_DRAGDROP, SMSET_BOTTOM, FALSE,
  1969. psm);
  1970. // Initializing, reset cache bits for top part of menu
  1971. _fHasMyDocuments = FALSE;
  1972. _fHasMyPictures = FALSE;
  1973. return hr;
  1974. }
  1975. HRESULT CStartMenuCallback::InitializeFastItemsShellMenu(IShellMenu* psm)
  1976. {
  1977. DWORD dwFlags = SMINIT_TOPLEVEL | SMINIT_VERTICAL;
  1978. if (IsStartMenuChangeNotAllowed(_fIsStartPanel))
  1979. dwFlags |= SMINIT_RESTRICT_DRAGDROP | SMINIT_RESTRICT_CONTEXTMENU;
  1980. HRESULT hr = psm->Initialize(this, 0, ANCESTORDEFAULT, dwFlags);
  1981. if (SUCCEEDED(hr))
  1982. {
  1983. _InitializePrograms();
  1984. // Add the fast item folder to the top of the menu
  1985. IShellFolder* psfFast;
  1986. LPITEMIDLIST pidlFast;
  1987. hr = GetMergedFolder(&psfFast, &pidlFast, c_rgmfiStartMenu, ARRAYSIZE(c_rgmfiStartMenu));
  1988. if (SUCCEEDED(hr))
  1989. {
  1990. HKEY hMenuKey = NULL; // WARNING: pmb2->Initialize() will always owns hMenuKey, so don't close it
  1991. // setshellfolder calls need read and write key
  1992. RegCreateKeyEx(HKEY_CURRENT_USER, STRREG_STARTMENU, NULL, NULL,
  1993. REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
  1994. NULL, &hMenuKey, NULL);
  1995. TraceMsg(TF_MENUBAND, "Root Start Menu Key Is %d", hMenuKey);
  1996. hr = psm->SetShellFolder(psfFast, pidlFast, hMenuKey, SMSET_TOP | SMSET_NOEMPTY);
  1997. psfFast->Release();
  1998. ILFree(pidlFast);
  1999. }
  2000. }
  2001. return hr;
  2002. }
  2003. HRESULT CStartMenuCallback::_InitializeFindMenu(IShellMenu* psm)
  2004. {
  2005. HRESULT hr = E_FAIL;
  2006. psm->Initialize(this, IDM_MENU_FIND, IDM_MENU_FIND, SMINIT_VERTICAL);
  2007. HMENU hmenu = CreatePopupMenu();
  2008. if (hmenu)
  2009. {
  2010. ATOMICRELEASE(_pcmFind);
  2011. if (_ptp)
  2012. {
  2013. if (SUCCEEDED(_ptp->GetFindCM(hmenu, TRAY_IDM_FINDFIRST, TRAY_IDM_FINDLAST, &_pcmFind)))
  2014. {
  2015. IContextMenu2 *pcm2;
  2016. _pcmFind->QueryInterface(IID_PPV_ARG(IContextMenu2, &pcm2));
  2017. if (pcm2)
  2018. {
  2019. pcm2->HandleMenuMsg(WM_INITMENUPOPUP, (WPARAM)hmenu, 0);
  2020. pcm2->Release();
  2021. }
  2022. }
  2023. if (_pcmFind)
  2024. {
  2025. hr = psm->SetMenu(hmenu, NULL, SMSET_TOP);
  2026. // Don't Release _pcmFind
  2027. }
  2028. }
  2029. // Since we failed to create the ShellMenu
  2030. // we need to dispose of this HMENU
  2031. if (FAILED(hr))
  2032. DestroyMenu(hmenu);
  2033. }
  2034. return hr;
  2035. }
  2036. HRESULT CStartMenuCallback::InitializeSubShellMenu(int idCmd, IShellMenu* psm)
  2037. {
  2038. HRESULT hr = E_FAIL;
  2039. switch(idCmd)
  2040. {
  2041. case IDM_PROGRAMS:
  2042. hr = InitializeProgramsShellMenu(psm);
  2043. break;
  2044. case IDM_RECENT:
  2045. hr = InitializeDocumentsShellMenu(psm);
  2046. break;
  2047. case IDM_MENU_FIND:
  2048. hr = _InitializeFindMenu(psm);
  2049. break;
  2050. case IDM_FAVORITES:
  2051. hr = InitializeCSIDLShellMenu(IDM_FAVORITES, CSIDL_FAVORITES, STRREG_FAVORITES,
  2052. NULL, 0, SMSET_HASEXPANDABLEFOLDERS | SMSET_USEBKICONEXTRACTION, FALSE,
  2053. psm);
  2054. break;
  2055. case IDM_CONTROLS:
  2056. hr = InitializeCSIDLShellMenu(IDM_CONTROLS, CSIDL_CONTROLS, STRREG_STARTMENU,
  2057. TEXT("ControlPanel"), 0, 0, TRUE,
  2058. psm);
  2059. break;
  2060. case IDM_PRINTERS:
  2061. hr = InitializeCSIDLShellMenu(IDM_PRINTERS, CSIDL_PRINTERS, STRREG_STARTMENU,
  2062. TEXT("Printers"), 0, 0, TRUE,
  2063. psm);
  2064. break;
  2065. case IDM_MYDOCUMENTS:
  2066. hr = InitializeCSIDLShellMenu(IDM_MYDOCUMENTS, CSIDL_PERSONAL, STRREG_STARTMENU,
  2067. TEXT("MyDocuments"), 0, 0, TRUE,
  2068. psm);
  2069. break;
  2070. case IDM_MYPICTURES:
  2071. hr = InitializeCSIDLShellMenu(IDM_MYPICTURES, CSIDL_MYPICTURES, STRREG_STARTMENU,
  2072. TEXT("MyPictures"), 0, 0, TRUE,
  2073. psm);
  2074. break;
  2075. case IDM_NETCONNECT:
  2076. hr = InitializeCSIDLShellMenu(IDM_NETCONNECT, CSIDL_CONNECTIONS, STRREG_STARTMENU,
  2077. TEXT("NetConnections"), 0, 0, TRUE,
  2078. psm);
  2079. break;
  2080. }
  2081. return hr;
  2082. }
  2083. HRESULT CStartMenuCallback::_GetObject(LPSMDATA psmd, REFIID riid, void** ppvOut)
  2084. {
  2085. HRESULT hr = E_FAIL;
  2086. UINT uId = psmd->uId;
  2087. ASSERT(ppvOut);
  2088. ASSERT(IS_VALID_READ_PTR(psmd, SMDATA));
  2089. *ppvOut = NULL;
  2090. if (IsEqualGUID(riid, IID_IShellMenu))
  2091. {
  2092. IShellMenu* psm = NULL;
  2093. hr = CoCreateInstance(CLSID_MenuBand, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellMenu, &psm));
  2094. if (SUCCEEDED(hr))
  2095. {
  2096. hr = InitializeSubShellMenu(uId, psm);
  2097. if (FAILED(hr))
  2098. {
  2099. psm->Release();
  2100. psm = NULL;
  2101. }
  2102. }
  2103. *ppvOut = psm;
  2104. }
  2105. else if (IsEqualGUID(riid, IID_IContextMenu))
  2106. {
  2107. //
  2108. // NOTE - we dont allow users to open the recent folder this way - ZekeL - 1-JUN-99
  2109. // because this is really an internal folder and not a user folder.
  2110. //
  2111. switch (uId)
  2112. {
  2113. case IDM_PROGRAMS:
  2114. case IDM_FAVORITES:
  2115. case IDM_MYDOCUMENTS:
  2116. case IDM_MYPICTURES:
  2117. case IDM_CONTROLS:
  2118. case IDM_PRINTERS:
  2119. case IDM_NETCONNECT:
  2120. {
  2121. CStartContextMenu* pcm = new CStartContextMenu(uId);
  2122. if (pcm)
  2123. {
  2124. hr = pcm->QueryInterface(riid, ppvOut);
  2125. pcm->Release();
  2126. }
  2127. else
  2128. hr = E_OUTOFMEMORY;
  2129. }
  2130. }
  2131. }
  2132. return hr;
  2133. }
  2134. //
  2135. // Return S_OK to remove the pidl from enumeration
  2136. //
  2137. HRESULT CStartMenuCallbackBase::_FilterPidl(UINT uParent, IShellFolder* psf, LPCITEMIDLIST pidl)
  2138. {
  2139. HRESULT hr = S_FALSE;
  2140. ASSERT(IS_VALID_PIDL(pidl));
  2141. ASSERT(IS_VALID_CODE_PTR(psf, IShellFolder));
  2142. if (uParent == IDM_PROGRAMS || uParent == IDM_TOPLEVELSTARTMENU)
  2143. {
  2144. TCHAR szChild[MAX_PATH];
  2145. if (SUCCEEDED(DisplayNameOf(psf, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, szChild, ARRAYSIZE(szChild))))
  2146. {
  2147. // HACKHACK (lamadio): This code assumes that the Display name
  2148. // of the Programs and Commons Programs folders are the same. It
  2149. // also assumes that the "programs" folder in the Start Menu folder
  2150. // is the same name as the one pointed to by CSIDL_PROGRAMS.
  2151. // Filter from top level start menu:
  2152. // Programs, Windows Update, Configure Programs
  2153. if (_IsTopLevelStartMenu(uParent, psf, pidl))
  2154. {
  2155. if ((_pszPrograms && (0 == lstrcmpi(szChild, _pszPrograms))) ||
  2156. (SHRestricted(REST_NOUPDATEWINDOWS) && _pszWindowsUpdate && (0 == lstrcmpi(szChild, _pszWindowsUpdate))) ||
  2157. (SHRestricted(REST_NOSMCONFIGUREPROGRAMS) && _pszConfigurePrograms && (0 == lstrcmpi(szChild, _pszConfigurePrograms))))
  2158. {
  2159. hr = S_OK;
  2160. }
  2161. }
  2162. else
  2163. {
  2164. // IDM_PROGRAMS
  2165. // Filter from Programs: Administrative tools.
  2166. if (!_fShowAdminTools && _pszAdminTools && lstrcmpi(szChild, _pszAdminTools) == 0)
  2167. {
  2168. hr = S_OK;
  2169. }
  2170. }
  2171. }
  2172. }
  2173. return hr;
  2174. }
  2175. BOOL LinkGetInnerPidl(IShellFolder *psf, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlOut, DWORD *pdwAttr)
  2176. {
  2177. *ppidlOut = NULL;
  2178. IShellLink *psl;
  2179. HRESULT hr = psf->GetUIObjectOf(NULL, 1, &pidl, IID_X_PPV_ARG(IShellLink, NULL, &psl));
  2180. if (SUCCEEDED(hr))
  2181. {
  2182. psl->GetIDList(ppidlOut);
  2183. if (*ppidlOut)
  2184. {
  2185. if (FAILED(SHGetAttributesOf(*ppidlOut, pdwAttr)))
  2186. {
  2187. ILFree(*ppidlOut);
  2188. *ppidlOut = NULL;
  2189. }
  2190. }
  2191. psl->Release();
  2192. }
  2193. return (*ppidlOut != NULL);
  2194. }
  2195. //
  2196. // _FilterRecentPidl()
  2197. // the Recent Documents folder can now (NT5) have more than 15 or
  2198. // so documents, but we only want to show the 15 most recent that we always have on
  2199. // the start menu. this means that we need to filter out all folders and
  2200. // anything more than MAXRECENTDOCS
  2201. //
  2202. HRESULT CStartMenuCallback::_FilterRecentPidl(IShellFolder* psf, LPCITEMIDLIST pidl)
  2203. {
  2204. HRESULT hr = S_OK;
  2205. ASSERT(IS_VALID_PIDL(pidl));
  2206. ASSERT(IS_VALID_CODE_PTR(psf, IShellFolder));
  2207. ASSERT(_cRecentDocs != -1);
  2208. ASSERT(_cRecentDocs <= MAXRECENTDOCS);
  2209. // if we already reached our limit, dont go over...
  2210. if (_pmruRecent && (_cRecentDocs < MAXRECENTDOCS))
  2211. {
  2212. // we now must take a looksee for it...
  2213. int iItem;
  2214. DWORD dwAttr = SFGAO_FOLDER | SFGAO_BROWSABLE;
  2215. LPITEMIDLIST pidlTrue;
  2216. // need to find out if the link points to a folder...
  2217. // because we dont want
  2218. if (SUCCEEDED(_pmruRecent->FindData((BYTE *) pidl, ILGetSize(pidl), &iItem))
  2219. && LinkGetInnerPidl(psf, pidl, &pidlTrue, &dwAttr))
  2220. {
  2221. if (!(dwAttr & SFGAO_FOLDER))
  2222. {
  2223. // we have a link to something that isnt a folder
  2224. hr = S_FALSE;
  2225. _cRecentDocs++;
  2226. }
  2227. ILFree(pidlTrue);
  2228. }
  2229. }
  2230. // return S_OK if you dont want to show this item...
  2231. return hr;
  2232. }
  2233. HRESULT CStartMenuCallback::_HandleAccelerator(TCHAR ch, SMDATA* psmdata)
  2234. {
  2235. // Since we renamed the 'Find' menu to 'Search' the PMs wanted to have
  2236. // an upgrade path for users (So they can continue to use the old accelerator
  2237. // on the new menu item.)
  2238. // To enable this, when toolbar detects that there is not an item in the menu
  2239. // that contains the key that has been pressed, then it sends a TBN_ACCL.
  2240. // This is intercepted by mnbase, and translated into SMC_ACCEL.
  2241. if (CharUpper((LPTSTR)ch) == CharUpper((LPTSTR)_szFindMnemonic[0]))
  2242. {
  2243. psmdata->uId = IDM_MENU_FIND;
  2244. return S_OK;
  2245. }
  2246. return S_FALSE;
  2247. }
  2248. HRESULT CStartMenuCallback::_GetTip(LPWSTR pstrTitle, LPWSTR pstrTip)
  2249. {
  2250. if (pstrTitle == NULL ||
  2251. pstrTip == NULL)
  2252. {
  2253. return S_FALSE;
  2254. }
  2255. // all callers must pass MAX_PATH
  2256. LoadString(HINST_THISDLL, IDS_CHEVRONTIPTITLE, pstrTitle, MAX_PATH);
  2257. LoadString(HINST_THISDLL, IDS_CHEVRONTIP, pstrTip, MAX_PATH);
  2258. // Why would this fail?
  2259. ASSERT(pstrTitle[0] != L'\0' && pstrTip[0] != L'\0');
  2260. return S_OK;
  2261. }
  2262. STDAPI CStartMenu_CreateInstance(LPUNKNOWN punkOuter, REFIID riid, void **ppvOut)
  2263. {
  2264. HRESULT hr = E_FAIL;
  2265. IMenuPopup* pmp = NULL;
  2266. *ppvOut = NULL;
  2267. CStartMenuCallback* psmc = new CStartMenuCallback();
  2268. if (psmc)
  2269. {
  2270. IShellMenu* psm;
  2271. hr = CoCreateInstance(CLSID_MenuBand, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellMenu, &psm));
  2272. if (SUCCEEDED(hr))
  2273. {
  2274. hr = CoCreateInstance(CLSID_MenuDeskBar, punkOuter, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IMenuPopup, &pmp));
  2275. if (SUCCEEDED(hr))
  2276. {
  2277. IBandSite* pbs;
  2278. hr = CoCreateInstance(CLSID_MenuBandSite, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IBandSite, &pbs));
  2279. if (SUCCEEDED(hr))
  2280. {
  2281. hr = pmp->SetClient(pbs);
  2282. if (SUCCEEDED(hr))
  2283. {
  2284. IDeskBand* pdb;
  2285. hr = psm->QueryInterface(IID_PPV_ARG(IDeskBand, &pdb));
  2286. if (SUCCEEDED(hr))
  2287. {
  2288. hr = pbs->AddBand(pdb);
  2289. pdb->Release();
  2290. }
  2291. }
  2292. pbs->Release();
  2293. }
  2294. // Don't free pmp. We're using it below.
  2295. }
  2296. if (SUCCEEDED(hr))
  2297. {
  2298. // This is so the ref counting happens correctly.
  2299. hr = psm->Initialize(psmc, 0, 0, SMINIT_VERTICAL | SMINIT_TOPLEVEL);
  2300. if (SUCCEEDED(hr))
  2301. {
  2302. // if this fails, we don't get that part of the menu
  2303. // this is okay since it can happen if the start menu is redirected
  2304. // to where we dont have access.
  2305. psmc->InitializeFastItemsShellMenu(psm);
  2306. }
  2307. }
  2308. psm->Release();
  2309. }
  2310. psmc->Release();
  2311. }
  2312. if (SUCCEEDED(hr))
  2313. {
  2314. hr = pmp->QueryInterface(riid, ppvOut);
  2315. }
  2316. else
  2317. {
  2318. // We need to do this so that it does a cascading delete
  2319. IUnknown_SetSite(pmp, NULL);
  2320. }
  2321. if (pmp)
  2322. pmp->Release();
  2323. return hr;
  2324. }
  2325. // IUnknown
  2326. STDMETHODIMP CStartContextMenu::QueryInterface(REFIID riid, void **ppvObj)
  2327. {
  2328. static const QITAB qit[] =
  2329. {
  2330. QITABENT(CStartContextMenu, IContextMenu),
  2331. { 0 },
  2332. };
  2333. return QISearch(this, qit, riid, ppvObj);
  2334. }
  2335. STDMETHODIMP_(ULONG) CStartContextMenu::AddRef(void)
  2336. {
  2337. return ++_cRef;
  2338. }
  2339. STDMETHODIMP_(ULONG) CStartContextMenu::Release(void)
  2340. {
  2341. ASSERT(_cRef > 0);
  2342. _cRef--;
  2343. if( _cRef > 0)
  2344. return _cRef;
  2345. delete this;
  2346. return 0;
  2347. }
  2348. // IContextMenu
  2349. STDMETHODIMP CStartContextMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  2350. {
  2351. HRESULT hr = E_FAIL;
  2352. HMENU hmenuStartMenu = SHLoadMenuPopup(HINST_THISDLL, MENU_STARTMENUSTATICITEMS);
  2353. if (hmenuStartMenu)
  2354. {
  2355. TCHAR szCommon[MAX_PATH];
  2356. BOOL fAddCommon = (S_OK == SHGetFolderPath(NULL, CSIDL_COMMON_STARTMENU, NULL, 0, szCommon));
  2357. if (fAddCommon)
  2358. fAddCommon = IsUserAnAdmin();
  2359. // Since we don't show this on the start button when the user is not an admin, don't show it here... I guess...
  2360. if (_idCmd != IDM_PROGRAMS || !fAddCommon)
  2361. {
  2362. DeleteMenu(hmenuStartMenu, SMCM_OPEN_ALLUSERS, MF_BYCOMMAND);
  2363. DeleteMenu(hmenuStartMenu, SMCM_EXPLORE_ALLUSERS, MF_BYCOMMAND);
  2364. }
  2365. if (Shell_MergeMenus(hmenu, hmenuStartMenu, 0, indexMenu, idCmdLast, uFlags))
  2366. {
  2367. SetMenuDefaultItem(hmenu, 0, MF_BYPOSITION);
  2368. _SHPrettyMenu(hmenu);
  2369. hr = ResultFromShort(GetMenuItemCount(hmenuStartMenu));
  2370. }
  2371. DestroyMenu(hmenuStartMenu);
  2372. }
  2373. return hr;
  2374. }
  2375. STDMETHODIMP CStartContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
  2376. {
  2377. HRESULT hr = E_FAIL;
  2378. if (HIWORD64(lpici->lpVerb) == 0)
  2379. {
  2380. BOOL fAllUsers = FALSE;
  2381. BOOL fOpen = TRUE;
  2382. switch(LOWORD(lpici->lpVerb))
  2383. {
  2384. case SMCM_OPEN_ALLUSERS:
  2385. fAllUsers = TRUE;
  2386. case SMCM_OPEN:
  2387. // fOpen = TRUE;
  2388. break;
  2389. case SMCM_EXPLORE_ALLUSERS:
  2390. fAllUsers = TRUE;
  2391. case SMCM_EXPLORE:
  2392. fOpen = FALSE;
  2393. break;
  2394. default:
  2395. return S_FALSE;
  2396. }
  2397. hr = ExecStaticStartMenuItem(_idCmd, fAllUsers, fOpen);
  2398. }
  2399. // Ahhh Don't handle verbs!!!
  2400. return hr;
  2401. }
  2402. STDMETHODIMP CStartContextMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax)
  2403. {
  2404. return E_NOTIMPL;
  2405. }
  2406. //****************************************************************************
  2407. //
  2408. // CPersonalStartMenuCallback
  2409. class CPersonalProgramsMenuCallback : public CStartMenuCallbackBase
  2410. {
  2411. public:
  2412. CPersonalProgramsMenuCallback() : CStartMenuCallbackBase(TRUE) { }
  2413. // *** IShellMenuCallback methods ***
  2414. STDMETHODIMP CallbackSM(LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  2415. // *** IObjectWithSite methods *** (overriding CObjectWithSite)
  2416. STDMETHODIMP SetSite(IUnknown* punk);
  2417. public:
  2418. HRESULT Initialize(IShellMenu *psm)
  2419. { return InitializeProgramsShellMenu(psm); }
  2420. private:
  2421. void _UpdateTrayPriv();
  2422. };
  2423. //
  2424. // Throw away any previous TrayPriv2 and try to find a new one.
  2425. //
  2426. // Throwing it away is important to break circular reference loops.
  2427. //
  2428. // Trying to find it will typically fail at SetSite since when we are
  2429. // given our site, CDesktopHost hasn't connected at the top yet so
  2430. // we are unable to find him. But he will be there by the time
  2431. // SMC_INITMENU arrives, so we try again then.
  2432. //
  2433. void CPersonalProgramsMenuCallback::_UpdateTrayPriv()
  2434. {
  2435. ATOMICRELEASE(_ptp2);
  2436. IObjectWithSite *pows;
  2437. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SMenuPopup, IID_PPV_ARG(IObjectWithSite, &pows))))
  2438. {
  2439. pows->GetSite(IID_PPV_ARG(ITrayPriv2, &_ptp2));
  2440. pows->Release();
  2441. }
  2442. }
  2443. STDMETHODIMP CPersonalProgramsMenuCallback::SetSite(IUnknown* punk)
  2444. {
  2445. HRESULT hr = CObjectWithSite::SetSite(punk);
  2446. _UpdateTrayPriv();
  2447. return hr;
  2448. }
  2449. STDMETHODIMP CPersonalProgramsMenuCallback::CallbackSM(LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2450. {
  2451. HRESULT hr = S_FALSE;
  2452. switch (uMsg)
  2453. {
  2454. case SMC_INITMENU:
  2455. _UpdateTrayPriv();
  2456. break;
  2457. case SMC_GETSFINFO:
  2458. hr = _GetSFInfo(psmd, (SMINFO*)lParam);
  2459. break;
  2460. case SMC_NEWITEM:
  2461. hr = _HandleNew(psmd);
  2462. break;
  2463. case SMC_FILTERPIDL:
  2464. ASSERT(psmd->dwMask & SMDM_SHELLFOLDER);
  2465. hr = _FilterPidl(psmd->uIdParent, psmd->psf, psmd->pidlItem);
  2466. break;
  2467. case SMC_GETSFINFOTIP:
  2468. if (!FeatureEnabled(TEXT("ShowInfoTip")))
  2469. hr = E_FAIL; // E_FAIL means don't show. S_FALSE means show default
  2470. break;
  2471. case SMC_PROMOTE:
  2472. hr = _Promote(psmd, (DWORD)wParam);
  2473. break;
  2474. case SMC_SHCHANGENOTIFY:
  2475. {
  2476. PSMCSHCHANGENOTIFYSTRUCT pshf = (PSMCSHCHANGENOTIFYSTRUCT)lParam;
  2477. hr = _ProcessChangeNotify(psmd, pshf->lEvent, pshf->pidl1, pshf->pidl2);
  2478. }
  2479. break;
  2480. case SMC_REFRESH:
  2481. _RefreshSettings();
  2482. break;
  2483. }
  2484. return hr;
  2485. }
  2486. STDAPI CPersonalStartMenu_CreateInstance(LPUNKNOWN punkOuter, REFIID riid, void **ppvOut)
  2487. {
  2488. HRESULT hr;
  2489. *ppvOut = NULL;
  2490. IShellMenu *psm;
  2491. hr = CoCreateInstance(CLSID_MenuBand, NULL, CLSCTX_INPROC_SERVER,
  2492. IID_PPV_ARG(IShellMenu, &psm));
  2493. if (SUCCEEDED(hr))
  2494. {
  2495. CPersonalProgramsMenuCallback *psmc = new CPersonalProgramsMenuCallback();
  2496. if (psmc)
  2497. {
  2498. hr = psmc->Initialize(psm);
  2499. if (SUCCEEDED(hr))
  2500. {
  2501. // SetShellFolder takes ownership of hkCustom
  2502. hr = psm->QueryInterface(riid, ppvOut);
  2503. }
  2504. psmc->Release();
  2505. }
  2506. else
  2507. {
  2508. hr = E_OUTOFMEMORY;
  2509. }
  2510. psm->Release();
  2511. }
  2512. return hr;
  2513. }