startmnu.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 61k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. // NOTE! Shdoc401 issue: Start | Find needs to check what version of shell32 user is running 
  2. //                       because of the way we pack icons in menu item info
  3. //                       that code is under #ifdef SHDOC401_DLL ... #endif (reljai)
  4. #include "stdafx.h"
  5. #pragma hdrstop
  6. //#include "resource.h"
  7. //#include "ids.h"
  8. #include "runtask.h"
  9. //#include <shguidp.h>
  10. //#include "shellp.h"
  11. //#include "shdguid.h"
  12. //#include "startids.h"
  13. //#include "cowsite.h"
  14. #ifdef SHDOC401_UEM
  15. //#include "uemapp.h"
  16. #endif
  17. //#include "clsobj.h"
  18. #ifdef SHDOC401_DLL
  19. //#include "options.h"
  20. #endif
  21. #include <mluisupp.h>
  22. // {DDB008FF-048D-11d1-B9CD-00C04FC2C1D2}
  23. const IID IID_IOldShellFolderTask = {0xddb008ff,0x048d,0x11d1,{0xb9,0xcd,0x0,0xc0,0x4f,0xc2,0xc1,0xd2}};
  24. // {0C55899C-1C3E-11d1-9829-00C04FD91972}
  25. const IID IID_IOldStartMenuTask = {0xc55899c,0x1c3e,0x11d1,{0x98,0x29,0x0,0xc0,0x4f,0xd9,0x19,0x72}};
  26. #define WINUPDATEKEY TEXT("SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\WindowsUpdate")
  27. #define TF_MENUBAND         0x00002000      // Menu band messages
  28. // CLSID copied from the Network Connections folder.
  29. const CLSID CLSID_ConnectionFolder = {0x7007ACC7,0x3202,0x11D1,{0xAA,0xD2,0x00,0x80,0x5F,0xC1,0x27,0x0E}};
  30. BOOL AreIntelliMenusEnbaled()
  31. {
  32. #define REG_EXPLORER_ADVANCED TEXT("Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced")
  33.     BOOL fIntelliMenus;
  34.     DWORD dwRest = SHRestricted(REST_INTELLIMENUS);
  35.     if (dwRest != RESTOPT_INTELLIMENUS_USER)
  36.     {
  37.         fIntelliMenus = (dwRest == RESTOPT_INTELLIMENUS_ENABLED);
  38.     }
  39.     else
  40.     {
  41.         fIntelliMenus = SHRegGetBoolUSValue(REG_EXPLORER_ADVANCED, TEXT("IntelliMenus"),
  42.                             FALSE, // Don't ignore HKCU
  43.                             TRUE); // Enable Menus by default
  44.     }
  45.     return fIntelliMenus;
  46. }
  47. // IMenuBandCallback implementation
  48. class CStartMenuCallback : public IShellMenuCallback,
  49.                            public CObjectWithSite
  50. {
  51. public:
  52.     // *** IUnknown methods ***
  53.     STDMETHODIMP QueryInterface (REFIID riid, LPVOID * ppvObj);
  54.     STDMETHODIMP_(ULONG) AddRef();
  55.     STDMETHODIMP_(ULONG)  Release();
  56.     // *** IShellMenuCallback methods ***
  57.     STDMETHODIMP CallbackSM(LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  58.     // *** IObjectWithSite methods ***
  59.     STDMETHODIMP SetSite(IUnknown* punk);
  60.     STDMETHODIMP GetSite(REFIID riid, void** ppvOut);
  61.     CStartMenuCallback();
  62. private:
  63.     virtual ~CStartMenuCallback();
  64.     int _cRef;
  65.     IContextMenu*   _pcmFind;
  66.     TCHAR           _szPrograms[MAX_PATH];
  67.     ITrayPriv*      _ptp;
  68.     IOleCommandTarget* _poct;
  69.     IUnknown*       _punkSite;
  70.     BITBOOL         _fFirstTime: 1;
  71.     BITBOOL         _fExpandoMenus: 1;
  72.     BITBOOL         _fSuspend: 1;
  73.     BITBOOL         _fEject: 1;
  74.     BITBOOL         _fFindMenuInvalid;
  75.     BITBOOL         _fScrollPrograms;
  76.     HWND            _hwnd;
  77.     HRESULT _ExecHmenuItem(HMENU hMenu, UINT uId);
  78.     HRESULT _Init(UINT uIdParent, HMENU hMenu, UINT uId, IUnknown* punk);
  79.     HRESULT _GetHmenuInfo(HMENU hMenu, UINT uId, SMINFO*sminfo);
  80.     HRESULT _GetSFInfo(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem, SMINFO* sminfo);
  81.     HRESULT _GetObject(LPSMDATA psmd, REFIID riid, void** ppvObj);
  82.     HRESULT _CheckRestricted(DWORD dwRestrict, BOOL* fRestricted);
  83.     HRESULT _FilterPidl(IShellFolder* psf, LPCITEMIDLIST pidl);
  84.     HRESULT _FilterFavorites(IShellFolder* psf, LPCITEMIDLIST pidl);
  85.     void _OnExecItemCB(LPSMDATA psmd, UINT uMsg);
  86.     HRESULT _GetDefaultIcon(TCHAR* psz, int* piIndex);
  87.     // helper functions
  88.     BOOL _IsAvailable(int idCmd);
  89.     void _RefreshItem(HMENU hmenu, int idCmd, IShellMenu* psm);
  90.     void _SetStartMenu(IUnknown* punk);
  91. };
  92. CStartMenuCallback::CStartMenuCallback() : _cRef(1)
  93. {
  94.     SHGetSpecialFolderPath(NULL, _szPrograms, CSIDL_PROGRAMS, FALSE);
  95.     PathStripPath(_szPrograms);
  96.     _fFirstTime = TRUE;
  97.     _fFindMenuInvalid = TRUE;
  98. }
  99. CStartMenuCallback::~CStartMenuCallback()
  100. {
  101.     ATOMICRELEASE(_pcmFind);
  102.     ATOMICRELEASE(_ptp);
  103.     ATOMICRELEASE(_poct);
  104. }
  105. // *** IUnknown methods ***
  106. STDMETHODIMP CStartMenuCallback::QueryInterface(REFIID riid, LPVOID * ppvObj)
  107. {
  108.     static const QITAB qit[] = 
  109.     {
  110.         QITABENT(CStartMenuCallback, IShellMenuCallback),
  111.         QITABENT(CStartMenuCallback, IObjectWithSite),
  112.         { 0 },
  113.     };
  114.     return QISearch(this, qit, riid, ppvObj);
  115. }
  116. STDMETHODIMP_(ULONG) CStartMenuCallback::AddRef()
  117. {
  118.     return ++_cRef;
  119. }
  120. STDMETHODIMP_(ULONG) CStartMenuCallback::Release()
  121. {
  122.     ASSERT(_cRef > 0);
  123.     _cRef--;
  124.     if( _cRef > 0)
  125.         return _cRef;
  126.     delete this;
  127.     return 0;
  128. }
  129. STDMETHODIMP CStartMenuCallback::SetSite(IUnknown* punk)
  130. {
  131.     ATOMICRELEASE(_punkSite);
  132.     _punkSite = punk;
  133.     if (punk)
  134.     {
  135.         _punkSite->AddRef();
  136.     }
  137.     return NOERROR;
  138. }
  139. STDMETHODIMP CStartMenuCallback::GetSite(REFIID riid, void**ppvOut)
  140. {
  141.     if (_ptp)
  142.         return _ptp->QueryInterface(riid, ppvOut);
  143.     else
  144.         return E_NOINTERFACE;
  145. }
  146. #if defined(SHDOC401_UEM) && defined(DEBUG)
  147. void DBUEMGetInfo(const IID *pguidGrp, int eCmd, WPARAM wParam, LPARAM lParam)
  148. {
  149. #if 1
  150.     return;
  151. #else
  152.     UEMINFO uei;
  153.     uei.cbSize = SIZEOF(uei);
  154.     uei.dwMask = ~0;    // UEIM_HIT etc.
  155.     UEMGetInfo(pguidGrp, eCmd, wParam, lParam, &uei);
  156.     TCHAR szBuf[20];
  157.     wsprintf(szBuf, TEXT("hit=%d"), uei.cHit);
  158.     MessageBox(NULL, szBuf, TEXT("UEM"), MB_OKCANCEL);
  159.     return;
  160. #endif
  161. }
  162. #endif
  163. STDMETHODIMP CStartMenuCallback::CallbackSM(LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  164. {
  165.     HRESULT hres = S_FALSE;
  166.     switch (uMsg)
  167.     {
  168.     case SMC_CREATE:
  169.         // We need to reset this because when the menu is created, then
  170.         // the item can't be removed. This bug was found in the favorites menu
  171.         // I'm adding it here, because I'm paranoid.
  172.         _fEject = TRUE; 
  173.         _fSuspend = TRUE;
  174.         _fScrollPrograms =  SHRegGetBoolUSValue(REG_EXPLORER_ADVANCED, TEXT("StartMenuScrollPrograms"),
  175.                             FALSE, // Don't ignore HKCU
  176.                             TRUE); // Enable Scrolling
  177.         break;
  178.     case SMC_INITMENU:
  179.         _Init(psmd->uIdParent, psmd->hmenu, psmd->uId, psmd->punk);
  180.         break;
  181. #ifdef SMC_POPUP
  182.     case SMC_POPUP:
  183.         break;
  184. #endif
  185.     case SMC_SFEXEC:
  186.         _OnExecItemCB(psmd, uMsg);
  187.         hres = _ptp->ExecItem(psmd->psf, psmd->pidlItem);
  188.         break;
  189.     case SMC_EXEC:
  190.         _OnExecItemCB(psmd, uMsg);
  191.         hres = _ExecHmenuItem(psmd->hmenu, psmd->uId);
  192.         break;
  193.     case SMC_GETOBJECT:
  194.         hres = _GetObject(psmd, (GUID)*((GUID*)wParam), (void**)lParam);
  195.         break;
  196.     case SMC_GETINFO:
  197.         hres = _GetHmenuInfo(psmd->hmenu, psmd->uId, (SMINFO*)lParam);
  198.         break;
  199.     case SMC_GETSFINFO:
  200.         ASSERT(psmd->dwMask & SMDM_SHELLFOLDER);
  201.         // We don't want to do anything at the root of start menu.
  202.         if (psmd->uIdParent != 0)
  203.         {   
  204.             hres = _GetSFInfo(psmd->pidlFolder, psmd->pidlItem, (SMINFO*)lParam);
  205.         }
  206.         break;
  207.     case SMC_BEGINENUM:
  208.         if (psmd->uIdParent == 0 &&
  209.             SHRestricted(REST_NOSTARTMENUSUBFOLDERS))
  210.         {
  211.             ASSERT(wParam != NULL);
  212.             *(DWORD*)wParam = SHCONTF_NONFOLDERS;
  213.             hres = S_OK;
  214.         }
  215.         break;
  216.     case SMC_FILTERPIDL:
  217.         ASSERT(psmd->dwMask & SMDM_SHELLFOLDER);
  218.         if (psmd->uIdParent == 0)
  219.         {   
  220.             // I only care about top level
  221.             hres = _FilterPidl(psmd->psf, psmd->pidlItem);
  222.         }
  223.         else if (psmd->uIdAncestor == IDM_FAVORITES)
  224.         {
  225.             hres = _FilterFavorites(psmd->psf, psmd->pidlItem);
  226.         }
  227.         break;
  228.     case SMC_INSERTINDEX:
  229.         ASSERT(lParam && IS_VALID_WRITE_PTR(lParam, int));
  230.         *((int*)lParam) = 0;
  231.         hres = S_OK;
  232.         break;
  233.     case SMC_REFRESH:
  234.         _SetStartMenu(psmd->punk);
  235.         _fFindMenuInvalid = TRUE;
  236.         _fScrollPrograms =  SHRegGetBoolUSValue(REG_EXPLORER_ADVANCED, TEXT("StartMenuScrollPrograms"),
  237.                             FALSE, // Don't ignore HKCU
  238.                             TRUE); // Enable Scrolling
  239.         break; 
  240.     case SMC_DEFAULTICON:
  241.         ASSERT(psmd->uIdAncestor == IDM_FAVORITES); // This is only valid for the Favorites menu
  242.         hres = _GetDefaultIcon((LPTSTR)wParam, (int*)lParam);
  243.         break;
  244.     }
  245.     return hres;
  246. }
  247. HRESULT CStartMenuCallback::_FilterFavorites(IShellFolder* psf, LPCITEMIDLIST pidl)
  248. {
  249.     HRESULT hres = S_FALSE;
  250.     if (psf && pidl)
  251.     {
  252.         DWORD dwAttribs = SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_LINK;
  253.         if (SUCCEEDED(psf->GetAttributesOf(1, &pidl, &dwAttribs)))
  254.         {
  255.             // include all non file system objects, folders and links
  256.             // (non file system things like the Channel folders contents)
  257.             if (!(dwAttribs & (SFGAO_FOLDER | SFGAO_LINK)) &&
  258.                 (dwAttribs & SFGAO_FILESYSTEM))
  259.             {
  260.                 hres = S_OK;
  261.             }
  262.         }
  263.     }
  264.     return hres;
  265. }
  266. //***   CStartMenuCallback::_OnExecItemCB -- log menu invokation
  267. // DESCRIPTION
  268. //  iterate up menu 'tree' from self up to root, logging each guy
  269. // NOTES
  270. //  n.b. this assumes the tracked site chain is still valid.  currently
  271. // mn*.cpp!CMenuSFToolbar::v_Show ensures this.  see comments there.
  272. //  BUGBUG perf: if logging is off, we do the walk anyway
  273. // FEATURE_UASSIST
  274. void CStartMenuCallback::_OnExecItemCB(/*const*/LPSMDATA psmd, UINT uMsg) 
  275. {
  276.     HRESULT hr;
  277.     LPITEMIDLIST pidl;
  278.     SMDATA smd = *psmd;     // gotta make copy since caller assumes const
  279.     BOOL fDestroy = FALSE;  // free up child (all but passed-in smd)
  280.     IShellMenu *psm;
  281.     hr = S_OK;
  282.     // we'll terminate after root *or* when somebody's GetState fails
  283.     while (SUCCEEDED(hr)) {
  284.         //
  285.         // party on current band
  286.         //
  287.         switch (smd.dwMask) {
  288.         case SMDM_SHELLFOLDER:
  289.             pidl = ILCombine(smd.pidlFolder, smd.pidlItem);
  290.             if (pidl) {
  291. #ifdef SHDOC401_UEM
  292. #ifdef DEBUG
  293.                 if (!fDestroy)  // hack: leaf
  294.                     DBUEMGetInfo(&UEMIID_SHELL, UEME_RUNPIDL, (WPARAM)CSIDL_PROGRAMS, (LPARAM)pidl);
  295. #endif
  296.                 // BUGBUG CSIDL_PROGRAMS isn't always right (e.g. Docs, Favs)
  297.                 // eventually use Exec(XXXID_PRIVATEID) to get real one
  298.                 UEMFireEvent(&UEMIID_SHELL, UEME_RUNPIDL, (WPARAM)CSIDL_PROGRAMS, (LPARAM)pidl);
  299. #endif // SHDOC401_UEM
  300.                 if ( smd.punk && 
  301.                      SUCCEEDED(smd.punk->QueryInterface(IID_IShellMenu, (void**)&psm)) )
  302.                 {
  303.                     psm->InvalidateItem(&smd, SMINV_REFRESH);
  304.                     psm->Release();
  305.                 }
  306.                 ILFree(pidl);
  307.             }
  308.             break;
  309. #ifdef SHDOC401_UEM
  310.         case SMDM_HMENU:
  311. #ifdef DEBUG
  312.             if (!fDestroy)  // hack: leaf
  313.                 DBUEMGetInfo(&UEMIID_SHELL, UEME_RUNWMCMD, (WPARAM)-1, (LPARAM)smd.uId);
  314. #endif
  315.             UEMFireEvent(&UEMIID_SHELL, UEME_RUNWMCMD, -1, (LPARAM)smd.uId);
  316.             break;
  317. #endif
  318.         default:
  319.             break;
  320.         }
  321.         //
  322.         // move on to parent
  323.         // smd.punk is band; GetSite is menusite; SID_SMenuBandParent is bar
  324.         //
  325.         IServiceProvider *psite;
  326.         hr = IUnknown_GetSite(smd.punk, IID_IServiceProvider, (void **)&psite);
  327.         if (fDestroy) {
  328.             // free up child
  329.             switch (smd.dwMask) {
  330.             case SMDM_SHELLFOLDER:
  331.                 ASSERT(smd.psf);
  332.                 ATOMICRELEASE(smd.psf);
  333.                 ASSERT(smd.pidlFolder);
  334.                 ILFree(smd.pidlFolder);
  335.                 ASSERT(smd.pidlItem);
  336.                 ILFree(smd.pidlItem);
  337.                 break;
  338. #ifdef DEBUG
  339.             case SMDM_HMENU:
  340.                 /*NOTHING*/
  341.                 break;
  342. #endif
  343.             }
  344.             smd.punk->Release();
  345.         }
  346.         fDestroy = TRUE;    // all parents need to be freed 
  347.         if (SUCCEEDED(hr)) {
  348.             hr = psite->QueryService(SID_SMenuBandParent, IID_IShellMenu, (void **)&psm);
  349.             if (SUCCEEDED(hr)) {
  350.                 hr = psm->GetState(&smd);
  351.                 ASSERT(SUCCEEDED(hr));
  352.                 psm->Release();
  353.             }
  354.             psite->Release();
  355.         }
  356.     }
  357.     return;
  358. }
  359. void ShowFolder(HWND hwnd, UINT csidl, UINT uFlags)
  360. {
  361.     SHELLEXECUTEINFO shei = { 0 };
  362.     shei.cbSize     = sizeof(shei);
  363.     shei.fMask      = SEE_MASK_IDLIST;
  364.     shei.nShow      = SW_SHOWNORMAL;
  365.     shei.lpVerb     = TEXT("open");
  366.     if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, csidl, (LPITEMIDLIST*)&shei.lpIDList)))
  367.     {
  368.         ShellExecuteEx(&shei);
  369.         ILFree((LPITEMIDLIST)shei.lpIDList);
  370.     }
  371. }
  372. BOOL IsActiveDesktopEnabled()
  373. {
  374.     HRESULT hres;
  375.     IActiveDesktop* pad;
  376.     COMPONENTSOPT co = { 0 };
  377.     ASSERT(co.fActiveDesktop == FALSE);
  378.     co.dwSize = sizeof(COMPONENTSOPT);
  379.     hres = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER,
  380.         IID_IActiveDesktop, (void**)&pad);
  381.     if(SUCCEEDED(hres))
  382.     {
  383.         pad->GetDesktopItemOptions(&co, NULL);
  384.         pad->Release();
  385.     }
  386.     return co.fActiveDesktop;
  387. }
  388. BOOL UpdateAllDesktopSubscriptions();
  389. DWORD CALLBACK ActiveDesktopThread(LPVOID dwID)
  390. {
  391.     HRESULT hrInit = SHCoInitialize();
  392.     switch((DWORD)dwID)
  393.     {
  394.     case IDM_DESKTOPHTML_ONOFF:
  395.         {
  396.             IActiveDesktop* pad;
  397.             COMPONENTSOPT co;
  398.             HRESULT hres;
  399.             co.dwSize = sizeof(COMPONENTSOPT);
  400.             hres = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER,
  401.                 IID_IActiveDesktop, (void**)&pad);
  402.             if(SUCCEEDED(hres))
  403.             {
  404.                 pad->GetDesktopItemOptions(&co, NULL);
  405.                 co.fActiveDesktop = co.fActiveDesktop? FALSE : TRUE;
  406.                 pad->SetDesktopItemOptions(&co, NULL);
  407.                 pad->ApplyChanges(AD_APPLY_REFRESH);
  408.                 pad->Release();
  409.             }
  410.             break;
  411.         }
  412.     case IDM_DESKTOPHTML_UPDATE:
  413.         UpdateAllDesktopSubscriptions();
  414.         break;
  415.     case IDM_DESKTOPHTML_CUSTOMIZE:
  416.         {
  417.             TCHAR szDesktopWeb[MAX_PATH];
  418.             MLLoadString(IDS_DESKTOPWEBSETTINGS, szDesktopWeb, ARRAYSIZE(szDesktopWeb));
  419.             SHRunControlPanel(szDesktopWeb, NULL);
  420.             break;
  421.         }
  422.     }
  423.     SHCoUninitialize(hrInit);
  424.     return 0;
  425. }
  426. HRESULT CStartMenuCallback::_ExecHmenuItem(HMENU hMenu, UINT uId)
  427. {
  428.     HRESULT hres = S_FALSE;
  429.     HANDLE  hThread;
  430.     DWORD   dwThread;
  431.     if (IsInRange(uId, TRAY_IDM_FINDFIRST, TRAY_IDM_FINDLAST) && _pcmFind)
  432.     {
  433.         CMINVOKECOMMANDINFOEX ici = { 0 };
  434.         ici.cbSize = SIZEOF(CMINVOKECOMMANDINFOEX);
  435.         ici.lpVerb = (LPSTR)MAKEINTRESOURCE(uId - TRAY_IDM_FINDFIRST);
  436.         ici.nShow = SW_NORMAL;
  437.         _pcmFind->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
  438.         hres = S_OK;
  439.     }
  440.     else
  441.     {
  442.         switch (uId)
  443.         {
  444. #ifdef SHDOC401_DLL
  445.         case IDM_FOLDERPROPERTIES:
  446.             DoGlobalFolderOptions();
  447.             break;
  448.         case IDM_DESKTOPHTML_ONOFF:
  449.         case IDM_DESKTOPHTML_CUSTOMIZE:
  450.         case IDM_DESKTOPHTML_UPDATE:
  451.             hThread = CreateThread(NULL, 0, ActiveDesktopThread, (LPVOID)uId, 
  452.                 THREAD_PRIORITY_NORMAL, &dwThread);
  453.             if(hThread != NULL)
  454.                 CloseHandle(hThread);
  455.             hres = NOERROR;
  456.             break;
  457. #endif
  458.         case IDM_MYDOCUMENTS:
  459.             ShowFolder(NULL, CSIDL_PERSONAL, 0);
  460.             hres = NOERROR;
  461.             break;
  462.         case IDM_UPDATEWIZARD:
  463.             {
  464.                 TCHAR szUpdateUrl[MAX_PATH];
  465.                 DWORD cbSize = ARRAYSIZE(szUpdateUrl);
  466.                 if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, WINUPDATEKEY, TEXT("UpdateURL"), 
  467.                     NULL, szUpdateUrl, &cbSize))
  468.                 {
  469.                     SHELLEXECUTEINFO shei= { 0 };
  470.                     shei.cbSize = sizeof(shei);
  471.                     shei.nShow  = SW_SHOWNORMAL;
  472.                     shei.lpParameters = PathGetArgs(szUpdateUrl);
  473.                     PathRemoveArgs(szUpdateUrl);
  474.                     shei.lpFile = szUpdateUrl;
  475.                     ShellExecuteEx(&shei);
  476.                 }
  477.             }
  478.             break;
  479.         }
  480.     }
  481.     return hres;
  482. }
  483. BOOL CStartMenuCallback::_IsAvailable(int idCmd)
  484. {
  485.     VARIANT varRet;
  486.     VariantInit(&varRet);
  487.     if (EVAL(_poct) && 
  488.         SUCCEEDED(_poct->Exec(&CGID_MenuBandItem, MBICMDID_IsVisible, 
  489.             idCmd, NULL, &varRet)))
  490.     {
  491.         // Is it not visible?
  492.         if (varRet.vt == VT_BOOL && varRet.boolVal == VARIANT_FALSE)
  493.         {
  494.             // Yes; Then it's not available
  495.             return FALSE;
  496.         }
  497.     }
  498.     // This item is available
  499.     return TRUE;
  500. }
  501. void CStartMenuCallback::_RefreshItem(HMENU hmenu, int idCmd, IShellMenu* psm)
  502. {
  503.     SMDATA smd;
  504.     smd.dwMask = SMDM_HMENU;
  505.     smd.hmenu = hmenu;
  506.     smd.uId = idCmd;
  507.     psm->InvalidateItem(&smd, SMINV_ID | SMINV_REFRESH);
  508. }
  509. void CStartMenuCallback::_SetStartMenu(IUnknown* punk)
  510. {
  511.     IShellMenu* psm;
  512.     HRESULT hres = punk->QueryInterface(IID_IShellMenu, (void**)&psm);
  513.     if (SUCCEEDED(hres))
  514.     {
  515.         HMENU hmenu;
  516.         IMenuPopup* pmp;
  517.         // The first one should be the bar that the start menu is sitting in.
  518.         if (_punkSite && 
  519.             SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SMenuPopup, IID_IMenuPopup, (void**)&pmp)))
  520.         {
  521.             IObjectWithSite* pows;
  522.             if (SUCCEEDED(pmp->QueryInterface(IID_IObjectWithSite, (void**)&pows)))
  523.             {
  524.                 // It's site should be CStartMenuHost;
  525.                 if (SUCCEEDED(pows->GetSite(IID_ITrayPriv, (void**)&_ptp)))
  526.                 {
  527.                     IOleWindow* pow;
  528.                     _ptp->GetStaticStartMenu(&hmenu);
  529.                     if (SUCCEEDED(_ptp->QueryInterface(IID_IOleWindow, (void**)&pow)))
  530.                     {
  531.                         pow->GetWindow(&_hwnd);
  532.                         pow->Release();
  533.                     }
  534.                     _ptp->QueryInterface(IID_IOleCommandTarget, (void**)&_poct);
  535.                 }
  536.                 else
  537.                     TraceMsg(TF_MENUBAND, "CStartMenuCallback::_SetSite : Failed to aquire CStartMenuHost");
  538.                 pows->Release();
  539.             }
  540.             pmp->Release();
  541.         }
  542.         _fSuspend = _IsAvailable(IDM_SUSPEND);
  543.         _fEject = _IsAvailable(IDM_EJECTPC);
  544.         // If Eject PC is not available, we need to remove it
  545.         if (!_fEject)
  546.             DeleteMenu(hmenu, IDM_EJECTPC, MF_BYCOMMAND);
  547.         // If Suspend is not available, we need to remove it
  548.         if (!_fSuspend)
  549.             DeleteMenu(hmenu, IDM_SUSPEND, MF_BYCOMMAND);
  550.         if (hmenu)
  551.         {
  552.             psm->SetMenu(hmenu, _hwnd, SMSET_BOTTOM);
  553.             TraceMsg(TF_MENUBAND, "CStartMenuCallback::_Init : SetMenu(HMENU 0x%x, HWND 0x%x", hmenu, _hwnd);
  554.         }
  555.         psm->Release();
  556.     }
  557. }
  558. HRESULT CStartMenuCallback::_Init(UINT uIdParent, HMENU hMenu, UINT uId, IUnknown* punk)
  559. {
  560.     if ((uIdParent != 0 && uIdParent != IDM_ACTIVEDESKTOP_PROP) || !punk)
  561.         return S_FALSE;
  562.     IShellMenu* psm;
  563.     HRESULT hres = punk->QueryInterface(IID_IShellMenu, (void**)&psm);
  564.     if (FAILED(hres))
  565.         return hres;
  566.     if (uIdParent == 0)
  567.     {
  568.         TraceMsg(TF_MENUBAND, "CStartMenuCallback::_Init : Initializing Toplevel Start Menu");
  569.         if (_fFirstTime)
  570.         {
  571.             _fFirstTime = FALSE;
  572.             TraceMsg(TF_MENUBAND, "CStartMenuCallback::_Init : First Time, and correct parameters");
  573.             _SetStartMenu(punk);
  574.         }
  575.         else
  576.         {
  577.             BOOL fSuspend = _IsAvailable(IDM_SUSPEND);
  578.             BOOL fEject = _IsAvailable(IDM_EJECTPC);
  579.             if ((_fSuspend ^ fSuspend) ||
  580.                 (_fEject ^ fEject))
  581.             {
  582.                 psm->InvalidateItem(NULL, SMINV_REFRESH);
  583.                 _fSuspend = fSuspend;
  584.                 _fEject = fEject;
  585.             }
  586.         }
  587.     }
  588.     if (uIdParent == IDM_ACTIVEDESKTOP_PROP)
  589.     {
  590.         ASSERT(hMenu);
  591.         DWORD dwFlags = MF_BYCOMMAND;
  592.         if (IsActiveDesktopEnabled())
  593.         {
  594.             dwFlags |= MF_CHECKED;
  595.         }
  596.         else
  597.         {
  598.             dwFlags |= MF_UNCHECKED;
  599.         }
  600.         // Refresh only if we were able to check it.  Refreshing what
  601.         // doesn't exist creates duplicate menu items which suck.
  602.         if (EVAL(CheckMenuItem(hMenu, IDM_DESKTOPHTML_ONOFF, dwFlags) != 0xFFFFFFFF))
  603.         {
  604.             _RefreshItem(hMenu, IDM_DESKTOPHTML_ONOFF, psm);
  605.         }
  606.     }
  607.     psm->Release();
  608.     return NOERROR;
  609. }
  610. typedef struct _SEARCHEXTDATA
  611. {
  612.     WCHAR wszMenuText[MAX_PATH];
  613.     WCHAR wszHelpText[MAX_PATH];
  614.     int   iIcon;
  615. } SEARCHEXTDATA, FAR* LPSEARCHEXTDATA;
  616. HRESULT CStartMenuCallback::_GetHmenuInfo(HMENU hMenu, UINT uId, SMINFO* psminfo)
  617. {
  618.     const static struct 
  619.     {
  620.         UINT idCmd;
  621.         int  iImage;
  622.     } s_mpcmdimg[] = { // Top level menu
  623.                        { IDM_PROGRAMS,       II_STPROGS },
  624. #ifdef IDM_FAVORITES
  625.                        { IDM_FAVORITES,      II_STFAVORITES },
  626. #endif
  627.                        { IDM_RECENT,         II_STDOCS },
  628.                        { IDM_SETTINGS,       II_STSETNGS },
  629.                        { IDM_MENU_FIND,      II_STFIND },
  630. #ifdef FEATURE_BROWSEWEB
  631.                        { IDM_MENU_WEB,       -1 },
  632. #endif
  633.                        { IDM_HELPSEARCH,     II_STHELP },
  634.                        { IDM_FILERUN,        II_STRUN },
  635.                        { IDM_LOGOFF,         II_STLOGOFF },
  636.                        { IDM_EJECTPC,        II_STEJECT },
  637.                        { IDM_EXITWIN,        II_STSHUTD },
  638. #ifdef _HYDRA_      
  639.                        { IDM_MU_SECURITY,    II_MU_STSECURITY },
  640.                        { IDM_MU_DISCONNECT,  II_MU_STDISCONN  },
  641. #endif
  642.                        // Settings Submenu
  643.                        { IDM_SETTINGSASSIST, II_STSETNGS },
  644.                        { IDM_CONTROLS,       II_STCPANEL },
  645.                        { IDM_PRINTERS,       II_STPRNTRS },
  646.                        { IDM_TRAYPROPERTIES, II_STTASKBR },
  647.                        { IDM_MYDOCUMENTS,    II_STDOCS},
  648.                        { IDM_UPDATEWIZARD,   II_WINUPDATE},
  649. #ifdef SHDOC401_DLL
  650.                        { IDM_SUSPEND,        II_STSUSPEND },
  651.                        { IDM_FOLDERPROPERTIES, II_STFLDRPROP},
  652.                        { IDM_ACTIVEDESKTOP_PROP, II_DESKTOP},
  653. #endif
  654. #ifndef SHDOC401_DLL
  655.                        { IDM_NETCONNECT,     -IDI_NETCONNECT},
  656. #endif
  657.                        };
  658.     ASSERT(IS_VALID_WRITE_PTR(psminfo, SMINFO));
  659.     int iIcon = -1;
  660.     DWORD dwFlags = psminfo->dwFlags;
  661.     MENUITEMINFO mii = {0};
  662.     HRESULT hres = S_FALSE;
  663.     if (psminfo->dwMask & SMIM_ICON)
  664.     {
  665.         if (IsInRange(uId, TRAY_IDM_FINDFIRST, TRAY_IDM_FINDLAST))
  666.         {
  667.             // The find menu extensions pack their icon into their data member of
  668.             // Menuiteminfo....
  669.             mii.cbSize = sizeof(MENUITEMINFO);
  670.             mii.fMask = MIIM_DATA;
  671.             if (GetMenuItemInfo(hMenu, uId, MF_BYCOMMAND, &mii))
  672.             {
  673. #ifdef SHDOC401_DLL
  674.                 if (g_uiShell32 >= 5) // g_uiShell32 not defined!! see browseuisccls.c
  675. #endif
  676.                 {
  677.                     // on nt5 we make find menu owner draw so as a result
  678.                     // we pack icons differently
  679.                     LPSEARCHEXTDATA psed = (LPSEARCHEXTDATA)mii.dwItemData;
  680.                     if (psed)
  681.                         psminfo->iIcon = psed->iIcon;
  682.                     else
  683.                         psminfo->iIcon = -1;
  684.                 }
  685. #ifdef SHDOC401_DLL
  686.                 else
  687.                 {
  688.                     // however if we are not running new shell32
  689.                     // the icon is in the good old place...
  690.                     psminfo->iIcon = mii.dwItemData;
  691.                 }
  692. #endif
  693.                 dwFlags |= SMIF_ICON;
  694.                 hres = S_OK;
  695.             }
  696.         }
  697.         else
  698.         {
  699.             for (int i = 0; i < ARRAYSIZE(s_mpcmdimg); i++)
  700.             {
  701.                 if (s_mpcmdimg[i].idCmd == uId)
  702.                 {
  703.                     iIcon = s_mpcmdimg[i].iImage;
  704.                     break;
  705.                 }
  706.             }
  707.             if(iIcon != -1)
  708.             {
  709.                 dwFlags |= SMIF_ICON;
  710.                 psminfo->iIcon = Shell_GetCachedImageIndex(TEXT("shell32.dll"), iIcon, 0);
  711.                 hres = S_OK;
  712.             }
  713.         }
  714.     }
  715.     if(psminfo->dwMask & SMIM_FLAGS)
  716.     {
  717.         psminfo->dwFlags = dwFlags;
  718.         switch (uId)
  719.         {
  720.         case IDM_PROGRAMS:
  721. #ifdef IDM_FAVORITES
  722.         case IDM_FAVORITES:
  723. #endif
  724.             psminfo->dwFlags |= SMIF_DROPCASCADE;
  725.             hres = S_OK;
  726.             break;
  727. #ifdef IDM_ACTIVEDESKTOP_PROP
  728.         case IDM_ACTIVEDESKTOP_PROP:
  729.             psminfo->dwFlags |= SMIF_SUBMENU;
  730.             hres = S_OK;
  731.             break;
  732. #endif
  733. #ifndef SHDOC401_DLL
  734.         case IDM_NETCONNECT:
  735.             // Put this here, because when there are no connections,
  736.             // This defaults to opening the connections folder.
  737.             psminfo->dwFlags |= SMIF_SUBMENU;
  738.             break;
  739. #endif
  740.         }
  741.     }
  742.     return hres;
  743. }
  744. #ifdef SHDOC401_UEM
  745. // BUGBUG (lamadio): This is a copy from browseui/sftbar.cpp
  746. DWORD _GetDemote(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem)
  747. {
  748.     LPITEMIDLIST pidlFull;
  749.     UEMINFO uei;
  750.     DWORD dwFlags;
  751.     dwFlags = 0;
  752.     if (pidlFolder && pidlItem)
  753.     {
  754.         pidlFull = ILCombine(pidlFolder, pidlItem);
  755.         if (pidlFull) {
  756.             uei.cbSize = SIZEOF(uei);
  757.             uei.dwMask = UEIM_HIT;
  758.             if (SUCCEEDED(UEMQueryEvent(&UEMIID_SHELL, UEME_RUNPIDL, (WPARAM)CSIDL_PROGRAMS, (LPARAM)pidlFull, &uei))) {
  759.                 if (uei.cHit == 0) {
  760.                     dwFlags |= SMIF_DEMOTED;
  761.                 }
  762.             }
  763. #if 0   
  764.             if (!(dwFlags & SMIF_DEMOTED))
  765.                 TraceMsg(DM_TRACE, "bui._gd: !SMIF_DEMOTED pidl=%s", DBPidlToTstr(pidlFull));
  766. #endif
  767.             ILFree(pidlFull);
  768.         }
  769.     }
  770.     return dwFlags;
  771. }
  772. #endif // SHDOC401_UEM
  773. HRESULT CStartMenuCallback::_GetSFInfo(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem, SMINFO* psminfo)
  774. {
  775. #ifdef SHDOC401_UEM
  776.     if ((psminfo->dwMask & SMIM_FLAGS) &&
  777.         _fExpandoMenus)
  778.     {
  779.         psminfo->dwFlags |= _GetDemote(pidlFolder, pidlItem);
  780.     }
  781. #endif
  782.     return S_OK;
  783. }
  784. HRESULT CreateNetworkConnections(IShellMenu* psm, HKEY hKeyRoot)
  785. {
  786.     IShellFolder* psfFolder;
  787.     HRESULT hres = CoCreateInstance (CLSID_ConnectionFolder, NULL, CLSCTX_INPROC, 
  788.             IID_IShellFolder, (LPVOID *)&psfFolder);
  789.     if (SUCCEEDED(hres))
  790.     {
  791.         // We now need to generate a pidl for this item.
  792.         // To do this, we create a string that contains ::<CLSID>
  793.         // then pass it through MyComputer::ParseDisplayName.
  794.         // This generates a pidl to the parent.
  795.         DWORD dwEaten;
  796.         IShellFolder* psfMyComputer;
  797.         LPITEMIDLIST pidlMyComputer;
  798.         LPITEMIDLIST pidl;
  799.         WCHAR wszRegItem[GUIDSTR_MAX + 2] = L"::";
  800.         SHStringFromGUIDW(CLSID_ConnectionFolder, (WCHAR*)(wszRegItem + 2), GUIDSTR_MAX); 
  801.         hres = SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidlMyComputer);
  802.         if (SUCCEEDED(hres))
  803.         {
  804.             hres = SHBindToObject(NULL, IID_IShellFolder, 
  805.                 pidlMyComputer, (void**)&psfMyComputer);
  806.             if (SUCCEEDED(hres))
  807.             {
  808.                 hres = psfMyComputer->ParseDisplayName(NULL, NULL, wszRegItem, 
  809.                     &dwEaten, &pidl, NULL);
  810.                 if (SUCCEEDED(hres))
  811.                 {
  812.                     HKEY hKey = NULL;
  813.                     DWORD dwDisp;
  814.                     if (hKeyRoot)
  815.                     {
  816.                         // It's okay if this fails.  We will pass in a null hkey, which simply
  817.                         // means the user's order preference isn't shown.  But at least the
  818.                         // startmenu still appears!
  819.                         RegCreateKeyEx(hKeyRoot, TEXT("Network Connections"), NULL, NULL,
  820.                             REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
  821.                             NULL, &hKey, &dwDisp);
  822.                     }
  823.                     hres = psm->SetShellFolder(psfFolder, pidl, hKey, SMSET_TOP);
  824.                     ILFree(pidl);
  825.                 }
  826.                 
  827.                 psfMyComputer->Release();
  828.             }
  829.             ILFree(pidlMyComputer);
  830.         }
  831.         psfFolder->Release();
  832.     }
  833.     return hres;
  834. }
  835. #define SZ_REGKEY_MYDOCUMENTS TEXT("Software\Microsoft\Windows\CurrentVersion\Explorer\CLSID\{450D8FBA-AD25-11D0-98A8-0800361B1103}")
  836. HRESULT CStartMenuCallback::_GetObject(LPSMDATA psmd, REFIID riid, void** ppvOut)
  837. {
  838.     HRESULT hres = S_FALSE;
  839.     HKEY    hKeyRoot = NULL;
  840.     UINT    uId = psmd->uId;
  841.     ASSERT(ppvOut);
  842.     ASSERT(IS_VALID_READ_PTR(psmd, SMDATA));
  843.     *ppvOut = NULL;
  844.     // BUGBUG: investigate why this can't return E_FAIL legitimately (rather than S_FALSE).
  845.     if (IsEqualGUID(riid, IID_IShellMenu))
  846.     {
  847.         LPITEMIDLIST   pidl2 = NULL;
  848.         LPITEMIDLIST   pidl = NULL;
  849.         IShellFolder*  psfFolder;
  850.         IShellMenu*    psm;
  851.         MENUITEMINFO   mii;
  852.         HKEY           hKey = NULL;
  853.         DWORD          dwDisp;
  854.         DWORD          dwSetFolderFlags = 0;
  855.         LPTSTR         pszKeyName = NULL;
  856.         // It's okay if this fails.  We will pass in a null hkey, which simply
  857.         // means the user's order preference isn't shown.  But at least the
  858.         // startmenu still appears!
  859.         RegCreateKeyEx(HKEY_CURRENT_USER, STRREG_STARTMENU, NULL, NULL,
  860.             REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
  861.             NULL, &hKeyRoot, &dwDisp);
  862.         hres = CoCreateInstance(CLSID_MenuBand, NULL, CLSCTX_INPROC, IID_IShellMenu, (void**)&psm);
  863.         if (SUCCEEDED(hres))
  864.         {
  865.             DWORD dwInitFlags = SMINIT_VERTICAL | SMINIT_LEGACYMENU;
  866.             if (SHRestricted(REST_NOCHANGESTARMENU))
  867.                 dwInitFlags |= SMINIT_RESTRICT_DRAGDROP | SMINIT_RESTRICT_CONTEXTMENU;
  868.             if (uId == IDM_FAVORITES)
  869.                 dwInitFlags &= ~SMINIT_LEGACYMENU;
  870.             if (uId == IDM_PROGRAMS && !_fScrollPrograms)
  871.                 dwInitFlags |= SMINIT_MULTICOLUMN;
  872.             psm->Initialize(this, uId, uId, dwInitFlags);
  873.             switch(uId)
  874.             {
  875.             case IDM_PROGRAMS:
  876.                 pszKeyName = TEXT("&Programs");
  877.                 SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAMS, &pidl);
  878.                 
  879. #ifdef WINNT
  880.                 // The All-Users folder is only available on NT.
  881.                 if (!SHRestricted(REST_NOCOMMONGROUPS))
  882.                 {
  883.                     if (SUCCEEDED(hres = SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_PROGRAMS, &pidl2)))
  884.                     {
  885.                         IAugmentedShellFolder* pasf;
  886.                         IShellFolder*   psfCommon;
  887.                         hres = CoCreateInstance(CLSID_AugmentedShellFolder, NULL, CLSCTX_INPROC, 
  888.                                                 IID_IAugmentedShellFolder, (LPVOID *)&pasf);
  889.                         if (SUCCEEDED(hres))
  890.                         {
  891.                             if (SUCCEEDED(SHBindToObject(NULL, IID_IShellFolder, pidl, (LPVOID*)&psfFolder)))
  892.                             {
  893.                                 pasf->AddNameSpace(NULL, psfFolder, pidl, ASFF_DEFAULT);
  894.                                 psfFolder->Release();
  895.                             }
  896.                             if (SUCCEEDED(SHBindToObject(NULL, IID_IShellFolder, pidl2, (LPVOID*)&psfCommon)))
  897.                             {
  898.                                 pasf->AddNameSpace(NULL, psfCommon, pidl2, ASFF_DEFAULT);
  899.                                 psfCommon->Release();
  900.                             }
  901.                             if (hKeyRoot)
  902.                             {
  903.                                 // It's okay if this fails.  We will pass in a null hkey, which simply
  904.                                 // means the user's order preference isn't shown.  But at least the
  905.                                 // startmenu still appears!
  906.                                 RegCreateKeyEx(hKeyRoot, pszKeyName, NULL, NULL,
  907.                                     REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
  908.                                     NULL, &hKey, &dwDisp);
  909.                             }
  910.                             hres = psm->SetShellFolder(pasf, pidl, hKey, SMSET_TOP);
  911.                             pasf->Release();
  912.                         }
  913.                         if (FAILED(hres))
  914.                         {
  915.                             psm->Release();
  916.                             psm = NULL;
  917.                         }
  918.                         *ppvOut = psm;
  919.                         ILFree(pidl);                        
  920.                         ILFree(pidl2);
  921.                         goto Cleanup;
  922.                     }
  923.                     // if CSIDL_COMMON_PROGRAMS failed, we're probably running
  924.                     // on a down-level shell32, so fall through and
  925.                     // do it the old Win95 way
  926.                 }
  927. #endif
  928.                 break;
  929. #ifdef IDM_FAVORITES
  930.             case IDM_FAVORITES:
  931.                 pszKeyName = TEXT("Favorites");
  932.                 // Favorites menu may have Channels, so we need to do the HasExpandable and
  933.                 // the icons are REALLY expensive, so use the Background icon extraction.
  934.                 dwSetFolderFlags = SMSET_HASEXPANDABLEFOLDERS | SMSET_USEBKICONEXTRACTION; 
  935.                 RegCreateKeyEx(HKEY_CURRENT_USER, STRREG_FAVORITES, NULL, NULL,
  936.                     REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
  937.                     NULL, &hKey, &dwDisp);
  938.                 SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidl);
  939.                 break;
  940. #endif
  941.             case IDM_RECENT:
  942.                 pszKeyName = TEXT("Documents");
  943.                 SHGetSpecialFolderLocation(NULL, CSIDL_RECENT, &pidl);
  944.                 SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl2);
  945.                 if (pidl2 != NULL)
  946.                 {   
  947.                     HMENU hMenu = SHLoadMenuPopup(MLGetHinst(), MENU_STARTMENU_MYDOCS);
  948.                     if (EVAL(hMenu))
  949.                     {
  950.                         TCHAR szDisplay[MAX_PATH] = TEXT("");
  951.                         DWORD dwType;
  952.                         DWORD cbSize = sizeof(szDisplay);
  953.                         SHGetValue(HKEY_CURRENT_USER, SZ_REGKEY_MYDOCUMENTS, TEXT(""), &dwType,
  954.                              szDisplay, &cbSize);
  955.                         if (szDisplay[0])
  956.                         {
  957.                             MENUITEMINFO mii;
  958.     
  959.                             mii.cbSize = sizeof(MENUITEMINFO);
  960.                             mii.dwTypeData = szDisplay; 
  961.                             mii.fMask = MIIM_TYPE | MIIM_ID;
  962.                             mii.cch = cbSize / sizeof(TCHAR) + 1;
  963.                             mii.fType = MFT_STRING;
  964.                             mii.wID = IDM_MYDOCUMENTS;
  965.                             SetMenuItemInfo(hMenu, IDM_MYDOCUMENTS, MF_BYCOMMAND, &mii);
  966.                         }
  967.                         psm->SetMenu(hMenu, _hwnd, SMSET_TOP);
  968.                         IShellFolder*   psf;
  969.                         if (SUCCEEDED(SHBindToObject(NULL, IID_IShellFolder, pidl, (LPVOID*)&psf)))
  970.                         {
  971.                             RegCreateKeyEx(hKeyRoot, pszKeyName, NULL, NULL,
  972.                                 REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
  973.                                 NULL, &hKey, &dwDisp);
  974.                             psm->SetShellFolder(psf, pidl, hKey, SMSET_BOTTOM);
  975.                             psf->Release();
  976.                         }
  977.                         *ppvOut = psm;
  978.                         hres = NOERROR;
  979.                     }
  980.                     ILFree(pidl); // Free the pidl returned from SHGetSpecialFolderLocation
  981.                     ILFree(pidl2); // Free the pidl returned from SHGetSpecialFolderLocation
  982.                     goto Cleanup;
  983.                 }
  984.                 break;
  985.             case IDM_MENU_FIND:
  986.                 if (_fFindMenuInvalid)
  987.                 {
  988.                     mii.cbSize = sizeof (mii);
  989.                     mii.fMask = MIIM_SUBMENU | MIIM_ID;
  990.                     GetMenuItemInfo(psmd->hmenu, IDM_MENU_FIND, MF_BYCOMMAND, &mii);
  991.                     if(mii.hSubMenu)
  992.                     {
  993.                         ATOMICRELEASE(_pcmFind);
  994.                         if (_ptp)
  995.                         {
  996.                             if (SUCCEEDED(_ptp->GetFindCM(mii.hSubMenu, TRAY_IDM_FINDFIRST, TRAY_IDM_FINDLAST, &_pcmFind)))
  997.                             {
  998.                                 // we don't want to pass wm_initmenupopup to defcm if we don't have shell32 ver >= 5
  999.                                 // because it may cause fault
  1000.                                 // 
  1001. #ifdef SHDOC401_DLL
  1002.                                 if (g_uiShell32 >= 5)
  1003. #endif
  1004.                                 {
  1005.                                 // hmenu is cached so no need to call init menu popup more than once (causes leaks
  1006.                                 // and bogus menu text)
  1007.                                     IContextMenu2 *pcm2;
  1008.                                     _pcmFind->QueryInterface(IID_IContextMenu2, (LPVOID*)&pcm2);
  1009.                                     if (pcm2)
  1010.                                     {
  1011.                                         pcm2->HandleMenuMsg(WM_INITMENUPOPUP, (WPARAM)mii.hSubMenu, (LPARAM)0);
  1012.                                         pcm2->Release();
  1013.                                     }
  1014.                                 }
  1015.                             }
  1016.                         }
  1017.                         if (_pcmFind)
  1018.                         {
  1019.                             psm->SetMenu(mii.hSubMenu, NULL, SMSET_TOP | SMSET_DONTOWN);
  1020.                             _fFindMenuInvalid = FALSE;
  1021.                             // Don't Release _pcmFind
  1022.                         }
  1023.             
  1024.                         *ppvOut = (LPVOID)psm;
  1025.                         hres = NOERROR;
  1026.                         goto Cleanup;
  1027.                     }
  1028.                 }
  1029.                 break;
  1030.                 
  1031. #ifndef SHDOC401_DLL
  1032.             case IDM_NETCONNECT: // Start | Settings | Network Connections Cascade
  1033.                 {
  1034.                     hres = CreateNetworkConnections(psm, hKeyRoot);
  1035.                     if (FAILED(hres))
  1036.                     {
  1037.                         psm->Release();
  1038.                         psm = NULL;
  1039.                     }
  1040.                     *ppvOut = psm;
  1041.                     return hres;
  1042.                 }
  1043.                 break;
  1044. #endif
  1045.             default:
  1046.                 psm->Release();
  1047.                 hres = S_FALSE;
  1048.                 break;
  1049.             }
  1050.             // Do we have a pidl?
  1051.             if (pidl)
  1052.             {
  1053.                 // Yes; this menu must want to enumerate a shellfolder
  1054.                 if (SUCCEEDED(SHBindToObject(NULL, IID_IShellFolder, pidl, (LPVOID*)&psfFolder)))
  1055.                 {
  1056.                     if (!hKey)
  1057.                     {
  1058.                         RegCreateKeyEx(hKeyRoot, pszKeyName, NULL, NULL,
  1059.                             REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
  1060.                             NULL, &hKey, &dwDisp);
  1061.                     }
  1062.                     // Point the menu to the shellfolder
  1063.                     psm->SetShellFolder(psfFolder, pidl, hKey, dwSetFolderFlags);
  1064.                     *ppvOut = psm;
  1065.                     psfFolder->Release();       //IMenuPopup hangs onto this, but they do an addref
  1066.                     hres = NOERROR;
  1067.                 }
  1068.                 ILFree(pidl); // Free the pidl returned from SHGetSpecialFolderLocation
  1069.             }
  1070.         }
  1071.     }
  1072. Cleanup:
  1073.     if (hKeyRoot)
  1074.         RegCloseKey(hKeyRoot);
  1075.     return hres;
  1076. }
  1077. HRESULT CStartMenuCallback::_FilterPidl(IShellFolder* psf, LPCITEMIDLIST pidl)
  1078. {
  1079.     HRESULT hres = S_FALSE;
  1080.     STRRET strret;
  1081.     ASSERT(IS_VALID_PIDL(pidl));
  1082.     ASSERT(IS_VALID_CODE_PTR(psf, IShellFolder));
  1083.     if (SUCCEEDED(psf->GetDisplayNameOf(pidl, SHGDN_INFOLDER, &strret)))
  1084.     {
  1085.         TCHAR szChild[MAX_PATH];
  1086.         StrRetToStrN(szChild, ARRAYSIZE(szChild), &strret, pidl);
  1087.         // HACKHACK (lamadio): This code assumes that the Display name
  1088.         // of the Programs and Commons Programs folders are the same. It
  1089.         // also assumes that the "programs" folder in the Start Menu folder
  1090.         // is the same name as the one pointed to by CSIDL_PROGRAMS.
  1091.         if (lstrcmpi(szChild, _szPrograms) == 0)
  1092.         {
  1093.             //They are the same, pull them.
  1094.             hres = S_OK;
  1095.         }
  1096.     }
  1097.     return hres;
  1098. }
  1099. HRESULT GetFastItemFolder(IShellFolder** ppsf, LPITEMIDLIST* ppidl)
  1100. {
  1101.     HRESULT hres;
  1102.     IAugmentedShellFolder * pasf = NULL;
  1103.     LPITEMIDLIST  pidlFast = NULL;
  1104.     hres = CoCreateInstance(CLSID_AugmentedShellFolder, NULL, CLSCTX_INPROC, 
  1105.                             IID_IAugmentedShellFolder, (LPVOID *)&pasf);
  1106.     if (SUCCEEDED(hres))
  1107.     {
  1108.         hres = SHGetSpecialFolderLocation(NULL, CSIDL_STARTMENU, &pidlFast);
  1109.         if (SUCCEEDED(hres))
  1110.         {
  1111.             IShellFolder* psfFast; //For Fast items on Start Menu
  1112.             hres = SHBindToObject(NULL, IID_IShellFolder, pidlFast, (LPVOID*)&psfFast);
  1113.             if (SUCCEEDED(hres))
  1114.             {
  1115.                 pasf->AddNameSpace(NULL, psfFast, pidlFast, ASFF_DEFAULT);
  1116.                 psfFast->Release();
  1117.             }
  1118. #ifdef WINNT
  1119.             IShellFolder* psfFastCommon;
  1120.             LPITEMIDLIST  pidlFastCommon;
  1121.             if(!SHRestricted(REST_NOCOMMONGROUPS))
  1122.             {
  1123.                 if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_STARTMENU, &pidlFastCommon)))
  1124.                 {
  1125.                     
  1126.                     if (SUCCEEDED(SHBindToObject(NULL, IID_IShellFolder, pidlFastCommon, (LPVOID*)&psfFastCommon)))
  1127.                     {
  1128.                         pasf->AddNameSpace(NULL, psfFastCommon, pidlFastCommon, ASFF_DEFAULT);
  1129.                         psfFastCommon->Release();
  1130.                     }
  1131.                     ILFree(pidlFastCommon);
  1132.                 }
  1133.             }
  1134. #endif
  1135.         }
  1136.     }
  1137.     if (FAILED(hres))
  1138.     {
  1139.         ATOMICRELEASE(pasf);
  1140.         ILFree(pidlFast);
  1141.         pidlFast = NULL;
  1142.     }
  1143.     *ppidl = pidlFast;
  1144.     *ppsf = pasf;
  1145.     return hres;
  1146. }
  1147. STDAPI  CStartMenu_CreateInstance(LPUNKNOWN punkOuter, REFIID riid, void **ppvOut)
  1148. {
  1149.     HRESULT hres = E_FAIL;
  1150.     IMenuPopup* pmp = NULL;
  1151.     *ppvOut = NULL;
  1152.     IShellMenuCallback* psmc = new CStartMenuCallback();
  1153.     if (psmc)
  1154.     {
  1155.         IShellMenu* psm;
  1156.         hres = CoCreateInstance(CLSID_MenuBand, NULL, CLSCTX_INPROC_SERVER, IID_IShellMenu, (void**)&psm);
  1157.         if (SUCCEEDED(hres))
  1158.         {
  1159.             DWORD dwDisp;
  1160.             HKEY hMenuKey = NULL;   // WARNING: pmb2->Initialize() will always owns hMenuKey, so don't close it
  1161.             RegCreateKeyEx(HKEY_CURRENT_USER, STRREG_STARTMENU, NULL, NULL,
  1162.                 REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
  1163.                 NULL, &hMenuKey, &dwDisp);
  1164.             TraceMsg(TF_MENUBAND, "Root Start Menu Key Is %d", hMenuKey);
  1165.             hres = CoCreateInstance(CLSID_MenuDeskBar, punkOuter, CLSCTX_INPROC_SERVER, IID_IMenuPopup, (void**)&pmp);
  1166.             if (SUCCEEDED(hres)) 
  1167.             {
  1168.                 IBandSite* pbs;
  1169.                 hres = CoCreateInstance(CLSID_MenuBandSite, NULL, CLSCTX_INPROC_SERVER, IID_IBandSite, (LPVOID*)&pbs);
  1170.                 if (SUCCEEDED(hres)) 
  1171.                 {
  1172.                     hres = pmp->SetClient(pbs);
  1173.                     if (SUCCEEDED(hres)) 
  1174.                     {
  1175.                         IDeskBand* pdb;
  1176.                         hres = psm->QueryInterface(IID_IDeskBand, (void**)&pdb);
  1177.                         if (SUCCEEDED(hres))
  1178.                         {
  1179.                            hres = pbs->AddBand(pdb);
  1180.                            pdb->Release();
  1181.                         }
  1182.                     }
  1183.                     pbs->Release();
  1184.                 }
  1185.                 // Don't free pmp. We're using it below.
  1186.             }
  1187.             
  1188.             // Initialize after it's built up. I need to have a separate com object that
  1189.             // builds this up....
  1190.             psm->Initialize(psmc, 0, 0, SMINIT_TOPLEVEL | SMINIT_VERTICAL | SMINIT_LEGACYMENU);
  1191.             // Add the fast item folder to the top of the menu
  1192.             IShellFolder* psfFast;
  1193.             LPITEMIDLIST pidlFast;
  1194.             if (SUCCEEDED(GetFastItemFolder(&psfFast, &pidlFast)))
  1195.             {
  1196.                 psm->SetShellFolder(psfFast, pidlFast, hMenuKey, SMSET_TOP | SMSET_NOEMPTY);
  1197.                 psfFast->Release();
  1198.                 ILFree(pidlFast);
  1199.             }
  1200.             psm->Release();
  1201.         }
  1202.         psmc->Release();
  1203.     }
  1204.     if (SUCCEEDED(hres))
  1205.     {
  1206.         hres = pmp->QueryInterface(riid, ppvOut);
  1207.     }
  1208.     if (pmp)
  1209.         pmp->Release();
  1210.     return hres;
  1211. }
  1212. ////////////////////////////////////////////////////////////////
  1213. /// start menu and desktop hotkey tasks
  1214. #define START_BANNER_COLOR_NT   RGB(0, 0, 0)
  1215. #define START_BANNER_COLOR_95   RGB(128, 128, 128)
  1216. //
  1217. // CStartMenuTask object.  This task enumerates the start menu for hotkeys 
  1218. // and to cache the icons.
  1219. //
  1220. #if 0
  1221. #define TF_SMTASK TF_CUSTOM1
  1222. #else
  1223. #define TF_SMTASK 0
  1224. #endif
  1225. #undef  THISCLASS   
  1226. #undef  SUPERCLASS
  1227. #define SUPERCLASS  CRunnableTask
  1228. class CStartMenuTask : public CRunnableTask,
  1229.                        public IOldStartMenuTask
  1230. {
  1231. public:
  1232.     // *** IUnknown Methods ***
  1233.     virtual STDMETHODIMP_(ULONG) AddRef(void) { return SUPERCLASS::AddRef(); };
  1234.     virtual STDMETHODIMP_(ULONG) Release(void){ return SUPERCLASS::Release(); };
  1235.     virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
  1236.     
  1237.     // *** IOldShellFolderTask Methods ***
  1238.     virtual STDMETHODIMP InitTaskSFT(IShellFolder *psfParent, LPITEMIDLIST pidlFull, DWORD dwFlags, DWORD dwTaskPriority);
  1239.     // *** IOldStartMenuTask Methods ***
  1240.     virtual STDMETHODIMP InitTaskSMT(IShellHotKey * pshk, int iThreadPriority);
  1241.     // *** CRunnableTask virtual methods
  1242.     virtual STDMETHODIMP RunInitRT(void);
  1243.     virtual STDMETHODIMP InternalResumeRT(void);
  1244. protected:
  1245.     friend HRESULT CStartMenuTask_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppvOut);
  1246.     CStartMenuTask();
  1247.     ~CStartMenuTask();
  1248.     HRESULT     _CreateNewTask(IShellFolder * psf, LPCITEMIDLIST pidl);
  1249.     
  1250.     int             _iThreadPriority;
  1251.     DWORD           _dwTaskPriority;
  1252.     DWORD           _dwFlags;
  1253.     IShellFolder*   _psf;
  1254.     LPITEMIDLIST    _pidlFolder;
  1255.     IShellHotKey *  _pshk;
  1256.     IEnumIDList *   _pei;
  1257. };
  1258. HRESULT CStartMenuTask_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppvOut)
  1259. {
  1260.     CStartMenuTask* pTask = new CStartMenuTask();
  1261.     if (pTask)
  1262.     {
  1263.         HRESULT hres = pTask->QueryInterface(riid, ppvOut);
  1264.         pTask->Release();
  1265.         return hres;
  1266.     }
  1267.     return E_OUTOFMEMORY;
  1268. }
  1269. // constructor
  1270. CStartMenuTask::CStartMenuTask() : CRunnableTask(RTF_SUPPORTKILLSUSPEND)
  1271. {
  1272. }
  1273. // destructor
  1274. CStartMenuTask::~CStartMenuTask()
  1275. {
  1276.     if (_pidlFolder)
  1277.         ILFree(_pidlFolder);
  1278.         
  1279.     ATOMICRELEASE(_pei);
  1280.     ATOMICRELEASE(_psf);
  1281.     ATOMICRELEASE(_pshk);
  1282. }
  1283. STDMETHODIMP CStartMenuTask::QueryInterface(REFIID riid, LPVOID * ppvObj)
  1284. {
  1285.     HRESULT hres;
  1286.     static const QITAB qit[] = {
  1287.         QITABENT(CStartMenuTask, IOldShellFolderTask),
  1288.         QITABENT(CStartMenuTask, IOldStartMenuTask),
  1289.         { 0 },
  1290.     };
  1291.     hres = QISearch(this, qit, riid, ppvObj);
  1292.     if (FAILED(hres))
  1293.         hres = SUPERCLASS::QueryInterface(riid, ppvObj);
  1294.         
  1295.     return hres;
  1296. }
  1297. /*----------------------------------------------------------
  1298. Purpose: IOldShellFolderTask::InitTaskSFT method
  1299. */
  1300. STDMETHODIMP CStartMenuTask::InitTaskSFT(IShellFolder *psfParent, LPITEMIDLIST pidlFull,
  1301.                                          DWORD dwFlags, DWORD dwTaskPriority)
  1302. {
  1303.     HRESULT hr = E_OUTOFMEMORY;
  1304.     IShellFolder* psf;
  1305.     if (!psfParent)
  1306.         return E_INVALIDARG;
  1307.     ASSERT(IS_VALID_PIDL(pidlFull));
  1308.     
  1309.     _pidlFolder = ILClone(pidlFull);
  1310.     if (_pidlFolder)
  1311.     {
  1312.         // we have a pidlChild, so bind to him
  1313.         hr = psfParent->BindToObject(ILFindLastID(_pidlFolder), NULL, IID_IShellFolder, (void **)&psf);
  1314.         if (SUCCEEDED(hr))
  1315.         {
  1316.             _psf = psf;
  1317.             _dwTaskPriority = dwTaskPriority;
  1318.             _dwFlags = dwFlags;
  1319. #ifdef DEBUG
  1320.             TCHAR szPath[MAX_PATH];
  1321.             
  1322.             if (pidlFull)
  1323.                 SHGetPathFromIDList(pidlFull, szPath);
  1324.             else
  1325.                 szPath[0] = TEXT('0');
  1326.             TraceMsg(TF_SMTASK, "StartMenuTask: task %#lx (pri %d) is '%s'", _dwTaskID, dwTaskPriority, szPath);
  1327. #endif
  1328.         }
  1329.     }
  1330.     return hr;
  1331. }
  1332. /*----------------------------------------------------------
  1333. Purpose: IOldStartMenuTask::InitTaskSMT method
  1334. */
  1335. STDMETHODIMP CStartMenuTask::InitTaskSMT(IShellHotKey * pshk, int iThreadPriority)
  1336. {
  1337.     ASSERT(pshk);
  1338.     _pshk = pshk;
  1339.     if (_pshk)
  1340.         _pshk->AddRef();
  1341.     _iThreadPriority = iThreadPriority;
  1342.     
  1343.     return S_OK;
  1344. }
  1345. HRESULT CStartMenuTask::_CreateNewTask(IShellFolder * psf, LPCITEMIDLIST pidl)
  1346. {   
  1347.     HRESULT hres;
  1348.     LPSHELLTASKSCHEDULER pScheduler;
  1349.     
  1350.     // Get the shared task scheduler
  1351.     // BUGBUG : this needs changing so the pointer to the scheduler is passed around, not
  1352.     // BUGBUG : a CoCreate each time....
  1353.     hres = CoCreateInstance( CLSID_SharedTaskScheduler, NULL, CLSCTX_INPROC, 
  1354.             IID_IShellTaskScheduler, (LPVOID *) & pScheduler );
  1355.     if (SUCCEEDED(hres))
  1356.     {
  1357.         // create a new task
  1358.         IOldStartMenuTask * psmt;
  1359.         
  1360.         if (SUCCEEDED(CoCreateInstance(CLSID_StartMenuTask, NULL, CLSCTX_INPROC,
  1361.                                        IID_IOldStartMenuTask, (LPVOID *)&psmt)))
  1362.         {
  1363.             // Initialize the task
  1364.             LPITEMIDLIST pidlFull = ILCombine(_pidlFolder, pidl);
  1365.             if (pidlFull)
  1366.             {
  1367.                 if (SUCCEEDED(psmt->InitTaskSFT(_psf, pidlFull, _dwTaskPriority - 1, THREAD_PRIORITY_NORMAL)))
  1368.                 {
  1369.                     IRunnableTask * ptask;
  1370.                     psmt->InitTaskSMT(_pshk, _iThreadPriority);
  1371.                     
  1372.                     if (SUCCEEDED(psmt->QueryInterface(IID_IRunnableTask, (LPVOID *)&ptask)))
  1373.                     {
  1374.                         // Add it to the scheduler, with the priority lower than
  1375.                         // the parent folders. 
  1376.                         hres = pScheduler->AddTask(ptask, CLSID_StartMenuTask, 0, _dwTaskPriority - 1);
  1377.                         
  1378.                         ptask->Release();
  1379.                     }
  1380.                 }
  1381.                 ILFree(pidlFull);
  1382.             }
  1383.             psmt->Release();
  1384.         }
  1385.         // release the explorer task scheduler
  1386.         pScheduler->Release();
  1387.     }
  1388.     return hres;
  1389. }
  1390. /*----------------------------------------------------------
  1391. Purpose: CRunnableTask::RunInitRT method
  1392. */
  1393. HRESULT CStartMenuTask::RunInitRT(void)
  1394. {
  1395.     // This is the first time that ::Run has been called on this task,
  1396.     // so create our pEnumIDList interface.
  1397.     ASSERT(IS_VALID_CODE_PTR(_psf, IShellFolder));
  1398.     return _psf->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &_pei);
  1399. }
  1400. /*----------------------------------------------------------
  1401. Purpose: CRunnableTask::InternalResumeRT method
  1402. */
  1403. HRESULT CStartMenuTask::InternalResumeRT(void)
  1404. {
  1405.     HRESULT hr = NOERROR;
  1406.     LPITEMIDLIST pidl;
  1407.     ULONG cElements;
  1408.     int iOldThreadPriority;
  1409.     HANDLE hThread = GetCurrentThread();
  1410.     ASSERT(_pei);
  1411.     // Change the thread priority for this task?
  1412.     iOldThreadPriority = GetThreadPriority(hThread);
  1413.     if (iOldThreadPriority != _iThreadPriority)
  1414.     {
  1415.         // Yes
  1416.         SetThreadPriority(hThread, _iThreadPriority);
  1417.         
  1418.         DEBUG_CODE( TraceMsg(TF_SMTASK, "StartMenuTask (%#lx): Set thread priority to %x", _dwTaskID, _iThreadPriority); )
  1419.     }
  1420.     // Enumerate the entire tree
  1421.     while (NOERROR == _pei->Next(1, &pidl, &cElements))
  1422.     {
  1423. #ifdef FULL_DEBUG
  1424.         TCHAR szPath[MAX_PATH];
  1425.         DWORD dwCount;
  1426.         
  1427.         SHGetPathFromIDList(pidl, szPath);
  1428.         // TraceMsg(TF_SMTASK, "StartMenuTask (%#lx): caching %s", _dwTaskID, PathFindFileName(szPath));
  1429.         dwCount = GetTickCount();
  1430. #endif
  1431.         // Enumerate folders.  Don't enumerate folders that behave like
  1432.         // shortcuts (channels).
  1433.         
  1434.         DWORD dwAttrib = SFGAO_FOLDER;
  1435.         _psf->GetAttributesOf(1, (LPCITEMIDLIST *)&pidl, &dwAttrib);
  1436.         // Is this a subfolder?
  1437.         if ((dwAttrib & SFGAO_FOLDER) && (_dwFlags & ITSFT_RECURSE))
  1438.         {
  1439.             // Yes; is this a channel?
  1440.             if (!SHIsExpandableFolder(_psf, pidl))
  1441.             {
  1442.                 // Yes; skip it
  1443. #ifdef FULL_DEBUG
  1444.                 TraceMsg(TF_SMTASK, "StartMenuTask (%#lx): skipping %s", _dwTaskID, PathFindFileName(szPath));
  1445. #endif
  1446.                 goto MoveOn;
  1447.             }
  1448.             
  1449.             // create a task to enumerate the folder
  1450.             _CreateNewTask(_psf, pidl);
  1451.         }
  1452.         // Register the hotkey
  1453.         if (_pshk)
  1454.             _pshk->RegisterHotKey(_psf, _pidlFolder, pidl);
  1455.         
  1456.         // Cache the icon
  1457.         SHMapPIDLToSystemImageListIndex(_psf, pidl, NULL);
  1458. #ifdef FULL_DEBUG
  1459.         TraceMsg(TF_SMTASK, "StartMenuTask (%#lx): cached '%s' (time: %ld)", _dwTaskID, PathFindFileName(szPath), GetTickCount()-dwCount);
  1460. #endif
  1461. MoveOn:
  1462.         ILFree(pidl);
  1463.         if (WAIT_OBJECT_0 == WaitForSingleObject(_hDone, 0))
  1464.         {
  1465.             // the _hDone is signaled, so we are either being suspended or killed.
  1466.             // TraceMsg(TF_SMTASK, "StartMenuTask (%#lx): ", _dwTaskID, PathFindFileName(szPath), GetTickCount()-dwCount);
  1467.             if (_hDone)
  1468.                 ResetEvent(_hDone);
  1469.         
  1470.             hr = E_PENDING;
  1471.             break;
  1472.         }
  1473.     }
  1474.     if (iOldThreadPriority != _iThreadPriority)
  1475.     {
  1476.         // bump the thread priority back to what it was
  1477.         // before we executed this task
  1478.         SetThreadPriority(hThread, iOldThreadPriority);
  1479.     }
  1480.     if (_lState == IRTIR_TASK_PENDING)
  1481.     {
  1482.         // We were told to quit, so do it.
  1483.         hr = E_FAIL;
  1484.     }
  1485.     if (_lState != IRTIR_TASK_SUSPENDED)
  1486.     {
  1487.         // We weren't suspended, so we must be finished
  1488.         SUPERCLASS::InternalResumeRT();
  1489. #ifdef FULL_DEBUG
  1490.         TraceMsg(TF_SMTASK, "StartMenuTask (%#lx): Finished. Total time = %ld", _dwTaskID, GetTickCount()-_dwTaskID);
  1491. #endif
  1492.     }
  1493.     
  1494.     return hr;
  1495. }
  1496. //
  1497. // CDesktopTask object.  This task enumerates the desktop for hotkeys 
  1498. //
  1499. #undef  THISCLASS   
  1500. #undef  SUPERCLASS
  1501. #define SUPERCLASS  CRunnableTask
  1502. class CDesktopTask : public CRunnableTask,
  1503.                      public IOldStartMenuTask
  1504. {
  1505. public:
  1506.     // *** IUnknown Methods ***
  1507.     virtual STDMETHODIMP_(ULONG) AddRef(void) { return SUPERCLASS::AddRef(); };
  1508.     virtual STDMETHODIMP_(ULONG) Release(void){ return SUPERCLASS::Release(); };
  1509.     virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
  1510.     
  1511.     // *** IOldShellFolderTask Methods ***
  1512.     virtual STDMETHODIMP InitTaskSFT(IShellFolder *psfParent, LPITEMIDLIST pidlFull, DWORD dwFlags, DWORD dwTaskPriority);
  1513.     // *** IOldStartMenuTask Methods ***
  1514.     virtual STDMETHODIMP InitTaskSMT(IShellHotKey * pshk, int iThreadPriority);
  1515.     // *** CRunnableTask virtual methods
  1516.     virtual STDMETHODIMP RunInitRT(void);
  1517.     virtual STDMETHODIMP InternalResumeRT(void);
  1518. protected:
  1519.     friend HRESULT CDesktopTask_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppvOut);
  1520.     CDesktopTask();
  1521.     ~CDesktopTask();
  1522.     DWORD           _dwTaskPriority;
  1523.     IShellFolder*   _psf;
  1524.     LPITEMIDLIST    _pidlFolder;
  1525.     IShellHotKey *  _pshk;
  1526.     IEnumIDList *   _pei;
  1527. };
  1528. HRESULT CDesktopTask_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppvOut)
  1529. {
  1530.     CDesktopTask* pTask = new CDesktopTask();
  1531.     if (pTask)
  1532.     {
  1533.         HRESULT hres = pTask->QueryInterface(riid, ppvOut);
  1534.         pTask->Release();
  1535.         return hres;
  1536.     }
  1537.     return E_OUTOFMEMORY;
  1538. }
  1539. // constructor
  1540. CDesktopTask::CDesktopTask() : CRunnableTask(RTF_SUPPORTKILLSUSPEND)
  1541. {
  1542. }
  1543. // destructor
  1544. CDesktopTask::~CDesktopTask()
  1545. {
  1546.     if (_pidlFolder)
  1547.         ILFree(_pidlFolder);
  1548.         
  1549.     ATOMICRELEASE(_pei);
  1550.     ATOMICRELEASE(_psf);
  1551.     ATOMICRELEASE(_pshk);
  1552. }
  1553. STDMETHODIMP CDesktopTask::QueryInterface(REFIID riid, LPVOID * ppvObj)
  1554. {
  1555.     HRESULT hres;
  1556.     static const QITAB qit[] = {
  1557.         QITABENT(CDesktopTask, IOldShellFolderTask),
  1558.         QITABENT(CDesktopTask, IOldStartMenuTask),
  1559.         { 0 },
  1560.     };
  1561.     hres = QISearch(this, qit, riid, ppvObj);
  1562.     if (FAILED(hres))
  1563.         hres = SUPERCLASS::QueryInterface(riid, ppvObj);
  1564.         
  1565.     return hres;
  1566. }
  1567. /*----------------------------------------------------------
  1568. Purpose: IOldShellFolderTask::InitTaskSFT method
  1569. */
  1570. STDMETHODIMP CDesktopTask::InitTaskSFT(IShellFolder *psfParent, LPITEMIDLIST pidlFull,
  1571.                                        DWORD dwFlags, DWORD dwTaskPriority)
  1572. {
  1573.     HRESULT hr = E_OUTOFMEMORY;
  1574.     IShellFolder* psf;
  1575.     if (!psfParent)
  1576.         return E_INVALIDARG;
  1577.     if (NULL == pidlFull)
  1578.     {
  1579.         psf = psfParent;
  1580.         psf->AddRef();
  1581.         hr = S_OK;
  1582.     }
  1583.     else
  1584.     {
  1585.         ASSERT(IS_VALID_PIDL(pidlFull));
  1586.         
  1587.         _pidlFolder = ILClone(pidlFull);
  1588.         if (_pidlFolder)
  1589.         {
  1590.             // we have a pidlChild, so bind to him
  1591.             hr = psfParent->BindToObject(ILFindLastID(_pidlFolder), NULL, IID_IShellFolder, (void **)&psf);
  1592.         }
  1593.     }
  1594.     if (SUCCEEDED(hr))
  1595.     {
  1596.         _psf = psf;
  1597.         _dwTaskPriority = dwTaskPriority;
  1598.     }
  1599.     return hr;
  1600. }
  1601. /*----------------------------------------------------------
  1602. Purpose: IOldStartMenuTask::InitTaskSMT method
  1603. */
  1604. STDMETHODIMP CDesktopTask::InitTaskSMT(IShellHotKey * pshk, int iThreadPriority)
  1605. {
  1606.     ASSERT(pshk);
  1607.     _pshk = pshk;
  1608.     if (_pshk)
  1609.         _pshk->AddRef();
  1610.     return S_OK;
  1611. }
  1612. /*----------------------------------------------------------
  1613. Purpose: CRunnableTask::RunInitRT method
  1614. */
  1615. HRESULT CDesktopTask::RunInitRT(void)
  1616. {
  1617.     // This is the first time that ::Run has been called on this task,
  1618.     // so create our pEnumIDList interface.
  1619.     ASSERT(IS_VALID_CODE_PTR(_psf, IShellFolder));
  1620.     return _psf->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &_pei);
  1621. }
  1622. /*----------------------------------------------------------
  1623. Purpose: CRunnableTask::InternalResumeRT method
  1624. */
  1625. HRESULT CDesktopTask::InternalResumeRT(void)
  1626. {
  1627.     HRESULT hr = NOERROR;
  1628.     LPITEMIDLIST pidl;
  1629.     ULONG cElements;
  1630.     HANDLE hThread = GetCurrentThread();
  1631.     ASSERT(_pei);
  1632.     // Enumerate the entire tree
  1633.     while (NOERROR == _pei->Next(1, &pidl, &cElements))
  1634.     {
  1635.         // Register the hotkey
  1636.         if (_pshk)
  1637.             _pshk->RegisterHotKey(_psf, _pidlFolder, pidl);
  1638.         ILFree(pidl);
  1639.         if (WAIT_OBJECT_0 == WaitForSingleObject(_hDone, 0))
  1640.         {
  1641.             // The _hDone is signaled, so we are either being suspended or killed.
  1642.             if (_hDone)
  1643.                 ResetEvent(_hDone);
  1644.         
  1645.             hr = E_PENDING;
  1646.             break;
  1647.         }
  1648.     }
  1649.     if (_lState == IRTIR_TASK_PENDING)
  1650.     {
  1651.         // We were told to quit, so do it.
  1652.         hr = E_FAIL;
  1653.     }
  1654.     if (_lState != IRTIR_TASK_SUSPENDED)
  1655.     {
  1656.         // We weren't suspended, so we must be finished
  1657.         SUPERCLASS::InternalResumeRT();
  1658. #ifdef FULL_DEBUG
  1659.         TraceMsg(TF_SMTASK, "DesktopTask (%#lx): Finished. Total time = %ld", _dwTaskID, GetTickCount()-_dwTaskID);
  1660. #endif
  1661.     }
  1662.     
  1663.     return hr;
  1664. }
  1665. // For the Favorites menu, since their icon handler is SO slow, we're going to fake the icon
  1666. // and have it get the real ones on the background thread...
  1667. HRESULT CStartMenuCallback::_GetDefaultIcon(TCHAR* psz, int* piIndex)
  1668. {
  1669.     DWORD cbSize = sizeof(TCHAR) * MAX_PATH;
  1670.     HKEY hkeyT;
  1671.     if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, TEXT("InternetShortcut\DefaultIcon"), &hkeyT))
  1672.     {
  1673.         if (ERROR_SUCCESS == SHQueryValueEx(hkeyT, NULL, NULL, NULL, 
  1674.                                           (LPBYTE)psz, &cbSize))
  1675.         {
  1676.             *piIndex = PathParseIconLocation(psz);
  1677.         }
  1678.         RegCloseKey(hkeyT);
  1679.     }
  1680.     return S_OK;
  1681. }