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.

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