// NOTE! Shdoc401 issue: Start | Find needs to check what version of shell32 user is running // because of the way we pack icons in menu item info // that code is under #ifdef SHDOC401_DLL ... #endif (reljai) #include "stdafx.h" #pragma hdrstop #include #include #include "apithk.h" #include "resource.h" #include // CLSID copied from the Network Connections folder. const CLSID CLSID_ConnectionFolder = {0x7007ACC7,0x3202,0x11D1,{0xAA,0xD2,0x00,0x80,0x5F,0xC1,0x27,0x0E}}; #define REGSTR_EXPLORER_ADVANCED REGSTR_PATH_EXPLORER TEXT("\\Advanced") #define REGSTR_EXPLORER_WINUPDATE REGSTR_PATH_EXPLORER TEXT("\\WindowsUpdate") #define TF_MENUBAND 0x00002000 // Menu band messages #define IDM_TOPLEVELSTARTMENU 0 // StartMenuInit Flags #define STARTMENU_DISPLAYEDBEFORE 0x00000001 #define STARTMENU_CHEVRONCLICKED 0x00000002 // New item counts for UEM stuff #define UEM_NEWITEMCOUNT 2 // Menuband per pane user data typedef struct { BITBOOL _fInitialized; } SMUSERDATA; // for g_hdpaDarwinAds BOOL g_fCritSectionInit = FALSE; CRITICAL_SECTION g_csDarwinAds = {0}; #define ENTERCRITICAL_DARWINADS EnterCriticalSection(&g_csDarwinAds) #define LEAVECRITICAL_DARWINADS LeaveCriticalSection(&g_csDarwinAds) // The threading concern with this variable is create/delete/add/remove. We will only remove an item // and delete the hdpa on the main thread. We will however add and create on both threads. // We need to serialize access to the dpa, so we're going to grab the shell crisec. HDPA g_hdpaDarwinAds = NULL; int g_cStartMenuTask = 0; class CDarwinAd { public: LPITEMIDLIST _pidl; LPTSTR _pszDescriptor; BOOL _fIsAd; CDarwinAd(LPITEMIDLIST pidl, LPTSTR psz) { // I take ownership of this pidl _pidl = pidl; Str_SetPtr(&_pszDescriptor, psz); CheckInstalled(); } void CheckInstalled() { _fIsAd = IsDarwinAd(_pszDescriptor); } BOOL IsAd() { return _fIsAd; } ~CDarwinAd() { ILFree(_pidl); Str_SetPtr(&_pszDescriptor, NULL); } }; BOOL AreIntelliMenusEnbaled() { DWORD dwRest = SHRestricted(REST_INTELLIMENUS); if (dwRest != RESTOPT_INTELLIMENUS_USER) return (dwRest == RESTOPT_INTELLIMENUS_ENABLED); return SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("IntelliMenus"), FALSE, TRUE); // Don't ignore HKCU, Enable Menus by default } BOOL FeatureEnabled(LPTSTR pszFeature) { return SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, pszFeature, FALSE, // Don't ignore HKCU FALSE); // Disable this cool feature. } // Since we can be presented with an Augmented shellfolder and we need a Full pidl, // we have been given the responsibility to unwrap it for perf reasons. LPITEMIDLIST FullPidlFromSMData(LPSMDATA psmd) { LPITEMIDLIST pidlItem; LPITEMIDLIST pidlFolder; LPITEMIDLIST pidlFull = NULL; IAugmentedShellFolder2* pasf2; if (SUCCEEDED(psmd->psf->QueryInterface(IID_IAugmentedShellFolder2, (LPVOID*)&pasf2))) { if( SUCCEEDED(pasf2->UnWrapIDList(psmd->pidlItem, 1, NULL, &pidlFolder, &pidlItem, NULL))) { pidlFull = ILCombine(pidlFolder, pidlItem); ILFree(pidlFolder); ILFree(pidlItem); } pasf2->Release(); } if (!pidlFolder) { pidlFull = ILCombine(psmd->pidlFolder, psmd->pidlItem); } return pidlFull; } BOOL ProcessDarwinAd(IShellLinkDataList* psldl, LPCITEMIDLIST pidlFull) { // This function does not check for the existance of a member before adding it, // so it is entirely possible for there to be duplicates in the list.... BOOL fIsLoaded = FALSE; BOOL fFreesldl = FALSE; BOOL fRetVal = FALSE; // If the critical section has not been initialized yet, then we don't need to process this // because the background thread will get it. if (!g_fCritSectionInit) return FALSE; if (!psldl) { // We will detect failure of this at use time. if (FAILED(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC, IID_IShellLinkDataList, (void**)&psldl))) { return FALSE; } fFreesldl = TRUE; IPersistFile* ppf; OLECHAR sz[MAX_PATH]; if (SHGetPathFromIDListW(pidlFull, sz)) { if (SUCCEEDED(psldl->QueryInterface(IID_IPersistFile, (void**)&ppf))) { if (SUCCEEDED(ppf->Load(sz, 0))) { fIsLoaded = TRUE; } ppf->Release(); } } } else fIsLoaded = TRUE; CDarwinAd* pda = NULL; if (fIsLoaded) { EXP_DARWIN_LINK* pexpDarwin; if (SUCCEEDED(psldl->CopyDataBlock(EXP_DARWIN_ID_SIG, (void**)&pexpDarwin))) { #ifdef UNICODE pda = new CDarwinAd(ILClone(pidlFull), pexpDarwin->szwDarwinID); #else pda = new CDarwinAd(ILClone(pidlFull), pexpDarwin->szDarwinID); #endif LocalFree(pexpDarwin); } } if (pda) { // When accessing the global HDPA, get the critical section. ENTERCRITICAL_DARWINADS; // Do we have a global cache? if (g_hdpaDarwinAds == NULL) { // No; This is either the first time this is called, or we // failed the last time. g_hdpaDarwinAds = DPA_Create(5); } if (g_hdpaDarwinAds) { // DPA_AppendPtr returns the zero based index it inserted it at. if(DPA_AppendPtr(g_hdpaDarwinAds, (void*)pda) >= 0) { fRetVal = TRUE; } } LEAVECRITICAL_DARWINADS; if (!fRetVal) { // if we failed to create a dpa, delete this. delete pda; } } if (fFreesldl) psldl->Release(); return fRetVal; } // This routine creates the IShellFolder and pidl for the Fast items // section of the start menu. HRESULT GetFastItemFolder(IShellFolder** ppsf, LPITEMIDLIST* ppidl) { HRESULT hres; LPITEMIDLIST pidlFast = NULL; #ifndef NO_MERGEDSHELLFOLDER IAugmentedShellFolder2 * pasf = NULL; hres = CoCreateInstance(CLSID_AugmentedShellFolder2, NULL, CLSCTX_INPROC, IID_IAugmentedShellFolder2, (void **)&pasf); #else IAugmentedShellFolder * pasf = NULL; hres = CoCreateInstance(CLSID_AugmentedShellFolder, NULL, CLSCTX_INPROC, IID_IAugmentedShellFolder, (void **)&pasf); #endif if (SUCCEEDED(hres)) { hres = SHGetSpecialFolderLocation(NULL, CSIDL_STARTMENU, &pidlFast); if (SUCCEEDED(hres)) { IShellFolder* psfFast; //For Fast items on Start Menu hres = SHBindToObject(NULL, IID_IShellFolder, pidlFast, (void **)&psfFast); if (SUCCEEDED(hres)) { pasf->AddNameSpace(NULL, psfFast, pidlFast, ASFF_DEFAULT | ASFF_DEFNAMESPACE_ALL); psfFast->Release(); } #ifdef WINNT LPITEMIDLIST pidlFastCommon; if (!SHRestricted(REST_NOCOMMONGROUPS) && SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_STARTMENU, &pidlFastCommon))) { IShellFolder* psfFastCommon; if (SUCCEEDED(SHBindToObject(NULL, IID_IShellFolder, pidlFastCommon, (void **)&psfFastCommon))) { pasf->AddNameSpace(NULL, psfFastCommon, pidlFastCommon, ASFF_DEFAULT); psfFastCommon->Release(); } ILFree(pidlFastCommon); } #endif } } if (FAILED(hres)) { ATOMICRELEASE(pasf); ILFree(pidlFast); pidlFast = NULL; } *ppidl = pidlFast; *ppsf = pasf; return hres; } HRESULT GetNetConnectPidl(LPITEMIDLIST* ppidl) { // We now need to generate a pidl for this item. // To do this, we create a string that contains :: // then pass it through Desktop::ParseDisplayName. // This generates a pidl to the parent. DWORD dwEaten; IShellFolder* psfDesktop = NULL; HRESULT hres = E_FAIL; WCHAR wszItem[MAX_PATH] = L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\" // CLSID_MyComputer L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\" // CLSID_ControlPanel L"::{7007ACC7-3202-11D1-AAD2-00805FC1270E}"; // CLSID_NetworkConnections SHGetDesktopFolder(&psfDesktop); if (psfDesktop) { hres = psfDesktop->ParseDisplayName(NULL, NULL, wszItem, &dwEaten, ppidl, NULL); psfDesktop->Release(); } return hres; } void SetFilesystemInfo(IShellFolder* psf, LPCITEMIDLIST pidlRoot, int csidl) { ASSERT(psf); IPersistFolder3* ppf; if (SUCCEEDED(psf->QueryInterface(IID_IPersistFolder3, (void**)&ppf))) { PERSIST_FOLDER_TARGET_INFO pfti = {0}; pfti.dwAttributes = -1; pfti.csidl = csidl; ppf->InitializeEx(NULL, pidlRoot, &pfti); ppf->Release(); } } HRESULT GetFilesystemInfo(IShellFolder* psf, LPITEMIDLIST* ppidlRoot, int* pcsidl) { ASSERT(psf); IPersistFolder3* ppf; HRESULT hres = E_FAIL; *pcsidl = 0; *ppidlRoot = 0; if (SUCCEEDED(psf->QueryInterface(IID_IPersistFolder3, (void**)&ppf))) { PERSIST_FOLDER_TARGET_INFO pfti = {0}; if(SUCCEEDED(ppf->GetFolderTargetInfo(&pfti))) { *pcsidl = pfti.csidl; if (-1 != pfti.csidl) hres = S_OK; ILFree(pfti.pidlTargetFolder); } if (SUCCEEDED(hres)) hres = ppf->GetCurFolder(ppidlRoot); ppf->Release(); } return hres; } HRESULT ExecStaticStartMenuItem(int idCmd, BOOL fAllUsers, BOOL fOpen) { int csidl = -1; HRESULT hres = E_OUTOFMEMORY; SHELLEXECUTEINFO shei = {0}; switch(idCmd) { case IDM_PROGRAMS: csidl = fAllUsers ? CSIDL_COMMON_PROGRAMS : CSIDL_PROGRAMS; break; case IDM_FAVORITES: csidl = CSIDL_FAVORITES; break; case IDM_MYDOCUMENTS: shei.lpIDList = MyDocsIDList(); break; case IDM_CONTROLS: csidl = CSIDL_CONTROLS; break; case IDM_PRINTERS: csidl = CSIDL_PRINTERS; break; case IDM_NETCONNECT: GetNetConnectPidl((LPITEMIDLIST*)&shei.lpIDList); break; default: return E_FAIL; } if (csidl != -1) { SHGetSpecialFolderLocation(NULL, csidl, (LPITEMIDLIST*)&shei.lpIDList); } if (shei.lpIDList) { shei.cbSize = sizeof(shei); shei.fMask = SEE_MASK_IDLIST; shei.nShow = SW_SHOWNORMAL; shei.lpVerb = fOpen? TEXT("open") : TEXT("explore"); hres = ShellExecuteEx(&shei)? S_OK: E_FAIL; ILFree((LPITEMIDLIST)shei.lpIDList); } return hres; } // IShellMenuCallback implementation class CStartMenuCallback : public IShellMenuCallback, public CObjectWithSite { public: // *** IUnknown methods *** STDMETHODIMP QueryInterface (REFIID riid, void ** ppvObj); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // *** IShellMenuCallback methods *** STDMETHODIMP CallbackSM(LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam); // *** IObjectWithSite methods *** STDMETHODIMP SetSite(IUnknown* punk); STDMETHODIMP GetSite(REFIID riid, void** ppvOut); CStartMenuCallback(); private: virtual ~CStartMenuCallback(); int _cRef; DEBUG_CODE( DWORD _dwThreadID; ) // Cache the thread of the object IContextMenu* _pcmFind; TCHAR _szPrograms[MAX_PATH]; TCHAR _szProgramsPath[MAX_PATH]; TCHAR _szCommonProgramsPath[MAX_PATH]; UINT _cchCommonProgramsPath; UINT _cchProgramsPath; LPTSTR _pszWindowsUpdate; LPTSTR _pszAdminTools; ITrayPriv* _ptp; IUnknown* _punkSite; IOleCommandTarget* _poct; BITBOOL _fExpandoMenus: 1; BITBOOL _fAddOpenFolder: 1; BITBOOL _fCascadeMyDocuments: 1; BITBOOL _fCascadePrinters: 1; BITBOOL _fCascadeControlPanel: 1; BITBOOL _fScrollPrograms: 1; BITBOOL _fFindMenuInvalid: 1; BITBOOL _fShowAdminTools: 1; BITBOOL _fCascadeNetConnections: 1; BITBOOL _fInitPrograms: 1; BITBOOL _fShowInfoTip: 1; BITBOOL _fBkThreadDoneOnLastShow: 1; TCHAR _szFindMeumonic[2]; HWND _hwnd; HANDLE _hmruRecent; DWORD _cRecentDocs; DWORD _dwFlags; DWORD _dwChevronCount; HRESULT _ExecHmenuItem(LPSMDATA psmdata); HRESULT _Init(SMDATA* psmdata); HRESULT _Create(SMDATA* psmdata, void** pvUserData); HRESULT _Destroy(SMDATA* psmdata); HRESULT _GetHmenuInfo(SMDATA* psmd, SMINFO*sminfo); HRESULT _GetSFInfo(SMDATA* psmd, SMINFO* psminfo); HRESULT _GetObject(LPSMDATA psmd, REFIID riid, void** ppvObj); HRESULT _CheckRestricted(DWORD dwRestrict, BOOL* fRestricted); HRESULT _FilterPidl(UINT uParent, IShellFolder* psf, LPCITEMIDLIST pidl); HRESULT _FilterFavorites(IShellFolder* psf, LPCITEMIDLIST pidl); HRESULT _FilterRecentPidl(IShellFolder* psf, LPCITEMIDLIST pidl); HRESULT _Demote(LPSMDATA psmd); HRESULT _Promote(LPSMDATA psmd); HRESULT _HandleNew(LPSMDATA psmd); HRESULT _GetTip(LPTSTR pstrTitle, LPTSTR pstrTip); DWORD _GetDemote(SMDATA* psmd); HRESULT _ProcessChangeNotify(SMDATA* psmd, LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); HRESULT _HandleAccelerator(TCHAR ch, SMDATA* psmdata); HRESULT _GetDefaultIcon(TCHAR* psz, int* piIndex); void _GetStaticStartMenu(HMENU* phmenu, HWND* phwnd); HRESULT _GetStaticInfoTip(SMDATA* psmd, LPTSTR pszTip, int cch); void _ReValidateDarwinCache(); static STDMETHODIMP_(int) s_DarwinAdsDestroyCallback(LPVOID pData1, LPVOID pData2); // helper functions DWORD GetInitFlags(); void SetInitFlags(DWORD dwFlags); HRESULT _InitializeFindMenu(IShellMenu* psm); int _GetDarwinIndex(LPCITEMIDLIST pidlFull, CDarwinAd** ppda); BOOL _IsDarwinAdvertisement(LPCITEMIDLIST pidlFull); HRESULT _ExecItem(LPSMDATA, UINT); HRESULT VerifyCSIDL(int idCmd, int csidl, IShellMenu* psm); HRESULT VerifyMergedGuy(BOOL fPrograms, IShellMenu* psm); void _InitializePrograms(); void _UpdateMyDocsMenuItemName(IShellMenu* psm); public: // Make these public to this file. This is for the CreateInstance // Sub Menu creation HRESULT InitializeFastItemsShellMenu(IShellMenu* psm); HRESULT InitializeProgramsShellMenu(IShellMenu* psm); HRESULT InitializeCSIDLShellMenu(int uId, int csidl, LPTSTR pszRoot, LPTSTR pszValue, DWORD dwPassInitFlags, DWORD dwSetFlags, BOOL fAddOpen, IShellMenu* psm); HRESULT InitializeDocumentsShellMenu(IShellMenu* psm); HRESULT InitializeSubShellMenu(int idCmd, IShellMenu* psm); HRESULT InitializeNetworkConnections(IShellMenu* psm); }; class CStartContextMenu : IContextMenu { public: // IUnknown STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj); STDMETHOD_(ULONG,AddRef)(void); STDMETHOD_(ULONG,Release)(void); // IContextMenu STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags); STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpici); STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax); CStartContextMenu(int idCmd) : _idCmd(idCmd), _cRef(1) {}; private: int _cRef; virtual ~CStartContextMenu() {}; int _idCmd; }; CStartMenuCallback::CStartMenuCallback() : _cRef(1), _cRecentDocs(-1) { DEBUG_CODE( _dwThreadID = GetCurrentThreadId() ); TCHAR szWindowsUpdate[MAX_PATH]; DWORD cbSize = ARRAYSIZE(szWindowsUpdate); if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, REGSTR_EXPLORER_WINUPDATE, TEXT("ShortcutName"), NULL, szWindowsUpdate, &cbSize)) { Str_SetPtr(&_pszWindowsUpdate, szWindowsUpdate); } TCHAR szPath[MAX_PATH]; if (SHGetSpecialFolderPath(NULL, szPath, CSIDL_COMMON_ADMINTOOLS, TRUE)) { Str_SetPtr(&_pszAdminTools, PathFindFileName(szPath)); } LoadString(g_hinst, IDS_FIND_MEUMONIC, _szFindMeumonic, ARRAYSIZE(_szFindMeumonic)); } int CStartMenuCallback::s_DarwinAdsDestroyCallback(LPVOID pData1, LPVOID pData2) { CDarwinAd* pda = (CDarwinAd*)pData1; if (pda) delete pda; return 0; } CStartMenuCallback::~CStartMenuCallback() { ASSERT( _dwThreadID == GetCurrentThreadId() ); if (_hmruRecent) FreeMRUList(_hmruRecent); ATOMICRELEASE(_pcmFind); ATOMICRELEASE(_ptp); Str_SetPtr(&_pszWindowsUpdate, NULL); Str_SetPtr(&_pszAdminTools, NULL); if (g_fCritSectionInit) { ENTERCRITICAL_DARWINADS; HDPA hdpa = g_hdpaDarwinAds; g_hdpaDarwinAds = NULL; LEAVECRITICAL_DARWINADS; if (hdpa) DPA_DestroyCallback(hdpa, s_DarwinAdsDestroyCallback, NULL); } } // *** IUnknown methods *** STDMETHODIMP CStartMenuCallback::QueryInterface(REFIID riid, void ** ppvObj) { static const QITAB qit[] = { QITABENT(CStartMenuCallback, IShellMenuCallback), QITABENT(CStartMenuCallback, IObjectWithSite), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } STDMETHODIMP_(ULONG) CStartMenuCallback::AddRef() { return ++_cRef; } STDMETHODIMP_(ULONG) CStartMenuCallback::Release() { ASSERT(_cRef > 0); _cRef--; if( _cRef > 0) return _cRef; delete this; return 0; } STDMETHODIMP CStartMenuCallback::SetSite(IUnknown* punk) { ATOMICRELEASE(_punkSite); _punkSite = punk; if (punk) { _punkSite->AddRef(); } return NOERROR; } STDMETHODIMP CStartMenuCallback::GetSite(REFIID riid, void**ppvOut) { if (_ptp) return _ptp->QueryInterface(riid, ppvOut); else return E_NOINTERFACE; } #ifdef DEBUG void DBUEMQueryEvent(const IID *pguidGrp, int eCmd, WPARAM wParam, LPARAM lParam) { #if 1 return; #else UEMINFO uei; uei.cbSize = SIZEOF(uei); uei.dwMask = ~0; // UEIM_HIT etc. UEMQueryEvent(pguidGrp, eCmd, wParam, lParam, &uei); TCHAR szBuf[20]; wsprintf(szBuf, TEXT("hit=%d"), uei.cHit); MessageBox(NULL, szBuf, TEXT("UEM"), MB_OKCANCEL); return; #endif } #endif DWORD CStartMenuCallback::GetInitFlags() { DWORD dwType; DWORD cbSize = sizeof(DWORD); DWORD dwFlags = 0; SHGetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED, TEXT("StartMenuInit"), &dwType, (BYTE*)&dwFlags, &cbSize); return dwFlags; } void CStartMenuCallback::SetInitFlags(DWORD dwFlags) { SHSetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED, TEXT("StartMenuInit"), REG_DWORD, &dwFlags, sizeof(DWORD)); } DWORD GetClickCount() { //This function retrieves the number of times the user has clicked on the chevron item. DWORD dwType; DWORD cbSize = sizeof(DWORD); DWORD dwCount = 1; // Default to three clicks before we give up. // PMs what it to 1 now. Leaving back end in case they change their mind. SHGetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED, TEXT("StartMenuChevron"), &dwType, (BYTE*)&dwCount, &cbSize); return dwCount; } void SetClickCount(DWORD dwClickCount) { SHSetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED, TEXT("StartMenuChevron"), REG_DWORD, &dwClickCount, sizeof(DWORD)); } STDMETHODIMP CStartMenuCallback::CallbackSM(LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam) { HRESULT hres = S_FALSE; switch (uMsg) { case SMC_CREATE: hres = _Create(psmd, (void**)lParam); break; case SMC_DESTROY: hres = _Destroy(psmd); break; case SMC_INITMENU: _Init(psmd); break; case SMC_SFEXEC: hres = _ExecItem(psmd, uMsg); break; case SMC_EXEC: hres = _ExecHmenuItem(psmd); break; case SMC_GETOBJECT: hres = _GetObject(psmd, (GUID)*((GUID*)wParam), (void**)lParam); break; case SMC_GETINFO: hres = _GetHmenuInfo(psmd, (SMINFO*)lParam); break; case SMC_GETSFINFOTIP: if (!_fShowInfoTip) hres = E_FAIL; // E_FAIL means don't show. S_FALSE means show default break; case SMC_GETINFOTIP: hres = _GetStaticInfoTip(psmd, (LPTSTR)wParam, (int)lParam); break; case SMC_GETSFINFO: hres = _GetSFInfo(psmd, (SMINFO*)lParam); break; case SMC_BEGINENUM: if (psmd->uIdParent == IDM_TOPLEVELSTARTMENU && SHRestricted(REST_NOSTARTMENUSUBFOLDERS)) { ASSERT(wParam != NULL); *(DWORD*)wParam = SHCONTF_NONFOLDERS; hres = S_OK; } else if (psmd->uIdParent == IDM_RECENT) { ASSERT(_cRecentDocs == -1); ASSERT(!_hmruRecent); _hmruRecent = CreateSharedRecentMRUList(NULL, NULL, SRMLF_COMPPIDL); _cRecentDocs = 0; hres = S_OK; } break; case SMC_ENDENUM: if (psmd->uIdParent == IDM_RECENT) { ASSERT(_cRecentDocs != -1); if (_hmruRecent) { FreeMRUList(_hmruRecent); _hmruRecent = NULL; } _cRecentDocs = -1; hres = S_OK; } break; case SMC_FILTERPIDL: ASSERT(psmd->dwMask & SMDM_SHELLFOLDER); if (psmd->uIdAncestor == IDM_FAVORITES) { hres = _FilterFavorites(psmd->psf, psmd->pidlItem); } else if (psmd->uIdParent == IDM_RECENT) { // we need to filter out all but the first MAXRECENTITEMS // and no folders allowed! hres = _FilterRecentPidl(psmd->psf, psmd->pidlItem); } else { hres = _FilterPidl(psmd->uIdParent, psmd->psf, psmd->pidlItem); } break; case SMC_INSERTINDEX: ASSERT(lParam && IS_VALID_WRITE_PTR(lParam, int)); *((int*)lParam) = 0; hres = S_OK; break; case SMC_SHCHANGENOTIFY: { PSMCSHCHANGENOTIFYSTRUCT pshf = (PSMCSHCHANGENOTIFYSTRUCT)lParam; hres = _ProcessChangeNotify(psmd, pshf->lEvent, pshf->pidl1, pshf->pidl2); } break; case SMC_REFRESH: { if (psmd->uIdParent == IDM_TOPLEVELSTARTMENU) { // We need to refresh Shell32's cache. TCHAR szSetting[MAX_PATH]; LPTSTR psz = NULL; // NOTE: All parameters passed to this interface are UNICODE. We need to convert // to shell32's character format. if (lParam) { SHUnicodeToTChar((LPWSTR)lParam, szSetting, ARRAYSIZE(szSetting)); psz = szSetting; } SHSettingsChanged(wParam, (LPARAM)psz); // Refresh is only called on the top level. HMENU hmenu; IShellMenu* psm; _GetStaticStartMenu(&hmenu, &_hwnd); if (psmd->punk && SUCCEEDED(psmd->punk->QueryInterface(IID_IShellMenu, (LPVOID*)&psm))) { psm->SetMenu(hmenu, _hwnd, SMSET_BOTTOM | SMSET_MERGE); psm->Release(); } _fExpandoMenus = AreIntelliMenusEnbaled(); _fCascadeMyDocuments = FeatureEnabled(TEXT("CascadeMyDocuments")); _fCascadePrinters = FeatureEnabled(TEXT("CascadePrinters")); _fCascadeControlPanel = FeatureEnabled(TEXT("CascadeControlPanel")); _fScrollPrograms = FeatureEnabled(TEXT("StartMenuScrollPrograms")); _fShowAdminTools = FeatureEnabled(TEXT("StartMenuAdminTools")); _fCascadeNetConnections = FeatureEnabled(TEXT("CascadeNetworkConnections")); _fAddOpenFolder = FeatureEnabled(TEXT("StartMenuOpen")); _fShowInfoTip = FeatureEnabled(TEXT("ShowInfoTip")); _fFindMenuInvalid = TRUE; _dwFlags = GetInitFlags(); hres = S_OK; } } break; case SMC_DEMOTE: hres = _Demote(psmd); break; case SMC_PROMOTE: hres = _Promote(psmd); break; case SMC_NEWITEM: hres = _HandleNew(psmd); break; case SMC_MAPACCELERATOR: hres = _HandleAccelerator((TCHAR)wParam, (SMDATA*)lParam); break; case SMC_DEFAULTICON: ASSERT(psmd->uIdAncestor == IDM_FAVORITES); // This is only valid for the Favorites menu hres = _GetDefaultIcon((LPTSTR)wParam, (int*)lParam); break; case SMC_GETMINPROMOTED: // Only do this for the programs menu if (psmd->uIdParent == IDM_PROGRAMS) *((int*)lParam) = 4; // 4 was choosen by RichSt 9.15.98 break; case SMC_CHEVRONEXPAND: // Has the user already seen the chevron tip enough times? (We set the bit when the count goes to zero. if (!(_dwFlags & STARTMENU_CHEVRONCLICKED)) { // No; Then get the current count from the registry. We set a default of 3, but an admin can set this // to -1, that would make it so that they user sees it all the time. DWORD dwClickCount = GetClickCount(); if (dwClickCount > 0) { // Since they clicked, take one off. dwClickCount--; // Set it back in. SetClickCount(dwClickCount); } if (dwClickCount == 0) { // Ah, the user has seen the chevron tip enought times... Stop being annoying. _dwFlags |= STARTMENU_CHEVRONCLICKED; SetInitFlags(_dwFlags); } } hres = S_OK; break; case SMC_DISPLAYCHEVRONTIP: // We only want to see the tip on the top level programs case, no where else. We also don't // want to see it if they've had enough. if (psmd->uIdParent == IDM_PROGRAMS && !(_dwFlags & STARTMENU_CHEVRONCLICKED) && !SHRestricted(REST_NOSMBALLOONTIP)) { hres = S_OK; } break; case SMC_CHEVRONGETTIP: if (!SHRestricted(REST_NOSMBALLOONTIP)) hres = _GetTip((LPTSTR)wParam, (LPTSTR)lParam); break; } return hres; } // For the Favorites menu, since their icon handler is SO slow, we're going to fake the icon // and have it get the real ones on the background thread... HRESULT CStartMenuCallback::_GetDefaultIcon(TCHAR* psz, int* piIndex) { HRESULT hres; DWORD cbSize = sizeof(TCHAR) * MAX_PATH; hres = SHGetValue(HKEY_CLASSES_ROOT, TEXT("InternetShortcut\\DefaultIcon"), NULL, NULL, psz, &cbSize); if (ERROR_SUCCESS == hres) { *piIndex = PathParseIconLocation(psz); } return hres; } HRESULT CStartMenuCallback::_ExecItem(LPSMDATA psmd, UINT uMsg) { ASSERT( _dwThreadID == GetCurrentThreadId() ); return _ptp->ExecItem(psmd->psf, psmd->pidlItem); } HRESULT CStartMenuCallback::_Demote(LPSMDATA psmd) { //We want to for the UEM to demote pidlFolder, // then tell the Parent menuband (If there is one) // to invalidate this pidl. HRESULT hres = S_FALSE; if (_fExpandoMenus && (psmd->uIdAncestor == IDM_PROGRAMS || psmd->uIdAncestor == IDM_FAVORITES)) { UEMINFO uei; uei.cbSize = SIZEOF(uei); uei.dwMask = UEIM_HIT; uei.cHit = 0; hres = UEMSetEvent(psmd->uIdAncestor == IDM_PROGRAMS? &UEMIID_SHELL : &UEMIID_BROWSER, UEME_RUNPIDL, (WPARAM)psmd->psf, (LPARAM)psmd->pidlItem, &uei); } return hres; } HRESULT CStartMenuCallback::_Promote(LPSMDATA psmd) { if (_fExpandoMenus && (psmd->uIdAncestor == IDM_PROGRAMS || psmd->uIdAncestor == IDM_FAVORITES)) { UEMFireEvent(psmd->uIdAncestor == IDM_PROGRAMS? &UEMIID_SHELL : &UEMIID_BROWSER, UEME_RUNPIDL, UEMF_XEVENT, (WPARAM)psmd->psf, (LPARAM)psmd->pidlItem); } return S_OK; } HRESULT CStartMenuCallback::_HandleNew(LPSMDATA psmd) { HRESULT hres = S_FALSE; if (_fExpandoMenus && (psmd->uIdAncestor == IDM_PROGRAMS || psmd->uIdAncestor == IDM_FAVORITES)) { UEMINFO uei; uei.cbSize = SIZEOF(uei); uei.dwMask = UEIM_HIT; uei.cHit = UEM_NEWITEMCOUNT; hres = UEMSetEvent(psmd->uIdAncestor == IDM_PROGRAMS? &UEMIID_SHELL : &UEMIID_BROWSER, UEME_RUNPIDL, (WPARAM)psmd->psf, (LPARAM)psmd->pidlItem, &uei); } if (psmd->uIdAncestor == IDM_PROGRAMS) { LPITEMIDLIST pidlFull = FullPidlFromSMData(psmd); if (pidlFull) { ProcessDarwinAd(NULL, pidlFull); ILFree(pidlFull); } } return hres; } void ShowPidl(HWND hwnd, LPITEMIDLIST pidl, UINT uFlags) { SHELLEXECUTEINFO shei = { 0 }; shei.cbSize = sizeof(shei); shei.fMask = SEE_MASK_IDLIST; shei.nShow = SW_SHOWNORMAL; shei.lpVerb = TEXT("open"); shei.lpIDList = pidl; ShellExecuteEx(&shei); } void ShowFolder(HWND hwnd, UINT csidl, UINT uFlags) { LPITEMIDLIST pidl; if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, csidl, (LPITEMIDLIST*)&pidl))) { ShowPidl(hwnd, pidl, uFlags); ILFree(pidl); } } void _ExecRegValue(LPCTSTR pszValue) { TCHAR szPath[MAX_PATH]; DWORD cbSize = ARRAYSIZE(szPath); if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, REGSTR_EXPLORER_ADVANCED, pszValue, NULL, szPath, &cbSize)) { SHELLEXECUTEINFO shei= { 0 }; shei.cbSize = sizeof(shei); shei.nShow = SW_SHOWNORMAL; shei.lpParameters = PathGetArgs(szPath); PathRemoveArgs(szPath); shei.lpFile = szPath; ShellExecuteEx(&shei); } } HRESULT CStartMenuCallback::_ExecHmenuItem(LPSMDATA psmd) { HRESULT hres = S_FALSE; if (IsInRange(psmd->uId, TRAY_IDM_FINDFIRST, TRAY_IDM_FINDLAST) && _pcmFind) { CMINVOKECOMMANDINFOEX ici = { 0 }; ici.cbSize = SIZEOF(CMINVOKECOMMANDINFOEX); ici.lpVerb = (LPSTR)MAKEINTRESOURCE(psmd->uId - TRAY_IDM_FINDFIRST); ici.nShow = SW_NORMAL; // record if shift or control was being held down SetICIKeyModifiers(&ici.fMask); _pcmFind->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici); hres = S_OK; } else { switch (psmd->uId) { case IDM_OPEN_FOLDER: switch(psmd->uIdParent) { case IDM_CONTROLS: ShowFolder(NULL, CSIDL_CONTROLS, 0); hres = NOERROR; break; case IDM_PRINTERS: ShowFolder(NULL, CSIDL_PRINTERS, 0); hres = NOERROR; break; case IDM_NETCONNECT: goto ShowNetConnect; case IDM_MYDOCUMENTS: goto ShowMyDocs; } break; case IDM_NETCONNECT: ShowNetConnect: { LPITEMIDLIST pidl; if (SUCCEEDED(GetNetConnectPidl(&pidl))) { ASSERT(NULL != pidl); ShowPidl(NULL, pidl, 0); hres = NOERROR; ILFree(pidl); } } break; case IDM_MYDOCUMENTS: ShowMyDocs: { LPITEMIDLIST pidl = MyDocsIDList(); if (NULL != pidl) { ShowPidl(NULL, pidl, 0); hres = NOERROR; ILFree(pidl); } } break; case IDM_CSC: _ExecRegValue(TEXT("StartMenuSyncAll")); break; default: hres = ExecStaticStartMenuItem(psmd->uId, FALSE, TRUE); break; } } return hres; } void CStartMenuCallback::_GetStaticStartMenu(HMENU* phmenu, HWND* phwnd) { IMenuPopup* pmp; // The first one should be the bar that the start menu is sitting in. if (_punkSite && SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SMenuPopup, IID_IMenuPopup, (void**)&pmp))) { IObjectWithSite* pows; if (SUCCEEDED(pmp->QueryInterface(IID_IObjectWithSite, (void**)&pows))) { // It's site should be CStartMenuHost; if (SUCCEEDED(pows->GetSite(IID_ITrayPriv, (void**)&_ptp))) { IOleWindow* pow; _ptp->GetStaticStartMenu(phmenu); if (SUCCEEDED(_ptp->QueryInterface(IID_IOleWindow, (void**)&pow))) { pow->GetWindow(phwnd); pow->Release(); } if (!_poct) _ptp->QueryInterface(IID_IOleCommandTarget, (void**)&_poct); } else TraceMsg(TF_MENUBAND, "CStartMenuCallback::_SetSite : Failed to aquire CStartMenuHost"); pows->Release(); } pmp->Release(); } } HRESULT CStartMenuCallback::_Create(SMDATA* psmdata, void** ppvUserData) { *ppvUserData = new SMUSERDATA; return S_OK; } HRESULT CStartMenuCallback::_Destroy(SMDATA* psmdata) { if (psmdata->pvUserData) { delete (SMUSERDATA*)psmdata->pvUserData; psmdata->pvUserData = NULL; } return S_OK; } void CStartMenuCallback::_InitializePrograms() { if (!_fInitPrograms) { // We're either initing these, or reseting them. SHGetSpecialFolderPath(NULL, _szProgramsPath, CSIDL_PROGRAMS, FALSE); lstrcpy(_szPrograms, PathFindFileName(_szProgramsPath)); _cchProgramsPath = lstrlen(_szProgramsPath); SHGetSpecialFolderPath(NULL, _szCommonProgramsPath, CSIDL_COMMON_PROGRAMS, FALSE); _cchCommonProgramsPath = lstrlen(_szCommonProgramsPath); _fInitPrograms = TRUE; } } // Given a CSIDL and a Shell menu, this will verify if the IShellMenu // is pointing at the same place as the CSIDL is. If not, then it will // update the shell menu to the new location. HRESULT CStartMenuCallback::VerifyCSIDL(int idCmd, int csidl, IShellMenu* psm) { DWORD dwFlags; LPITEMIDLIST pidl; IShellFolder* psf; HRESULT hres = S_OK; if (SUCCEEDED(psm->GetShellFolder(&dwFlags, &pidl, IID_IShellFolder, (void**)&psf))) { psf->Release(); LPITEMIDLIST pidlCSIDL; if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, csidl, &pidlCSIDL))) { // If the pidl of the IShellMenu is not equal to the // SpecialFolder Location, then we need to update it so they are... if (!ILIsEqual(pidlCSIDL, pidl)) { hres = InitializeSubShellMenu(idCmd, psm); } ILFree(pidlCSIDL); } ILFree(pidl); } return hres; } // This code special cases the Programs and Fast items shell menus. It // understands Merging and will check both shell folders in a merged case // to verify that the shell folder is still pointing at that location HRESULT CStartMenuCallback::VerifyMergedGuy(BOOL fPrograms, IShellMenu* psm) { DWORD dwFlags; LPITEMIDLIST pidl; HRESULT hres = S_OK; IAugmentedShellFolder2* pasf; if (SUCCEEDED(psm->GetShellFolder(&dwFlags, &pidl, IID_IAugmentedShellFolder2, (void**)&pasf))) { IShellFolder* psf; // There are 2 things in the merged namespace: CSIDL_PROGRAMS and CSIDL_COMMON_PROGRAMS for (int i = 0; i < 2; i++) { if (SUCCEEDED(pasf->QueryNameSpace(i, 0, &psf))) { int csidl; LPITEMIDLIST pidlFolder; if (SUCCEEDED(GetFilesystemInfo(psf, &pidlFolder, &csidl))) { LPITEMIDLIST pidlCSIDL; if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, csidl, &pidlCSIDL))) { // If the pidl of the IShellMenu is not equal to the // SpecialFolder Location, then we need to update it so they are... if (!ILIsEqual(pidlCSIDL, pidlFolder)) { // Since one of these things has changed, // we need to update the string cache // so that we do proper filtering of // the programs item. _fInitPrograms = FALSE; if (fPrograms) hres = InitializeProgramsShellMenu(psm); else hres = InitializeFastItemsShellMenu(psm); i = 100; // break out of the loop. } ILFree(pidlCSIDL); } ILFree(pidlFolder); } psf->Release(); } } ILFree(pidl); pasf->Release(); } return hres; } void CStartMenuCallback::_UpdateMyDocsMenuItemName(IShellMenu* psm) { if (SHRestricted(REST_NOSMMYDOCS)) return; HMENU hMenu; ASSERT(NULL != psm); if (SUCCEEDED(psm->GetMenu(&hMenu, NULL, NULL))) { MENUITEMINFO mii; TCHAR szMenuName[256]; mii.cbSize = sizeof(mii); mii.fMask = MIIM_TYPE; mii.dwTypeData = szMenuName; mii.cch = ARRAYSIZE(szMenuName); if (::GetMenuItemInfo(hMenu, IDM_MYDOCUMENTS, FALSE, &mii)) { TCHAR szMyDocsName[256]; if (SUCCEEDED(GetMyDocumentsDisplayName(szMyDocsName, ARRAYSIZE(szMyDocsName)))) { if (0 != StrCmp(szMenuName, szMyDocsName)) { // The mydocs name has changed, update the menu item: mii.dwTypeData = szMyDocsName; if (::SetMenuItemInfo(hMenu, IDM_MYDOCUMENTS, FALSE, &mii)) { SMDATA smd; smd.dwMask = SMDM_HMENU; smd.uId = IDM_MYDOCUMENTS; psm->InvalidateItem(&smd, SMINV_ID | SMINV_REFRESH); } } } } } } HRESULT CStartMenuCallback::_Init(SMDATA* psmdata) { HRESULT hres = S_FALSE; IShellMenu* psm; if (psmdata->punk && SUCCEEDED(hres = psmdata->punk->QueryInterface(IID_IShellMenu, (void**)&psm))) { switch(psmdata->uIdParent) { case IDM_TOPLEVELSTARTMENU: if (psmdata->pvUserData && !((SMUSERDATA*)psmdata->pvUserData)->_fInitialized) { TraceMsg(TF_MENUBAND, "CStartMenuCallback::_Init : Initializing Toplevel Start Menu"); ((SMUSERDATA*)psmdata->pvUserData)->_fInitialized = TRUE; HMENU hmenu; TraceMsg(TF_MENUBAND, "CStartMenuCallback::_Init : First Time, and correct parameters"); _GetStaticStartMenu(&hmenu, &_hwnd); if (hmenu) { psm->SetMenu(hmenu, _hwnd, SMSET_BOTTOM); TraceMsg(TF_MENUBAND, "CStartMenuCallback::_Init : SetMenu(HMENU 0x%x, HWND 0x%x", hmenu, _hwnd); } _fExpandoMenus = AreIntelliMenusEnbaled(); _fCascadeMyDocuments = FeatureEnabled(TEXT("CascadeMyDocuments")); _fCascadePrinters = FeatureEnabled(TEXT("CascadePrinters")); _fCascadeControlPanel = FeatureEnabled(TEXT("CascadeControlPanel")); _fScrollPrograms = FeatureEnabled(TEXT("StartMenuScrollPrograms")); _fShowAdminTools = FeatureEnabled(TEXT("StartMenuAdminTools")); _fCascadeNetConnections = FeatureEnabled(TEXT("CascadeNetworkConnections")); _fAddOpenFolder = FeatureEnabled(TEXT("StartMenuOpen")); _fShowInfoTip = FeatureEnabled(TEXT("ShowInfoTip")); _dwFlags = GetInitFlags(); } else if (g_cStartMenuTask == 0 && !_fBkThreadDoneOnLastShow) { _fBkThreadDoneOnLastShow = TRUE; psm->InvalidateItem(NULL, SMINV_REFRESH); } // Verify that the Fast items is still pointing to the right location hres = VerifyMergedGuy(FALSE, psm); break; case IDM_MENU_FIND: if (_fFindMenuInvalid) { hres = _InitializeFindMenu(psm); _fFindMenuInvalid = FALSE; } break; case IDM_PROGRAMS: // Verify the programs menu is still pointing to the right location hres = VerifyMergedGuy(TRUE, psm); break; case IDM_FAVORITES: hres = VerifyCSIDL(IDM_FAVORITES, CSIDL_FAVORITES, psm); break; case IDM_MYDOCUMENTS: hres = VerifyCSIDL(IDM_MYDOCUMENTS, CSIDL_PERSONAL, psm); break; case IDM_RECENT: _UpdateMyDocsMenuItemName(psm); hres = VerifyCSIDL(IDM_RECENT, CSIDL_RECENT, psm); break; case IDM_CONTROLS: hres = VerifyCSIDL(IDM_CONTROLS, CSIDL_CONTROLS, psm); break; case IDM_PRINTERS: hres = VerifyCSIDL(IDM_PRINTERS, CSIDL_PRINTERS, psm); break; } psm->Release(); } return hres; } HRESULT CStartMenuCallback::_GetStaticInfoTip(SMDATA* psmd, LPTSTR pszTip, int cch) { if (!_fShowInfoTip) return E_FAIL; HRESULT hres = E_FAIL; const static struct { UINT idCmd; UINT idInfoTip; } s_mpcmdTip[] = { #if 0 // No tips for the Toplevel. Keep this here because I bet that someone will want them... { IDM_PROGRAMS, IDS_PROGRAMS_TIP }, { IDM_FAVORITES, IDS_FAVORITES_TIP }, { IDM_RECENT, IDS_RECENT_TIP }, { IDM_SETTINGS, IDS_SETTINGS_TIP }, { IDM_MENU_FIND, IDS_FIND_TIP }, { IDM_HELPSEARCH, IDS_HELP_TIP }, // Redundant? { IDM_FILERUN, IDS_RUN_TIP }, { IDM_LOGOFF, IDS_LOGOFF_TIP }, { IDM_EJECTPC, IDS_EJECT_TIP }, { IDM_EXITWIN, IDS_SHUTDOWN_TIP }, #endif // Settings Submenu { IDM_CONTROLS, IDS_CONTROL_TIP }, { IDM_PRINTERS, IDS_PRINTERS_TIP }, { IDM_TRAYPROPERTIES, IDS_TRAYPROP_TIP }, { IDM_NETCONNECT, IDS_NETCONNECT_TIP}, // Recent Folder { IDM_MYDOCUMENTS, IDS_MYDOCS_TIP}, }; for (int i = 0; i < ARRAYSIZE(s_mpcmdTip); i++) { if (s_mpcmdTip[i].idCmd == psmd->uId) { if (LoadString(g_hinst, s_mpcmdTip[i].idInfoTip, pszTip, cch)) hres = S_OK; break; } } return hres; } typedef struct _SEARCHEXTDATA { WCHAR wszMenuText[MAX_PATH]; WCHAR wszHelpText[MAX_PATH]; int iIcon; } SEARCHEXTDATA, FAR* LPSEARCHEXTDATA; HRESULT CStartMenuCallback::_GetHmenuInfo(SMDATA* psmd, SMINFO* psminfo) { const static struct { UINT idCmd; int iImage; } s_mpcmdimg[] = { // Top level menu { IDM_PROGRAMS, II_STPROGS }, { IDM_FAVORITES, II_STFAVORITES }, { IDM_RECENT, II_STDOCS }, { IDM_SETTINGS, II_STSETNGS }, { IDM_MENU_FIND, II_STFIND }, { IDM_HELPSEARCH, II_STHELP }, { IDM_FILERUN, II_STRUN }, { IDM_LOGOFF, II_STLOGOFF }, { IDM_EJECTPC, II_STEJECT }, { IDM_EXITWIN, II_STSHUTD }, #ifdef WINNT // hydra specfic menu items { IDM_MU_SECURITY, II_MU_STSECURITY }, { IDM_MU_DISCONNECT, II_MU_STDISCONN }, #endif // Settings Submenu { IDM_SETTINGSASSIST, II_STSETNGS }, { IDM_CONTROLS, II_STCPANEL }, { IDM_PRINTERS, II_STPRNTRS }, { IDM_TRAYPROPERTIES, II_STTASKBR }, { IDM_MYDOCUMENTS, II_STDOCS}, { IDM_CSC, -IDI_CSC}, { IDM_NETCONNECT, -IDI_NETCONNECT}, }; ASSERT(IS_VALID_WRITE_PTR(psminfo, SMINFO)); int iIcon = -1; DWORD dwFlags = psminfo->dwFlags; MENUITEMINFO mii = {0}; HRESULT hres = S_FALSE; if (psminfo->dwMask & SMIM_ICON) { if (IsInRange(psmd->uId, TRAY_IDM_FINDFIRST, TRAY_IDM_FINDLAST)) { // The find menu extensions pack their icon into their data member of // Menuiteminfo.... mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_DATA; if (GetMenuItemInfo(psmd->hmenu, psmd->uId, MF_BYCOMMAND, &mii)) { #ifdef SHDOC401_DLL if (g_uiShell32 >= 5) // g_uiShell32 not defined!! see browseui\sccls.c #endif { // on nt5 we make find menu owner draw so as a result // we pack icons differently LPSEARCHEXTDATA psed = (LPSEARCHEXTDATA)mii.dwItemData; if (psed) psminfo->iIcon = psed->iIcon; else psminfo->iIcon = -1; } #ifdef SHDOC401_DLL else { // however if we are not running new shell32 // the icon is in the good old place... psminfo->iIcon = mii.dwItemData; } #endif dwFlags |= SMIF_ICON; hres = S_OK; } } else { UINT uIdLocal = psmd->uId; if (uIdLocal == IDM_OPEN_FOLDER) uIdLocal = psmd->uIdAncestor; for (int i = 0; i < ARRAYSIZE(s_mpcmdimg); i++) { if (s_mpcmdimg[i].idCmd == uIdLocal) { iIcon = s_mpcmdimg[i].iImage; break; } } if (iIcon != -1) { dwFlags |= SMIF_ICON; psminfo->iIcon = Shell_GetCachedImageIndex(TEXT("shell32.dll"), iIcon, 0); hres = S_OK; } } } if(psminfo->dwMask & SMIM_FLAGS) { psminfo->dwFlags = dwFlags; if ( (psmd->uId == IDM_CONTROLS && _fCascadeControlPanel ) || (psmd->uId == IDM_PRINTERS && _fCascadePrinters ) || (psmd->uId == IDM_MYDOCUMENTS && _fCascadeMyDocuments ) || (psmd->uId == IDM_NETCONNECT && _fCascadeNetConnections ) ) { psminfo->dwFlags |= SMIF_SUBMENU; hres = S_OK; } else switch (psmd->uId) { case IDM_FAVORITES: case IDM_PROGRAMS: psminfo->dwFlags |= SMIF_DROPCASCADE; hres = S_OK; break; } } return hres; } DWORD CStartMenuCallback::_GetDemote(SMDATA* psmd) { UEMINFO uei; DWORD dwFlags; dwFlags = 0; uei.cbSize = SIZEOF(uei); uei.dwMask = UEIM_HIT; if (SUCCEEDED(UEMQueryEvent(psmd->uIdAncestor == IDM_PROGRAMS? &UEMIID_SHELL : &UEMIID_BROWSER, UEME_RUNPIDL, (WPARAM)psmd->psf, (LPARAM)psmd->pidlItem, &uei))) { if (uei.cHit == 0) { dwFlags |= SMIF_DEMOTED; } } return dwFlags; } int CStartMenuCallback::_GetDarwinIndex(LPCITEMIDLIST pidlFull, CDarwinAd** ppda) { int iRet = -1; if (g_hdpaDarwinAds && g_fCritSectionInit) { ENTERCRITICAL_DARWINADS; int chdpa = DPA_GetPtrCount(g_hdpaDarwinAds); for (int ihdpa = 0; ihdpa < chdpa; ihdpa++) { *ppda = (CDarwinAd*)DPA_FastGetPtr(g_hdpaDarwinAds, ihdpa); if (*ppda) { if (ILIsEqual((*ppda)->_pidl, pidlFull)) { iRet = ihdpa; break; } } } LEAVECRITICAL_DARWINADS; } return iRet; } BOOL CStartMenuCallback::_IsDarwinAdvertisement(LPCITEMIDLIST pidlFull) { // What this is doing is comparing the passed in pidl with the // list of pidls in g_hdpaDarwinAds. That hdpa contains a list of // pidls that are darwin ads. // If the background thread is not done, then we must assume that // it has not processed the shortcut that we are on. That is why we process it // in line. // NOTE: There can be two items in the hdpa. This is ok. BOOL fAd = FALSE; CDarwinAd* pda; int iIndex = _GetDarwinIndex(pidlFull, &pda); // Are there any ads? if (iIndex != -1) { //This is a Darwin pidl. Is it installed? fAd = pda->IsAd(); } return fAd; } // BUGBUG (lamadio): There is a duplicate of this helper in browseui\browmenu.cpp // When modifying this, rev that one as well. void UEMRenamePidl(const GUID *pguidGrp1, IShellFolder* psf1, LPCITEMIDLIST pidl1, const GUID *pguidGrp2, IShellFolder* psf2, LPCITEMIDLIST pidl2) { UEMINFO uei; uei.cbSize = SIZEOF(uei); uei.dwMask = UEIM_HIT; if (SUCCEEDED(UEMQueryEvent(pguidGrp1, UEME_RUNPIDL, (WPARAM)psf1, (LPARAM)pidl1, &uei)) && uei.cHit > 0) { UEMSetEvent(pguidGrp2, UEME_RUNPIDL, (WPARAM)psf2, (LPARAM)pidl2, &uei); uei.cHit = 0; UEMSetEvent(pguidGrp1, UEME_RUNPIDL, (WPARAM)psf1, (LPARAM)pidl1, &uei); } } // BUGBUG (lamadio): There is a duplicate of this helper in browseui\browmenu.cpp // When modifying this, rev that one as well. void UEMDeletePidl(const GUID *pguidGrp, IShellFolder* psf, LPCITEMIDLIST pidl) { UEMINFO uei; uei.cbSize = SIZEOF(uei); uei.dwMask = UEIM_HIT; uei.cHit = 0; UEMSetEvent(pguidGrp, UEME_RUNPIDL, (WPARAM)psf, (LPARAM)pidl, &uei); } HRESULT CStartMenuCallback::_ProcessChangeNotify(SMDATA* psmd, LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { switch (lEvent) { case SHCNE_ASSOCCHANGED: _ReValidateDarwinCache(); return S_OK; case SHCNE_RENAMEFOLDER: // BUGBUG (lamadio): We should move the MenuOrder stream as well. 5.5.99 case SHCNE_RENAMEITEM: { LPITEMIDLIST pidlPrograms; LPITEMIDLIST pidlProgramsCommon; LPITEMIDLIST pidlFavorites; SHGetFolderLocation(NULL, CSIDL_PROGRAMS, NULL, 0, &pidlPrograms); SHGetFolderLocation(NULL, CSIDL_COMMON_PROGRAMS, NULL, 0, &pidlProgramsCommon); SHGetFolderLocation(NULL, CSIDL_FAVORITES, NULL, 0, &pidlFavorites); BOOL fPidl1InStartMenu = ILIsParent(pidlPrograms, pidl1, FALSE) || ILIsParent(pidlProgramsCommon, pidl1, FALSE); BOOL fPidl1InFavorites = ILIsParent(pidlFavorites, pidl1, FALSE); // If we're renaming something from the Start Menu if ( fPidl1InStartMenu ||fPidl1InFavorites) { IShellFolder* psfFrom; LPCITEMIDLIST pidlFrom; if (SUCCEEDED(SHBindToParent(pidl1, IID_IShellFolder, (void**)&psfFrom, &pidlFrom))) { // Into the Start Menu BOOL fPidl2InStartMenu = ILIsParent(pidlPrograms, pidl2, FALSE) || ILIsParent(pidlProgramsCommon, pidl2, FALSE); BOOL fPidl2InFavorites = ILIsParent(pidlFavorites, pidl2, FALSE); if (fPidl2InStartMenu || fPidl2InFavorites) { IShellFolder* psfTo; LPCITEMIDLIST pidlTo; if (SUCCEEDED(SHBindToParent(pidl2, IID_IShellFolder, (void**)&psfTo, &pidlTo))) { // Then we need to rename it UEMRenamePidl(fPidl1InStartMenu ? &UEMIID_SHELL: &UEMIID_BROWSER, psfFrom, pidlFrom, fPidl2InStartMenu ? &UEMIID_SHELL: &UEMIID_BROWSER, psfTo, pidlTo); psfTo->Release(); } } else { // Otherwise, we delete it. UEMDeletePidl(fPidl1InStartMenu ? &UEMIID_SHELL : &UEMIID_BROWSER, psfFrom, pidlFrom); } psfFrom->Release(); } } ILFree(pidlPrograms); ILFree(pidlProgramsCommon); ILFree(pidlFavorites); } break; case SHCNE_DELETE: // BUGBUG (lamadio): We should nuke the MenuOrder stream as well. 5.5.99 case SHCNE_RMDIR: { IShellFolder* psf; LPCITEMIDLIST pidl; if (SUCCEEDED(SHBindToParent(pidl1, IID_IShellFolder, (void**)&psf, &pidl))) { // NOTE favorites is the only that will be initialized UEMDeletePidl(psmd->uIdAncestor == IDM_FAVORITES? &UEMIID_BROWSER : &UEMIID_SHELL, psf, pidl); psf->Release(); } } break; case SHCNE_CREATE: case SHCNE_MKDIR: { IShellFolder* psf; LPCITEMIDLIST pidl; if (SUCCEEDED(SHBindToParent(pidl1, IID_IShellFolder, (void**)&psf, &pidl))) { UEMINFO uei; uei.cbSize = SIZEOF(uei); uei.dwMask = UEIM_HIT; uei.cHit = UEM_NEWITEMCOUNT; UEMSetEvent(psmd->uIdAncestor == IDM_FAVORITES? &UEMIID_BROWSER: &UEMIID_SHELL, UEME_RUNPIDL, (WPARAM)psf, (LPARAM)pidl, &uei); psf->Release(); } } break; } return S_FALSE; } HRESULT CStartMenuCallback::_GetSFInfo(SMDATA* psmd, SMINFO* psminfo) { if (psminfo->dwMask & SMIM_FLAGS && (psmd->uIdAncestor == IDM_PROGRAMS || psmd->uIdAncestor == IDM_FAVORITES)) { if (_fExpandoMenus) { psminfo->dwFlags |= _GetDemote(psmd); } // This is a little backwards. If the Restriction is On, Then we allow the feature. if (SHRestricted(REST_GREYMSIADS) && psmd->uIdAncestor == IDM_PROGRAMS) { LPITEMIDLIST pidlFull = FullPidlFromSMData(psmd); if (pidlFull) { if (_IsDarwinAdvertisement(pidlFull)) { psminfo->dwFlags |= SMIF_ALTSTATE; } ILFree(pidlFull); } } } return S_OK; } void CStartMenuCallback::_ReValidateDarwinCache() { if (g_hdpaDarwinAds && g_fCritSectionInit) { ENTERCRITICAL_DARWINADS; int chdpa = DPA_GetPtrCount(g_hdpaDarwinAds); for (int ihdpa = 0; ihdpa < chdpa; ihdpa++) { CDarwinAd* pda = (CDarwinAd*)DPA_FastGetPtr(g_hdpaDarwinAds, ihdpa); if (pda) { pda->CheckInstalled(); } } LEAVECRITICAL_DARWINADS; } } // Determines if a CSIDL is a child of another CSIDL // e.g. // CSIDL_STARTMENU = c:\foo\bar\Start Menu // CSIDL_PROGRAMS = c:\foo\bar\Start Menu\Programs // Return true BOOL IsCSIDLChild(int csidlParent, int csidlChild) { BOOL fChild = FALSE; TCHAR sz1[MAX_PATH]; TCHAR sz2[MAX_PATH]; TCHAR szCommonRoot[MAX_PATH]; if (SUCCEEDED(SHGetSpecialFolderPath(NULL, sz1, csidlParent, FALSE))) { if (SUCCEEDED(SHGetSpecialFolderPath(NULL, sz2, csidlChild, FALSE))) { if (PathCommonPrefix(sz1, sz2, szCommonRoot) == lstrlen(sz1)) { fChild = TRUE; } } } return fChild; } // Creates the "Start Menu\\Programs" section of the start menu by // generating a Merged Shell folder, setting the locations into that item // then sets it into the passed IShellMenu. HRESULT CStartMenuCallback::InitializeProgramsShellMenu(IShellMenu* psm) { HRESULT hres; DWORD dwDisp; HKEY hkeyPrograms = NULL; LPITEMIDLIST pidl = NULL; DWORD dwInitFlags = SMINIT_VERTICAL; if (!_fScrollPrograms) dwInitFlags |= SMINIT_MULTICOLUMN; if (SHRestricted(REST_NOCHANGESTARMENU)) dwInitFlags |= SMINIT_RESTRICT_DRAGDROP | SMINIT_RESTRICT_CONTEXTMENU; EVAL(SUCCEEDED(psm->Initialize(this, IDM_PROGRAMS, IDM_PROGRAMS, dwInitFlags))); _InitializePrograms(); RegCreateKeyEx(HKEY_CURRENT_USER, STRREG_STARTMENU TEXT("\\Programs"), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hkeyPrograms, &dwDisp); #ifdef WINNT IAugmentedShellFolder2* psf = NULL; hres = CoCreateInstance(CLSID_AugmentedShellFolder2, NULL, CLSCTX_INPROC, IID_IAugmentedShellFolder2, (void **)&psf); if (SUCCEEDED(hres)) { IShellFolder* psfFolder; hres = SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAMS, &pidl); if (pidl && SUCCEEDED(SHBindToObject(NULL, IID_IShellFolder, pidl, (void **)&psfFolder))) { SetFilesystemInfo(psfFolder, pidl, CSIDL_PROGRAMS); hres = psf->AddNameSpace(NULL, psfFolder, pidl, ASFF_DEFAULT | ASFF_DEFNAMESPACE_ALL); if (!SHRestricted(REST_NOCOMMONGROUPS)) { LPITEMIDLIST pidl2 = NULL; IShellFolder* psfCommon; SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_PROGRAMS, &pidl2); if (pidl2 && SUCCEEDED(SHBindToObject(NULL, IID_IShellFolder, pidl2, (void **)&psfCommon))) { SetFilesystemInfo(psfCommon, pidl2, CSIDL_COMMON_PROGRAMS); psf->AddNameSpace(NULL, psfCommon, pidl2, ASFF_DEFAULT); psfCommon->Release(); } ILFree(pidl2); } psfFolder->Release(); } if (SUCCEEDED(hres)) { #else // Windows 9x does not have a Common group. IShellFolder* psf = NULL; SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAMS, &pidl); if (pidl && SUCCEEDED(SHBindToObject(NULL, IID_IShellFolder, pidl, (void **)&psf))) { SetFilesystemInfo(psf, pidl, CSIDL_PROGRAMS); { #endif // We should have a pidl from CSIDL_Programs ASSERT(pidl); // We should have a shell folder from the bind. ASSERT(psf); // We used to register for change notify at CSIDL_STARTMENU and assumed // that CSIDL_PROGRAMS was a child of CSIDL_STARTMENU. Since this wasn't always the // case, I removed the optimization. // Both panes are registered recursive. So, When CSIDL_PROGRAMS _IS_ a // child of CSIDL_STARTMENU we can enter a code path where when destroying // CSIDL_PROGRAMS, we unregister it. This will flush the change nofiy queue // of CSIDL_STARTMENU, and blow away all of the children, including CSIDL_PROGRAMS, // while we are in the middle of destroying it... See the problem? I have been adding // reentrance "Blockers" but this only delayed where we crashed. // What was needed was to determine if Programs was a child of the Start Menu directory. // if it was we need to add the optmimization. If it's not we don't have a problem. // BUGBUG (lamadio): If one of the two is redirected, then this will get optimized // we can't do better than this because both are registed recursive, and this will fault... BOOL fOptimize = IsCSIDLChild(CSIDL_STARTMENU, CSIDL_PROGRAMS) #ifdef WINNT || IsCSIDLChild(CSIDL_COMMON_STARTMENU, CSIDL_COMMON_PROGRAMS) #endif ; hres = psm->SetShellFolder(psf, pidl, hkeyPrograms, SMSET_TOP | (fOptimize?SMSET_DONTREGISTERCHANGENOTIFY:0)); } } if (psf) psf->Release(); ILFree(pidl); if (FAILED(hres)) RegCloseKey(hkeyPrograms); return hres; } // Creates the "Start Menu\\" section of the start menu by // looking up the csidl, generating the Hkey from HKCU\pszRoot\pszValue, // Initializing the IShellMenu with dwPassInitFlags, then setting the locations // into the passed IShellMenu passing the flags dwSetFlags. HRESULT CStartMenuCallback::InitializeCSIDLShellMenu(int uId, int csidl, LPTSTR pszRoot, LPTSTR pszValue, DWORD dwPassInitFlags, DWORD dwSetFlags, BOOL fAddOpen, IShellMenu* psm) { DWORD dwDisp; HKEY hKey = NULL; LPITEMIDLIST pidl; HRESULT hres = E_FAIL; TCHAR szPath[MAX_PATH]; DWORD dwInitFlags = SMINIT_VERTICAL | dwPassInitFlags; if (SHRestricted(REST_NOCHANGESTARMENU)) dwInitFlags |= SMINIT_RESTRICT_DRAGDROP | SMINIT_RESTRICT_CONTEXTMENU; EVAL(SUCCEEDED(psm->Initialize(this, uId, uId, dwInitFlags))); SHGetSpecialFolderLocation(NULL, csidl, &pidl); // Do we have a pidl? if (pidl) { IShellFolder* psfFolder; // Yes; this menu must want to enumerate a shellfolder if (SUCCEEDED(SHBindToObject(NULL, IID_IShellFolder, pidl, (void **)&psfFolder))) { SetFilesystemInfo(psfFolder, pidl, csidl); if (pszRoot) { StrCpyN(szPath, pszRoot, ARRAYSIZE(szPath)); if (pszValue) { PathAppend(szPath, pszValue); } RegCreateKeyEx(HKEY_CURRENT_USER, szPath, NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hKey, &dwDisp); } // Point the menu to the shellfolder hres = psm->SetShellFolder(psfFolder, pidl, hKey, dwSetFlags); psfFolder->Release(); //IMenuPopup hangs onto this, but they do an addref if (SUCCEEDED(hres)) { if (fAddOpen && _fAddOpenFolder) { HMENU hMenu = SHLoadMenuPopup(HINST_THISDLL, MENU_STARTMENU_OPENFOLDER); if (EVAL(hMenu)) { psm->SetMenu(hMenu, _hwnd, SMSET_BOTTOM); } } } else RegCloseKey(hKey); } ILFree(pidl); // Free the pidl returned from SHGetSpecialFolderLocation } return hres; } // This generates the Recent | My Documents sub menu. HRESULT CStartMenuCallback::InitializeDocumentsShellMenu(IShellMenu* psm) { HRESULT hres = E_FAIL; hres = InitializeCSIDLShellMenu(IDM_RECENT, CSIDL_RECENT, NULL, NULL, SMINIT_RESTRICT_DRAGDROP, SMSET_BOTTOM, FALSE, psm); // Allowed to show the my documents item? if (!SHRestricted(REST_NOSMMYDOCS)) { LPITEMIDLIST pidlMyDocs; SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidlMyDocs); if (pidlMyDocs != NULL) { HMENU hMenu = SHLoadMenuPopup(HINST_THISDLL, MENU_STARTMENU_MYDOCS); if (EVAL(hMenu)) { psm->SetMenu(hMenu, _hwnd, SMSET_TOP); } ILFree(pidlMyDocs); } } return hres; } HRESULT CStartMenuCallback::InitializeFastItemsShellMenu(IShellMenu* psm) { HRESULT hres = E_FAIL; DWORD dwDisp; DWORD dwFlags = SMINIT_TOPLEVEL | SMINIT_VERTICAL; if (SHRestricted(REST_NOCHANGESTARMENU)) dwFlags |= SMINIT_RESTRICT_DRAGDROP | SMINIT_RESTRICT_CONTEXTMENU; psm->Initialize(this, 0, ANCESTORDEFAULT, dwFlags); _InitializePrograms(); // Add the fast item folder to the top of the menu IShellFolder* psfFast; LPITEMIDLIST pidlFast; if (SUCCEEDED(GetFastItemFolder(&psfFast, &pidlFast))) { HKEY hMenuKey = NULL; // WARNING: pmb2->Initialize() will always owns hMenuKey, so don't close it RegCreateKeyEx(HKEY_CURRENT_USER, STRREG_STARTMENU, NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hMenuKey, &dwDisp); TraceMsg(TF_MENUBAND, "Root Start Menu Key Is %d", hMenuKey); hres = psm->SetShellFolder(psfFast, pidlFast, hMenuKey, SMSET_TOP | SMSET_NOEMPTY); psfFast->Release(); ILFree(pidlFast); } return hres; } HRESULT CStartMenuCallback::_InitializeFindMenu(IShellMenu* psm) { HRESULT hres = E_FAIL; psm->Initialize(this, IDM_MENU_FIND, IDM_MENU_FIND, SMINIT_VERTICAL); HMENU hmenu = CreatePopupMenu(); if(hmenu) { ATOMICRELEASE(_pcmFind); if (_ptp) { if (SUCCEEDED(_ptp->GetFindCM(hmenu, TRAY_IDM_FINDFIRST, TRAY_IDM_FINDLAST, &_pcmFind))) { IContextMenu2 *pcm2; _pcmFind->QueryInterface(IID_IContextMenu2, (void **)&pcm2); if (pcm2) { pcm2->HandleMenuMsg(WM_INITMENUPOPUP, (WPARAM)hmenu, (LPARAM)0); pcm2->Release(); } } if (_pcmFind) { hres = psm->SetMenu(hmenu, NULL, SMSET_TOP); // Don't Release _pcmFind } } // Since we failed to create the ShellMenu // we need to dispose of this HMENU if (FAILED(hres)) DestroyMenu(hmenu); } return hres; } HRESULT CStartMenuCallback::InitializeNetworkConnections(IShellMenu* psm) { IShellFolder* psfFolder; HRESULT hres = CoCreateInstance (CLSID_ConnectionFolder, NULL, CLSCTX_INPROC, IID_IShellFolder, (LPVOID *)&psfFolder); psm->Initialize(this, IDM_NETCONNECT, IDM_NETCONNECT, SMINIT_VERTICAL); if (SUCCEEDED(hres)) { LPITEMIDLIST pidl; hres = GetNetConnectPidl(&pidl); if (SUCCEEDED(hres)) { hres = psm->SetShellFolder(psfFolder, pidl, NULL, SMSET_TOP); if (_fAddOpenFolder) { HMENU hMenu = SHLoadMenuPopup(HINST_THISDLL, MENU_STARTMENU_OPENFOLDER); if (EVAL(hMenu)) { psm->SetMenu(hMenu, _hwnd, SMSET_BOTTOM); } } ILFree(pidl); } psfFolder->Release(); } return hres; } HRESULT CStartMenuCallback::InitializeSubShellMenu(int idCmd, IShellMenu* psm) { HRESULT hres = E_FAIL; switch(idCmd) { case IDM_PROGRAMS: hres = InitializeProgramsShellMenu(psm); break; case IDM_RECENT: hres = InitializeDocumentsShellMenu(psm); break; case IDM_MENU_FIND: hres = _InitializeFindMenu(psm); break; case IDM_FAVORITES: hres = InitializeCSIDLShellMenu(IDM_FAVORITES, CSIDL_FAVORITES, STRREG_FAVORITES, NULL, 0, SMSET_HASEXPANDABLEFOLDERS | SMSET_USEBKICONEXTRACTION, FALSE, psm); break; case IDM_CONTROLS: hres = InitializeCSIDLShellMenu(IDM_CONTROLS, CSIDL_CONTROLS, STRREG_STARTMENU, TEXT("ControlPanel"), 0, 0, TRUE, psm); break; case IDM_PRINTERS: hres = InitializeCSIDLShellMenu(IDM_PRINTERS, CSIDL_PRINTERS, STRREG_STARTMENU, TEXT("Printers"), 0, 0, TRUE, psm); break; case IDM_MYDOCUMENTS: hres = InitializeCSIDLShellMenu(IDM_MYDOCUMENTS, CSIDL_PERSONAL, STRREG_STARTMENU, TEXT("MyDocuments"), 0, 0, TRUE, psm); break; case IDM_NETCONNECT: hres = InitializeNetworkConnections(psm); break; } return hres; } HRESULT CStartMenuCallback::_GetObject(LPSMDATA psmd, REFIID riid, void** ppvOut) { HRESULT hres = E_FAIL; UINT uId = psmd->uId; ASSERT(ppvOut); ASSERT(IS_VALID_READ_PTR(psmd, SMDATA)); *ppvOut = NULL; if (IsEqualGUID(riid, IID_IShellMenu)) { IShellMenu* psm = NULL; hres = CoCreateInstance(CLSID_MenuBand, NULL, CLSCTX_INPROC, IID_IShellMenu, (void**)&psm); if (SUCCEEDED(hres)) { hres = InitializeSubShellMenu(uId, psm); if (FAILED(hres)) { psm->Release(); psm = NULL; } } *ppvOut = psm; } else if (IsEqualGUID(riid, IID_IContextMenu)) { // NOTE - we dont allow users to open the recent folder this way - ZekeL - 1-JUN-99 // because this is really an internal folder and not a user folder. switch (uId) { case IDM_PROGRAMS: case IDM_FAVORITES: case IDM_MYDOCUMENTS: case IDM_CONTROLS: case IDM_PRINTERS: case IDM_NETCONNECT: { CStartContextMenu* pcm = new CStartContextMenu(uId); if (pcm) { hres = pcm->QueryInterface(riid, ppvOut); pcm->Release(); } else hres = E_OUTOFMEMORY; } } } return hres; } HRESULT CStartMenuCallback::_FilterFavorites(IShellFolder* psf, LPCITEMIDLIST pidl) { HRESULT hres = S_FALSE; if (psf && pidl) { DWORD dwAttribs = SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_LINK; if (SUCCEEDED(psf->GetAttributesOf(1, &pidl, &dwAttribs))) { // include all non file system objects, folders and links // (non file system things like the Channel folders contents) if (!(dwAttribs & (SFGAO_FOLDER | SFGAO_LINK)) && (dwAttribs & SFGAO_FILESYSTEM)) { hres = S_OK; } } } return hres; } HRESULT CStartMenuCallback::_FilterPidl(UINT uParent, IShellFolder* psf, LPCITEMIDLIST pidl) { HRESULT hres = S_FALSE; STRRET strret; ASSERT(IS_VALID_PIDL(pidl)); ASSERT(IS_VALID_CODE_PTR(psf, IShellFolder)); if (uParent == IDM_PROGRAMS || uParent == IDM_TOPLEVELSTARTMENU) { TCHAR szChild[MAX_PATH]; if (SUCCEEDED(psf->GetDisplayNameOf(pidl, SHGDN_INFOLDER, &strret)) && SUCCEEDED(StrRetToBuf(&strret, pidl, szChild, ARRAYSIZE(szChild)))) { // HACKHACK (lamadio): This code assumes that the Display name // of the Programs and Commons Programs folders are the same. It // also assumes that the "programs" folder in the Start Menu folder // is the same name as the one pointed to by CSIDL_PROGRAMS. // We filter: Programs, Windows Update and Administrative tools. if (uParent == IDM_TOPLEVELSTARTMENU) { if (lstrcmpi(szChild, _szPrograms) == 0 || SHRestricted(REST_NOUPDATEWINDOWS) && _pszWindowsUpdate && lstrcmpi(szChild, _pszWindowsUpdate) == 0) { hres = S_OK; } } else if ( /*uParent == IDM_PROGRAMS && */ !_fShowAdminTools && _pszAdminTools && lstrcmpi(szChild, _pszAdminTools) == 0) { hres = S_OK; } } } return hres; } BOOL LinkGetInnerPidl(IShellFolder *psf, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlOut, DWORD *pdwAttr) { *ppidlOut = NULL; IShellLink *psl; HRESULT hr = psf->GetUIObjectOf(NULL, 1, &pidl, IID_IShellLink, NULL, (void **)&psl); if (SUCCEEDED(hr)) { psl->GetIDList(ppidlOut); if (*ppidlOut) { if (FAILED(SHGetAttributesOf(*ppidlOut, pdwAttr))) { ILFree(*ppidlOut); *ppidlOut = NULL; } } psl->Release(); } return (*ppidlOut != NULL); } // _FilterRecentPidl() // the Recent Documents folder can now (NT5) have more than 15 or // so documents, but we only want to show the 15 most recent that we always have on // the start menu. this means that we need to filter out all folders and // anything more than MAXRECENTDOCS HRESULT CStartMenuCallback::_FilterRecentPidl(IShellFolder* psf, LPCITEMIDLIST pidl) { HRESULT hres = S_OK; ASSERT(IS_VALID_PIDL(pidl)); ASSERT(IS_VALID_CODE_PTR(psf, IShellFolder)); ASSERT(_cRecentDocs != -1); ASSERT(_cRecentDocs <= MAXRECENTDOCS); // if we already reached our limit, dont go over... if (_hmruRecent && (_cRecentDocs < MAXRECENTDOCS)) { // we now must take a looksee for it... int iItem = FindMRUData(_hmruRecent, (void *) pidl, ILGetSize(pidl), NULL); DWORD dwAttr = SFGAO_FOLDER | SFGAO_BROWSABLE; LPITEMIDLIST pidlTrue; // need to find out if the link points to a folder... // because we dont want if ((iItem != -1) && LinkGetInnerPidl(psf, pidl, &pidlTrue, &dwAttr)) { if (!(dwAttr & SFGAO_FOLDER)) { // we have a link to something that isnt a folder hres = S_FALSE; _cRecentDocs++; } ILFree(pidlTrue); } } // return S_OK if you dont want to show this item... return hres; } HRESULT CStartMenuCallback::_HandleAccelerator(TCHAR ch, SMDATA* psmdata) { // Since we renamed the 'Find' menu to 'Search' the PMs wanted to have // an upgrade path for users (So they can continue to use the old accelerator // on the new menu item.) // To enable this, when toolbar detects that there is not an item in the menu // that contains the key that has been pressed, then it sends a TBN_ACCL. // This is intercepted by mnbase, and translated into SMC_ACCEL. if (CharUpper((LPTSTR)ch) == CharUpper((LPTSTR)_szFindMeumonic[0])) { psmdata->uId = IDM_MENU_FIND; return S_OK; } return S_FALSE; } HRESULT CStartMenuCallback::_GetTip(LPTSTR pstrTitle, LPTSTR pstrTip) { LoadString(HINST_THISDLL, IDS_CHEVRONTIPTITLE, pstrTitle, MAX_PATH); LoadString(HINST_THISDLL, IDS_CHEVRONTIP, pstrTip, MAX_PATH); // Why would this fail? if (EVAL(pstrTitle[0] != TEXT('\0') && pstrTip[0] != TEXT('\0'))) return S_OK; return S_FALSE; } STDAPI CStartMenu_CreateInstance(LPUNKNOWN punkOuter, REFIID riid, void **ppvOut) { HRESULT hres = E_FAIL; IMenuPopup* pmp = NULL; *ppvOut = NULL; CStartMenuCallback* psmc = new CStartMenuCallback(); if (psmc) { IShellMenu* psm; hres = CoCreateInstance(CLSID_MenuBand, NULL, CLSCTX_INPROC_SERVER, IID_IShellMenu, (void**)&psm); if (SUCCEEDED(hres)) { hres = CoCreateInstance(CLSID_MenuDeskBar, punkOuter, CLSCTX_INPROC_SERVER, IID_IMenuPopup, (void**)&pmp); if (SUCCEEDED(hres)) { IBandSite* pbs; hres = CoCreateInstance(CLSID_MenuBandSite, NULL, CLSCTX_INPROC_SERVER, IID_IBandSite, (void **)&pbs); if (SUCCEEDED(hres)) { hres = pmp->SetClient(pbs); if (SUCCEEDED(hres)) { IDeskBand* pdb; hres = psm->QueryInterface(IID_IDeskBand, (void**)&pdb); if (SUCCEEDED(hres)) { hres = pbs->AddBand(pdb); pdb->Release(); } } pbs->Release(); } // Don't free pmp. We're using it below. } if (SUCCEEDED(hres)) { // This is so the ref counting happens correctly. psm->Initialize(psmc, 0, 0, SMINIT_VERTICAL | SMINIT_TOPLEVEL); hres = psmc->InitializeFastItemsShellMenu(psm); } psm->Release(); } psmc->Release(); } if (SUCCEEDED(hres)) { hres = pmp->QueryInterface(riid, ppvOut); } else { // We need to do this so that it does a cascading delete IUnknown_SetSite(pmp, NULL); } if (pmp) pmp->Release(); return hres; } /// start menu and desktop hotkey tasks #define START_BANNER_COLOR_NT RGB(0, 0, 0) #define START_BANNER_COLOR_95 RGB(128, 128, 128) // CStartMenuTask object. This task enumerates the start menu for hotkeys // and to cache the icons. #if 0 #define TF_SMTASK TF_CUSTOM1 #else #define TF_SMTASK 0 #endif #undef THISCLASS #undef SUPERCLASS #define SUPERCLASS CRunnableTask class CStartMenuTask : public CRunnableTask, public IStartMenuTask { public: // *** IUnknown Methods *** virtual STDMETHODIMP_(ULONG) AddRef(void) { return SUPERCLASS::AddRef(); }; virtual STDMETHODIMP_(ULONG) Release(void){ return SUPERCLASS::Release(); }; virtual STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj); // *** IShellFolderTask Methods *** virtual STDMETHODIMP InitTaskSFT(IShellFolder *psfParent, LPITEMIDLIST pidlFull, LONG nMaxRecursionLevel, DWORD dwFlags, DWORD dwTaskPriority); // *** IStartMenuTask Methods *** virtual STDMETHODIMP InitTaskSMT(IShellHotKey * pshk, int iThreadPriority); // *** CRunnableTask virtual methods virtual STDMETHODIMP RunInitRT(void); virtual STDMETHODIMP InternalResumeRT(void); protected: friend HRESULT CStartMenuTask_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppvOut); CStartMenuTask(); ~CStartMenuTask(); HRESULT _CreateNewTask(IShellFolder * psf, LPCITEMIDLIST pidl); int _iThreadPriority; DWORD _dwTaskPriority; DWORD _dwFlags; LONG _nMaxRecursionLevel ; IShellFolder* _psf; LPITEMIDLIST _pidlFolder; IShellHotKey * _pshk; IEnumIDList * _pei; IShellLinkDataList* _psldl; }; HRESULT CStartMenuTask_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppvOut) { CStartMenuTask* pTask = new CStartMenuTask(); if (pTask) { HRESULT hres = pTask->QueryInterface(riid, ppvOut); pTask->Release(); return hres; } return E_OUTOFMEMORY; } // constructor CStartMenuTask::CStartMenuTask() : CRunnableTask(RTF_SUPPORTKILLSUSPEND), _nMaxRecursionLevel(-1) { InterlockedIncrement((PLONG)&g_cStartMenuTask); } // destructor CStartMenuTask::~CStartMenuTask() { if (_pidlFolder) ILFree(_pidlFolder); ATOMICRELEASE(_pei); ATOMICRELEASE(_psf); ATOMICRELEASE(_pshk); ATOMICRELEASE(_psldl); InterlockedDecrement((PLONG)&g_cStartMenuTask); } STDMETHODIMP CStartMenuTask::QueryInterface(REFIID riid, void ** ppvObj) { HRESULT hres; static const QITAB qit[] = { QITABENT(CStartMenuTask, IShellFolderTask), QITABENT(CStartMenuTask, IStartMenuTask), { 0 }, }; hres = QISearch(this, qit, riid, ppvObj); if (FAILED(hres)) hres = SUPERCLASS::QueryInterface(riid, ppvObj); return hres; } /* Purpose: IShellFolderTask::InitTaskSFT method */ STDMETHODIMP CStartMenuTask::InitTaskSFT(IShellFolder *psfParent, LPITEMIDLIST pidlFull, LONG nMaxRecursionLevel, DWORD dwFlags, DWORD dwTaskPriority) { HRESULT hr = E_OUTOFMEMORY; if (!psfParent) return E_INVALIDARG; ASSERT(IS_VALID_PIDL(pidlFull)); _pidlFolder = ILClone(pidlFull); if (_pidlFolder) { // we have a pidlChild, so bind to him IShellFolder* psf; hr = psfParent->BindToObject(ILFindLastID(_pidlFolder), NULL, IID_IShellFolder, (void **)&psf); if (SUCCEEDED(hr)) { _psf = psf; _dwTaskPriority = dwTaskPriority; _dwFlags = dwFlags; _nMaxRecursionLevel = nMaxRecursionLevel ; #ifdef DEBUG TCHAR szPath[MAX_PATH]; if (pidlFull) SHGetPathFromIDList(pidlFull, szPath); else szPath[0] = TEXT('0'); TraceMsg(TF_SMTASK, "StartMenuTask: task %#lx (pri %d) is '%s'", _dwTaskID, dwTaskPriority, szPath); #endif } } return hr; } /* Purpose: IStartMenuTask::InitTaskSMT method */ STDMETHODIMP CStartMenuTask::InitTaskSMT(IShellHotKey * pshk, int iThreadPriority) { ASSERT(pshk); _pshk = pshk; if (_pshk) _pshk->AddRef(); _iThreadPriority = iThreadPriority; return S_OK; } HRESULT CStartMenuTask::_CreateNewTask(IShellFolder * psf, LPCITEMIDLIST pidl) { HRESULT hres; LPSHELLTASKSCHEDULER pScheduler; // Get the shared task scheduler // BUGBUG : this needs changing so the pointer to the scheduler is passed around, not // BUGBUG : a CoCreate each time.... hres = CoCreateInstance( CLSID_SharedTaskScheduler, NULL, CLSCTX_INPROC, IID_IShellTaskScheduler, (void **) & pScheduler ); if (SUCCEEDED(hres)) { // create a new task IStartMenuTask * psmt; if (SUCCEEDED(CoCreateInstance(CLSID_StartMenuTask, NULL, CLSCTX_INPROC, IID_IStartMenuTask, (void **)&psmt))) { // Initialize the task LPITEMIDLIST pidlFull = ILCombine(_pidlFolder, pidl); if (pidlFull) { if (SUCCEEDED(psmt->InitTaskSFT(_psf, pidlFull, _nMaxRecursionLevel - 1, _dwTaskPriority - 1, THREAD_PRIORITY_NORMAL))) { IRunnableTask * ptask; psmt->InitTaskSMT(_pshk, _iThreadPriority); if (SUCCEEDED(psmt->QueryInterface(IID_IRunnableTask, (void **)&ptask))) { // Add it to the scheduler, with the priority lower than // the parent folders. hres = pScheduler->AddTask(ptask, CLSID_StartMenuTask, 0, _dwTaskPriority - 1); ptask->Release(); } } ILFree(pidlFull); } psmt->Release(); } // release the explorer task scheduler pScheduler->Release(); } return hres; } /* Purpose: CRunnableTask::RunInitRT method */ HRESULT CStartMenuTask::RunInitRT(void) { // This is the first time that ::Run has been called on this task, // so create our pEnumIDList interface. ASSERT(IS_VALID_CODE_PTR(_psf, IShellFolder)); /* // We will detect failure of this at use time. CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC, IID_IShellLinkDataList, (void**)&_psldl);*/ if (!g_fCritSectionInit) { g_fCritSectionInit = TRUE; InitializeCriticalSection(&g_csDarwinAds); } return _psf->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &_pei); } /* Purpose: CRunnableTask::InternalResumeRT method */ //#define _SM_LOADLINKONCE #ifdef _SM_LOADLINKONCE HRESULT _GetILIndexGivenPXIcon(IExtractIcon *pxicon, UINT uFlags, LPCITEMIDLIST pidl, int *piImage, BOOL fAnsiCrossOver); #endif HRESULT CStartMenuTask::InternalResumeRT(void) { HRESULT hr = NOERROR; LPITEMIDLIST pidl; ULONG cElements; int iOldThreadPriority; HANDLE hThread = GetCurrentThread(); ASSERT(_pei); // Change the thread priority for this task? iOldThreadPriority = GetThreadPriority(hThread); if (iOldThreadPriority != _iThreadPriority) { // Yes SetThreadPriority(hThread, _iThreadPriority); DEBUG_CODE( TraceMsg(TF_SMTASK, "StartMenuTask (%#lx): Set thread priority to %x", _dwTaskID, _iThreadPriority); ) } // Enumerate the entire tree while (NOERROR == _pei->Next(1, &pidl, &cElements)) { #ifdef FULL_DEBUG LPITEMIDLIST pidlFull = ILCombine( _pidlFolder, pidl ) ; TCHAR szPath[MAX_PATH]; SHGetPathFromIDList(pidlFull, szPath); // TraceMsg(TF_SMTASK, "StartMenuTask (%#lx): caching %s", _dwTaskID, PathFindFileName(szPath)); DWORD dwCount = GetTickCount(); #endif // Enumerate folders. Don't enumerate folders that behave like // shortcuts (channels). DWORD dwAttrib = SFGAO_FOLDER | SFGAO_LINK | SFGAO_FILESYSTEM; _psf->GetAttributesOf(1, (LPCITEMIDLIST *)&pidl, &dwAttrib); // Is this a subfolder? if ((dwAttrib & SFGAO_FOLDER) && (dwAttrib & SFGAO_FILESYSTEM) && (_dwFlags & ITSFT_RECURSE)) { // Yes; is this a channel? if (!SHIsExpandableFolder(_psf, pidl)) { // Yes; skip it #ifdef FULL_DEBUG TCHAR szPath[MAX_PATH]; TraceMsg(TF_SMTASK, "StartMenuTask (%#lx): skipping %s", _dwTaskID, PathFindFileName(szPath)); #endif goto MoveOn; } // is this a Folder Shortcut? if so don't recurse into it. if ((dwAttrib & (SFGAO_FOLDER | SFGAO_LINK)) == (SFGAO_FOLDER | SFGAO_LINK)) { goto MoveOn; } // PERF: We don't want to recurse into the common Admin Tools Folder. This is not on by default // and sucks in a bunch of DLLs that we don't need. STRRET str; TCHAR szFolder[MAX_PATH]; if (SUCCEEDED(_psf->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str)) && SUCCEEDED(StrRetToBuf(&str, pidl, szFolder, ARRAYSIZE(szFolder)))) { if (PathIsEqualOrSubFolder(MAKEINTRESOURCE(CSIDL_COMMON_ADMINTOOLS), szFolder)) goto MoveOn; } // create a task to enumerate the folder _CreateNewTask(_psf, pidl); } // StartCAP(); #ifndef _SM_LOADLINKONCE // Register the hotkey if (_pshk) _pshk->RegisterHotKey(_psf, _pidlFolder, pidl); // Cache the icon if( _nMaxRecursionLevel >= 0 ) SHMapPIDLToSystemImageListIndex(_psf, pidl, NULL); #ifdef FULL_DEBUG else { TraceMsg(TF_SMTASK, "StartMenuTask (%#lx): skipping icon cache at recursion level %d for %s", _dwTaskID, _nMaxRecursionLevel, PathFindFileName(szPath)); } #endif // if it is a shortcut try to load it if (dwAttrib & SFGAO_LINK) { LPITEMIDLIST pidlFull = ILCombine(_pidlFolder, pidl); if (pidlFull) { // Cache the Darwin info ProcessDarwinAd(_psldl, pidlFull); ILFree(pidlFull); } } // StopCAP(); #else // StartCAP(); // Is this a shortcut? if (dwAttrib & SFGAO_LINK) { // Yes IShellLink * pLink; UINT rgfInOut = 0; HRESULT hres = _psf->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST*)&pidl, IID_IShellLink, &rgfInOut, (void**)&pLink); if (SUCCEEDED(hres)) { ASSERT(pLink); // Hotkey stuff WORD wHotkey; pLink->GetHotkey(&wHotkey); if (wHotkey) { _pshk->RegisterHotKey(NULL, wHotkey, _pidlFolder, pidl); } // Icon stuff IExtractIcon* pxi; hres = pLink->QueryInterface(IID_IExtractIcon, (void**)&pxi); if (SUCCEEDED(hres)) { ASSERT(pxi); int iUnusedImage; hres = _GetILIndexGivenPXIcon(pxi, 0, pidl, &iUnusedImage, FALSE); pxi->Release(); } // Darwin stuff IShellLinkDataList* psldl; hres = pLink->QueryInterface(IID_IShellLinkDataList, (void**)&psldl); if (SUCCEEDED(hres)) { ASSERT(psldl); LPITEMIDLIST pidlFull = ILCombine(_pidlFolder, pidl); ProcessDarwinAd(psldl, pidlFull); psldl->Release(); } pLink->Release(); /* IShellLinkDataList* psldl; CDarwinAd* pda = NULL; hres = pLink->QueryInterface(IID_IShellLinkDataList, (void**)&psldl); if (SUCCEEDED(hres)) { ASSERT(psldl); EXP_DARWIN_LINK* pexpDarwin = NULL; if (SUCCEEDED(psldl->CopyDataBlock(EXP_DARWIN_ID_SIG, (void**)&pexpDarwin))) { ASSERT(pexpDarwin); LPITEMIDLIST pidlFull = ILCombine(_pidlFolder, pidl); #ifdef UNICODE pda = new CDarwinAd(pidlFull, pexpDarwin->szwDarwinID); #else pda = new CDarwinAd(pidlFull, pexpDarwin->szDarwinID); #endif LocalFree(pexpDarwin); } } if (pda) { // When accessing the global HDPA, get the critical section. ENTERCRITICAL; // Do we have a global cache? if (g_hdpaDarwinAds == NULL) { // No; This is either the first time this is called, or we // failed the last time. g_hdpaDarwinAds = DPA_Create(5); } BOOL fRetVal = FALSE; if (g_hdpaDarwinAds) { // DPA_AppendPtr returns the zero based index it inserted it at. if(DPA_AppendPtr(g_hdpaDarwinAds, (void*)pda) >= 0) { fRetVal = TRUE; } } LEAVECRITICAL; if (!fRetVal) { // if we failed to create a dpa, delete this. delete pda; } }*/ } } else { // No it's not a shortcut // Cache the icon SHMapPIDLToSystemImageListIndex(_psf, pidl, NULL); } // StopCAP(); #endif #ifdef FULL_DEBUG TraceMsg(TF_SMTASK, "StartMenuTask (%#lx): cached '%s' (time: %ld)", _dwTaskID, PathFindFileName(szPath), GetTickCount()-dwCount); #endif MoveOn: ILFree(pidl); if (WAIT_OBJECT_0 == WaitForSingleObject(_hDone, 0)) { // the _hDone is signaled, so we are either being suspended or killed. // TraceMsg(TF_SMTASK, "StartMenuTask (%#lx): ", _dwTaskID, PathFindFileName(szPath), GetTickCount()-dwCount); if (_hDone) ResetEvent(_hDone); hr = E_PENDING; break; } } if (iOldThreadPriority != _iThreadPriority) { // bump the thread priority back to what it was // before we executed this task SetThreadPriority(hThread, iOldThreadPriority); } if (_lState == IRTIR_TASK_PENDING) { // We were told to quit, so do it. hr = E_FAIL; } if (_lState != IRTIR_TASK_SUSPENDED) { // We weren't suspended, so we must be finished SUPERCLASS::InternalResumeRT(); #ifdef FULL_DEBUG TraceMsg(TF_SMTASK, "StartMenuTask (%#lx): Finished. Total time = %ld", _dwTaskID, GetTickCount()-_dwTaskID); #endif } // StopCAP(); return hr; } // CDesktopTask object. This task enumerates the desktop for hotkeys #undef THISCLASS #undef SUPERCLASS #define SUPERCLASS CRunnableTask class CDesktopTask : public CRunnableTask, public IStartMenuTask { public: // *** IUnknown Methods *** virtual STDMETHODIMP_(ULONG) AddRef(void) { return SUPERCLASS::AddRef(); }; virtual STDMETHODIMP_(ULONG) Release(void){ return SUPERCLASS::Release(); }; virtual STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj); // *** IShellFolderTask Methods *** virtual STDMETHODIMP InitTaskSFT(IShellFolder *psfParent, LPITEMIDLIST pidlFull, LONG nMaxRecursionLevel, DWORD dwFlags, DWORD dwTaskPriority); // *** IStartMenuTask Methods *** virtual STDMETHODIMP InitTaskSMT(IShellHotKey * pshk, int iThreadPriority); // *** CRunnableTask virtual methods virtual STDMETHODIMP RunInitRT(void); virtual STDMETHODIMP InternalResumeRT(void); protected: friend HRESULT CDesktopTask_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppvOut); CDesktopTask(); ~CDesktopTask(); DWORD _dwTaskPriority; IShellFolder* _psf; LPITEMIDLIST _pidlFolder; IShellHotKey * _pshk; IEnumIDList * _pei; }; HRESULT CDesktopTask_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppvOut) { CDesktopTask* pTask = new CDesktopTask(); if (pTask) { HRESULT hres = pTask->QueryInterface(riid, ppvOut); pTask->Release(); return hres; } return E_OUTOFMEMORY; } // constructor CDesktopTask::CDesktopTask() : CRunnableTask(RTF_SUPPORTKILLSUSPEND) { } // destructor CDesktopTask::~CDesktopTask() { if (_pidlFolder) ILFree(_pidlFolder); ATOMICRELEASE(_pei); ATOMICRELEASE(_psf); ATOMICRELEASE(_pshk); } STDMETHODIMP CDesktopTask::QueryInterface(REFIID riid, void ** ppvObj) { HRESULT hres; static const QITAB qit[] = { QITABENT(CDesktopTask, IShellFolderTask), QITABENT(CDesktopTask, IStartMenuTask), { 0 }, }; hres = QISearch(this, qit, riid, ppvObj); if (FAILED(hres)) hres = SUPERCLASS::QueryInterface(riid, ppvObj); return hres; } /* Purpose: IShellFolderTask::InitTaskSFT method */ STDMETHODIMP CDesktopTask::InitTaskSFT(IShellFolder *psfParent, LPITEMIDLIST pidlFull, LONG nMaxRecursionLevel, DWORD dwFlags, DWORD dwTaskPriority) { HRESULT hr = E_OUTOFMEMORY; IShellFolder* psf; if (!psfParent) return E_INVALIDARG; if (NULL == pidlFull) { psf = psfParent; psf->AddRef(); hr = S_OK; } else { ASSERT(IS_VALID_PIDL(pidlFull)); _pidlFolder = ILClone(pidlFull); if (_pidlFolder) { // we have a pidlChild, so bind to him hr = psfParent->BindToObject(ILFindLastID(_pidlFolder), NULL, IID_IShellFolder, (void **)&psf); } } if (SUCCEEDED(hr)) { _psf = psf; _dwTaskPriority = dwTaskPriority; } return hr; } /* Purpose: IStartMenuTask::InitTaskSMT method */ STDMETHODIMP CDesktopTask::InitTaskSMT(IShellHotKey * pshk, int iThreadPriority) { ASSERT(pshk); _pshk = pshk; if (_pshk) _pshk->AddRef(); return S_OK; } /* Purpose: CRunnableTask::RunInitRT method */ HRESULT CDesktopTask::RunInitRT(void) { // This is the first time that ::Run has been called on this task, // so create our pEnumIDList interface. ASSERT(IS_VALID_CODE_PTR(_psf, IShellFolder)); return _psf->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &_pei); } /* Purpose: CRunnableTask::InternalResumeRT method */ HRESULT CDesktopTask::InternalResumeRT(void) { HRESULT hr = NOERROR; LPITEMIDLIST pidl; ULONG cElements; HANDLE hThread = GetCurrentThread(); ASSERT(_pei); // Enumerate the entire tree while (NOERROR == _pei->Next(1, &pidl, &cElements)) { // Register the hotkey if (_pshk) #ifndef _SM_LOADLINKONCE _pshk->RegisterHotKey(_psf, _pidlFolder, pidl); #else _pshk->RegisterHotKey(_psf, 0, _pidlFolder, pidl); #endif ILFree(pidl); if (WAIT_OBJECT_0 == WaitForSingleObject(_hDone, 0)) { // The _hDone is signaled, so we are either being suspended or killed. if (_hDone) ResetEvent(_hDone); hr = E_PENDING; break; } } if (_lState == IRTIR_TASK_PENDING) { // We were told to quit, so do it. hr = E_FAIL; } if (_lState != IRTIR_TASK_SUSPENDED) { // We weren't suspended, so we must be finished SUPERCLASS::InternalResumeRT(); #ifdef FULL_DEBUG TraceMsg(TF_SMTASK, "DesktopTask (%#lx): Finished. Total time = %ld", _dwTaskID, GetTickCount()-_dwTaskID); #endif } return hr; } // IUnknown STDMETHODIMP CStartContextMenu::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CStartContextMenu, IContextMenu), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } STDMETHODIMP_(ULONG) CStartContextMenu::AddRef(void) { return ++_cRef; } STDMETHODIMP_(ULONG) CStartContextMenu::Release(void) { ASSERT(_cRef > 0); _cRef--; if( _cRef > 0) return _cRef; delete this; return 0; } // IContextMenu STDMETHODIMP CStartContextMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { HRESULT hres = E_FAIL; HMENU hmenuStartMenu = SHLoadMenuPopup(HINST_THISDLL, MENU_STARTMENUSTATICITEMS); if (hmenuStartMenu) { TCHAR szCommon[MAX_PATH]; BOOL fAddCommon = (S_OK == SHGetFolderPath(NULL, CSIDL_COMMON_STARTMENU, NULL, 0, szCommon)); if (fAddCommon) fAddCommon = IsUserAnAdmin(); // Since we don't show this on the start button when the user is not an admin, don't show it here... I guess... if (_idCmd != IDM_PROGRAMS || !fAddCommon) { DeleteMenu(hmenuStartMenu, SMCM_OPEN_ALLUSERS, MF_BYCOMMAND); DeleteMenu(hmenuStartMenu, SMCM_EXPLORE_ALLUSERS, MF_BYCOMMAND); } if (Shell_MergeMenus(hmenu, hmenuStartMenu, 0, indexMenu, idCmdLast, uFlags)) { SetMenuDefaultItem(hmenu, 0, MF_BYPOSITION); _SHPrettyMenu(hmenu); hres = ResultFromShort(GetMenuItemCount(hmenuStartMenu)); } DestroyMenu(hmenuStartMenu); } return hres; } STDMETHODIMP CStartContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici) { HRESULT hres = E_FAIL; if (HIWORD64(lpici->lpVerb) == 0) { BOOL fAllUsers = FALSE; BOOL fOpen = TRUE; switch(LOWORD(lpici->lpVerb)) { case SMCM_OPEN_ALLUSERS: fAllUsers = TRUE; case SMCM_OPEN: // fOpen = TRUE; break; case SMCM_EXPLORE_ALLUSERS: fAllUsers = TRUE; case SMCM_EXPLORE: fOpen = FALSE; break; default: return S_FALSE; } hres = ExecStaticStartMenuItem(_idCmd, fAllUsers, fOpen); } // Ahhh Don't handle verbs!!! return hres; } STDMETHODIMP CStartContextMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax) { return E_NOTIMPL; }