#include "cabinet.h" #include "rcids.h" #include #include #include "bandsite.h" #include "shellp.h" #include "shdguid.h" #include #include "startmnu.h" #include "trayp.h" // for WMTRAY_* #include "tray.h" #include "util.h" HMENU GetStaticStartMenu(BOOL fEdit); // *** IUnknown methods *** STDMETHODIMP CStartMenuHost::QueryInterface (REFIID riid, LPVOID * ppvObj) { static const QITAB qit[] = { QITABENTMULTI(CStartMenuHost, IOleWindow, IMenuPopup), QITABENTMULTI(CStartMenuHost, IDeskBarClient, IMenuPopup), QITABENT(CStartMenuHost, IMenuPopup), QITABENT(CStartMenuHost, ITrayPriv), QITABENT(CStartMenuHost, IShellService), QITABENT(CStartMenuHost, IServiceProvider), QITABENT(CStartMenuHost, IOleCommandTarget), QITABENT(CStartMenuHost, IWinEventHandler), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } STDMETHODIMP_(ULONG) CStartMenuHost::AddRef () { return ++_cRef; } STDMETHODIMP_(ULONG) CStartMenuHost::Release() { ASSERT(_cRef > 0); _cRef--; if( _cRef > 0) return _cRef; delete this; return 0; } /*---------------------------------------------------------- Purpose: ITrayPriv::ExecItem method */ STDMETHODIMP CStartMenuHost::ExecItem (IShellFolder* psf, LPCITEMIDLIST pidl) { // ShellExecute will display errors (if any). No need // to show errors here. return SHInvokeDefaultCommand(v_hwndTray, psf, pidl); } /*---------------------------------------------------------- Purpose: ITrayPriv::GetFindCM method */ STDMETHODIMP CStartMenuHost::GetFindCM(HMENU hmenu, UINT idFirst, UINT idLast, IContextMenu** ppcmFind) { *ppcmFind = SHFind_InitMenuPopup(hmenu, v_hwndTray, TRAY_IDM_FINDFIRST, TRAY_IDM_FINDLAST); if(*ppcmFind) return NOERROR; else return E_FAIL; } /*---------------------------------------------------------- Purpose: ITrayPriv::GetStaticStartMenu method */ STDMETHODIMP CStartMenuHost::GetStaticStartMenu(HMENU* phmenu) { *phmenu = ::GetStaticStartMenu(TRUE); if(*phmenu) return NOERROR; else return E_FAIL; } // *** IServiceProvider *** STDMETHODIMP CStartMenuHost::QueryService (REFGUID guidService, REFIID riid, void ** ppvObject) { if(IsEqualGUID(guidService,SID_SMenuPopup)) return QueryInterface(riid,ppvObject); else return E_NOINTERFACE; } // *** IShellService *** STDMETHODIMP CStartMenuHost::SetOwner (struct IUnknown* punkOwner) { return E_NOTIMPL; } // *** IOleWindow methods *** STDMETHODIMP CStartMenuHost::GetWindow(HWND * lphwnd) { *lphwnd = v_hwndTray; return NOERROR; } /*---------------------------------------------------------- Purpose: IMenuPopup::Popup method */ STDMETHODIMP CStartMenuHost::Popup(POINTL *ppt, RECTL *prcExclude, DWORD dwFlags) { return E_NOTIMPL; } /*---------------------------------------------------------- Purpose: IMenuPopup::OnSelect method */ STDMETHODIMP CStartMenuHost::OnSelect(DWORD dwSelectType) { return NOERROR; } /*---------------------------------------------------------- Purpose: IMenuPopup::SetSubMenu method */ STDMETHODIMP CStartMenuHost::SetSubMenu(IMenuPopup* pmp, BOOL fSet) { if (!fSet) { Tray_OnStartMenuDismissed(); } return NOERROR; } // *** IOleCommandTarget *** STDMETHODIMP CStartMenuHost::QueryStatus (const GUID * pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext) { return E_NOTIMPL; } STDMETHODIMP CStartMenuHost::Exec (const GUID * pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut) { if (IsEqualGUID(CGID_MENUDESKBAR,*pguidCmdGroup)) { switch (nCmdID) { case MBCID_GETSIDE: pvarargOut->vt = VT_I4; pvarargOut->lVal = MENUBAR_TOP; break; default: break; } } return NOERROR; } // *** IWinEventHandler *** STDMETHODIMP CStartMenuHost::OnWinEvent(HWND h, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres) { //Forward events to the tray winproc? return E_NOTIMPL; } STDMETHODIMP CStartMenuHost::IsWindowOwner(HWND hwnd) { return E_NOTIMPL; } CStartMenuHost::CStartMenuHost() : _cRef(1) { } HRESULT StartMenuHost_Create(IMenuPopup** ppmp, IMenuBand** ppmb) { HRESULT hres = E_OUTOFMEMORY; IMenuPopup * pmp = NULL; IMenuBand * pmb = NULL; CStartMenuHost *psmh = new CStartMenuHost(); if (psmh) { hres = CoCreateInstance(CLSID_StartMenuBar, NULL, CLSCTX_INPROC_SERVER, IID_IMenuPopup, (LPVOID*)&pmp); if (SUCCEEDED(hres)) { IObjectWithSite* pows; hres = pmp->QueryInterface(IID_IObjectWithSite, (void**)&pows); if(SUCCEEDED(hres)) { IInitializeObject* pio; pows->SetSite(SAFECAST(psmh, ITrayPriv*)); hres = pmp->QueryInterface(IID_IInitializeObject, (void**)&pio); if(SUCCEEDED(hres)) { hres = pio->Initialize(); pio->Release(); } if (SUCCEEDED(hres)) { IUnknown* punk; hres = pmp->GetClient(&punk); if (SUCCEEDED(hres)) { IBandSite* pbs; hres = punk->QueryInterface(IID_IBandSite, (void**)&pbs); if(SUCCEEDED(hres)) { DWORD dwBandID; pbs->EnumBands(0, &dwBandID); hres = pbs->GetBandObject(dwBandID, IID_IMenuBand, (void**)&pmb); pbs->Release(); // Don't release pmb } punk->Release(); } } if (FAILED(hres)) pows->SetSite(NULL); pows->Release(); } // Don't release pmp } psmh->Release(); } if (FAILED(hres)) { ATOMICRELEASE(pmp); ATOMICRELEASE(pmb); } *ppmp = pmp; *ppmb = pmb; return hres; } HRESULT IMenuPopup_SetIconSize(IMenuPopup* pmp,DWORD iIcon) { IBanneredBar* pbb; if (pmp == NULL) return E_FAIL; HRESULT hres = pmp->QueryInterface(IID_IBanneredBar,(void**)&pbb); if (SUCCEEDED(hres)) { pbb->SetIconSize(iIcon); pbb->Release(); } return hres; } void CreateInitialMFU(BOOL fReset); // // "Delayed per-user install". // // StartMenuInit is the value that tells us what version of the shell // this user has seen most recently. // // missing = has never run explorer before, or pre-IE4 // 1 = IE4 or later // 2 = XP or later // void HandleFirstTime() { DWORD dwStartMenuInit = 0; DWORD cb = sizeof(dwStartMenuInit); SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_ADVANCED, TEXT("StartMenuInit"), NULL, &dwStartMenuInit, &cb); if (dwStartMenuInit < 2) { DWORD dwValue; switch (dwStartMenuInit) { case 0: // Upgrade from 0 to latest { // If this is the first boot of the shell for this user, then we need to see if it's an upgrade. // If it is, then we need set the Logoff option. PM Decision to have a different // look for upgraded machines... TCHAR szPath[MAX_PATH]; TCHAR szPathExplorer[MAX_PATH]; DWORD cbSize = ARRAYSIZE(szPath); DWORD dwType; // Is this an upgrade (Does WindowsUpdate\UpdateURL Exist?) PathCombine(szPathExplorer, REGSTR_PATH_EXPLORER, TEXT("WindowsUpdate")); if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, szPathExplorer, TEXT("UpdateURL"), &dwType, szPath, &cbSize) && szPath[0] != TEXT('\0')) { // Yes; Then write the option out to the registry. dwValue = 1; SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_ADVANCED, TEXT("StartMenuLogoff"), REG_DWORD, &dwValue, sizeof(DWORD)); } } // FALL THROUGH case 1: // Upgrade from 1 to latest // User has never seen XP before. // PMs in certain groups insist on getting free advertising // even on upgrades, so we do it. CreateInitialMFU(dwStartMenuInit == 0); // FALL THROUGH default: break; } // If AuditInProgress is set; that means that we are in the // OEM sysprep stage and not running as an end user, in which // case don't set the flag saying "don't do this again" because // we do want to do this again when the retail end user logs // on for the first time. // // (We need to do all this work even in Audit mode so the OEM // gets a warm fuzzy feeling.) if (!SHRegGetBoolUSValue(TEXT("System\\Setup"), TEXT("AuditinProgress"), TRUE, FALSE)) { // Mark this so that we know we've been launched once. dwValue = 2; SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_ADVANCED, TEXT("StartMenuInit"), REG_DWORD, &dwValue, sizeof(DWORD)); } } } BOOL GetLogonUserName(LPTSTR pszUsername, DWORD* pcchUsername) { BOOL fSuccess = FALSE; HKEY hkeyExplorer = NULL; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER, 0, KEY_QUERY_VALUE, &hkeyExplorer)) { DWORD dwType; DWORD dwSize = (*pcchUsername) * sizeof(TCHAR); if (ERROR_SUCCESS == RegQueryValueEx(hkeyExplorer, TEXT("Logon User Name"), 0, &dwType, (LPBYTE) pszUsername, &dwSize)) { if ((REG_SZ == dwType) && (*pszUsername)) { fSuccess = TRUE; } } RegCloseKey(hkeyExplorer); } // Fall back on GetUserName if the Logon User Name isn't set. if (!fSuccess) { fSuccess = GetUserName(pszUsername, pcchUsername); if (fSuccess) { CharUpperBuff(pszUsername, 1); } } return fSuccess; } BOOL IsNetConnectInstalled() { #ifdef WINNT return TRUE; // Always installed #else HKEY hkey = NULL; BOOL fInstalled = FALSE; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows\\") TEXT("CurrentVersion\\Setup\\OptionalComponents\\RNA"), 0, KEY_QUERY_VALUE, &hkey)) { DWORD dwType; TCHAR sz[MAX_PATH]; DWORD dwSize = ARRAYSIZE(sz); if (ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("Installed"), 0, &dwType, (LPBYTE) &sz, &dwSize)) { if (dwType == REG_SZ && sz[0] == TEXT('1')) { fInstalled = TRUE; } } RegCloseKey(hkey); } return fInstalled; #endif } BOOL _ShowStartMenuLogoff() { // We want the Logoff menu on the start menu if: // These MUST both be true // 1) It's not restricted // 2) We have Logged On. // Any of these three. // 3) We've Upgraded from IE4 // 4) The user has specified that it should be present // 5) It's been "Restricted" On. // Behavior also depends on whether we are a remote session or not (dsheldon): // Remote session: Logoff brings up shutdown dialog // Console session: Logoff directly does logoff DWORD dwRest = SHRestricted(REST_STARTMENULOGOFF); SHELLSTATE ss = {0}; SHGetSetSettings(&ss, SSF_STARTPANELON, FALSE); // if the new start menu is on, always show logoff BOOL fUserWantsLogoff = ss.fStartPanelOn || GetExplorerUserSetting(HKEY_CURRENT_USER, TEXT("Advanced"), TEXT("StartMenuLogoff")) > 0; BOOL fAdminWantsLogoff = (BOOL)(dwRest == 2) || SHRestricted(REST_FORCESTARTMENULOGOFF); BOOL fIsFriendlyUIActive = IsOS(OS_FRIENDLYLOGONUI); if ((dwRest != 1 && (GetSystemMetrics(SM_NETWORK) & RNC_LOGON) != 0) && ( fUserWantsLogoff || fAdminWantsLogoff || fIsFriendlyUIActive)) { return TRUE; } else { return FALSE; } } BOOL _ShowStartMenuEject() { if(SHRestricted(REST_NOSMEJECTPC)) //Is there a policy restriction? return FALSE; // CanShowEject Queries the user's permission to eject, // IsEjectAllowed queries the hardware. return SHTestTokenPrivilege(NULL, SE_UNDOCK_NAME) && IsEjectAllowed(FALSE) && !GetSystemMetrics(SM_REMOTESESSION); } BOOL _ShowStartMenuRun() { return !IsRestrictedOrUserSetting(HKEY_CURRENT_USER, REST_NORUN, TEXT("Advanced"), TEXT("StartMenuRun"), ROUS_KEYALLOWS | ROUS_DEFAULTALLOW); } BOOL _ShowStartMenuHelp() { return !IsRestrictedOrUserSetting(HKEY_CURRENT_USER, REST_NOSMHELP, TEXT("Advanced"), TEXT("NoStartMenuHelp"), ROUS_KEYRESTRICTS | ROUS_DEFAULTALLOW); } BOOL _ShowStartMenuShutdown() { return !SHRestricted(REST_NOCLOSE) && !GetSystemMetrics(SM_REMOTESESSION) && (!IsOS(OS_FRIENDLYLOGONUI) || SHTestTokenPrivilege(NULL, SE_SHUTDOWN_NAME)); // if friendly logon is active, then don't show shutdown unless they have privileges, since shutdown "only" shuts you down. // if they're not using friendly logon ui, then shutdown also contains options to log you off/hibernate, so show it... } // If remote and not disabled by administrator then show "Disconnect". BOOL _ShowStartMenuDisconnect() { return GetSystemMetrics(SM_REMOTESESSION) && !SHRestricted(REST_NODISCONNECT); } BOOL _ShowStartMenuSearch() { return !SHRestricted(REST_NOFIND); } HMENU GetStaticStartMenu(BOOL fEdit) { #ifdef WINNT // hydra adds two more items #define CITEMSMISSING 4 #else #define CITEMSMISSING 3 #endif HMENU hStartMenu = LoadMenuPopup(MAKEINTRESOURCE(MENU_START)); // If no editing requested, then we're done, lickity-split if (!fEdit) return hStartMenu; HMENU hmenu; UINT iSep2ItemsMissing = 0; // // Default to the Win95/NT4 version of the Settings menu. // // Restictions if (!_ShowStartMenuRun()) { DeleteMenu(hStartMenu, IDM_FILERUN, MF_BYCOMMAND); } if (!_ShowStartMenuHelp()) { DeleteMenu(hStartMenu, IDM_HELPSEARCH, MF_BYCOMMAND); } if (IsRestrictedOrUserSetting(HKEY_LOCAL_MACHINE, REST_NOCSC, TEXT("Advanced"), TEXT("StartMenuSyncAll"), ROUS_KEYALLOWS | ROUS_DEFAULTRESTRICT)) { DeleteMenu(hStartMenu, IDM_CSC, MF_BYCOMMAND); iSep2ItemsMissing++; } BOOL fIsFriendlyUIActive = IsOS(OS_FRIENDLYLOGONUI); if (_ShowStartMenuLogoff()) { UINT idMenuRenameToLogoff = IDM_LOGOFF; TCHAR szUserName[200]; TCHAR szTemp[256]; TCHAR szMenuText[256]; DWORD dwSize = ARRAYSIZE(szUserName); MENUITEMINFO mii; mii.cbSize = sizeof(MENUITEMINFO); mii.dwTypeData = szTemp; mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_SUBMENU | MIIM_STATE | MIIM_DATA; mii.cch = ARRAYSIZE(szTemp); mii.hSubMenu = NULL; mii.fType = MFT_SEPARATOR; // to avoid ramdom result. mii.dwItemData = 0; GetMenuItemInfo(hStartMenu,idMenuRenameToLogoff,MF_BYCOMMAND,&mii); if (GetLogonUserName(szUserName, &dwSize)) { if (fIsFriendlyUIActive) { dwSize = ARRAYSIZE(szUserName); if (FAILED(SHGetUserDisplayName(szUserName, &dwSize))) { dwSize = ARRAYSIZE(szUserName); GetLogonUserName(szUserName, &dwSize); } } wsprintf (szMenuText,szTemp, szUserName); } else if (!LoadString(hinstCabinet, IDS_LOGOFFNOUSER, szMenuText, ARRAYSIZE(szMenuText))) { // mem error, use the current string. szUserName[0] = 0; wsprintf(szMenuText, szTemp, szUserName); } mii.dwTypeData = szMenuText; mii.cch = ARRAYSIZE(szMenuText); SetMenuItemInfo(hStartMenu,idMenuRenameToLogoff,MF_BYCOMMAND,&mii); } else { DeleteMenu(hStartMenu, IDM_LOGOFF, MF_BYCOMMAND); iSep2ItemsMissing++; } // If restricted, then user cannot shut down at all. // If friendly UI is active change "Shut Down..." to "Turn Off Computer..." if (!_ShowStartMenuShutdown()) { DeleteMenu(hStartMenu, IDM_EXITWIN, MF_BYCOMMAND); iSep2ItemsMissing++; } else if (fIsFriendlyUIActive) { // If the user has the SE_SHUTDOWN_NAME privilege // then rename the menu item. if (SHTestTokenPrivilege(NULL, SE_SHUTDOWN_NAME) && !GetSystemMetrics(SM_REMOTESESSION)) { MENUITEMINFO mii; TCHAR szMenuText[256]; (int)LoadString(hinstCabinet, IDS_TURNOFFCOMPUTER, szMenuText, ARRAYSIZE(szMenuText)); ZeroMemory(&mii, sizeof(mii)); mii.cbSize = sizeof(mii); mii.fMask = MIIM_TYPE; mii.fType = MFT_STRING; mii.dwTypeData = szMenuText; mii.cch = ARRAYSIZE(szMenuText); TBOOL(SetMenuItemInfo(hStartMenu, IDM_EXITWIN, FALSE, &mii)); } // Otherwise delete the menu item. else { DeleteMenu(hStartMenu, IDM_EXITWIN, MF_BYCOMMAND); iSep2ItemsMissing++; } } if (!_ShowStartMenuDisconnect()) { DeleteMenu(hStartMenu, IDM_MU_DISCONNECT, MF_BYCOMMAND); iSep2ItemsMissing++; } if (iSep2ItemsMissing == CITEMSMISSING) { DeleteMenu(hStartMenu, IDM_SEP2, MF_BYCOMMAND); } if (!_ShowStartMenuEject()) { DeleteMenu(hStartMenu, IDM_EJECTPC, MF_BYCOMMAND); } // Setting stuff. hmenu = SHGetMenuFromID(hStartMenu, IDM_SETTINGS); if (hmenu) { int iMissingSettings = 0; #ifdef WINNT // hydra menu items #define CITEMS_SETTINGS 5 // Number of items in settings menu #else #define CITEMS_SETTINGS 4 // Number of items in settings menu #endif if (SHRestricted(REST_NOSETTASKBAR)) { DeleteMenu(hStartMenu, IDM_TRAYPROPERTIES, MF_BYCOMMAND); iMissingSettings++; } if (SHRestricted(REST_NOSETFOLDERS) || SHRestricted(REST_NOCONTROLPANEL)) { DeleteMenu(hStartMenu, IDM_CONTROLS, MF_BYCOMMAND); // For the separator that now on top DeleteMenu(hmenu, 0, MF_BYPOSITION); iMissingSettings++; } if (SHRestricted(REST_NOSETFOLDERS)) { DeleteMenu(hStartMenu, IDM_PRINTERS, MF_BYCOMMAND); iMissingSettings++; } if (SHRestricted(REST_NOSETFOLDERS) || SHRestricted(REST_NONETWORKCONNECTIONS) || !IsNetConnectInstalled()) { DeleteMenu(hStartMenu, IDM_NETCONNECT, MF_BYCOMMAND); iMissingSettings++; } #ifdef WINNT // hydra menu items if (!SHGetMachineInfo(GMI_TSCLIENT) || SHRestricted(REST_NOSECURITY)) { DeleteMenu(hStartMenu, IDM_MU_SECURITY, MF_BYCOMMAND); iMissingSettings++; } #endif // Are all the items missing? if (iMissingSettings == CITEMS_SETTINGS) { // Yes; don't bother showing the menu at all DeleteMenu(hStartMenu, IDM_SETTINGS, MF_BYCOMMAND); } } else { DebugMsg(DM_ERROR, TEXT("c.fm_rui: Settings menu couldn't be found. Restricted items may not have been removed.")); } // Find menu. if (!_ShowStartMenuSearch()) { DeleteMenu(hStartMenu, IDM_MENU_FIND, MF_BYCOMMAND); } // Documents menu. if (SHRestricted(REST_NORECENTDOCSMENU)) { DeleteMenu(hStartMenu, IDM_RECENT, MF_BYCOMMAND); } // Favorites menu. if (IsRestrictedOrUserSetting(HKEY_CURRENT_USER, REST_NOFAVORITESMENU, TEXT("Advanced"), TEXT("StartMenuFavorites"), ROUS_KEYALLOWS | ROUS_DEFAULTRESTRICT)) { DeleteMenu(hStartMenu, IDM_FAVORITES, MF_BYCOMMAND); } return hStartMenu; } // // CHotKey class // // constructor CHotKey::CHotKey() : _cRef(1) { } STDMETHODIMP CHotKey::QueryInterface(REFIID riid, LPVOID * ppvObj) { if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IShellHotKey)) { *ppvObj = SAFECAST(this, IShellHotKey *); } else { *ppvObj = NULL; return E_NOINTERFACE; } AddRef(); return NOERROR; } STDMETHODIMP_(ULONG) CHotKey::AddRef() { return ++_cRef; } STDMETHODIMP_(ULONG) CHotKey::Release() { ASSERT(_cRef > 0); _cRef--; if( _cRef > 0) return _cRef; delete this; return 0; } HRESULT Tray_RegisterHotKey(WORD wHotkey, LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidl) { if (wHotkey) { int i = c_tray.HotkeyAdd(wHotkey, (LPITEMIDLIST)pidlParent, (LPITEMIDLIST)pidl, TRUE); if (i != -1) { // Register in the context of the tray's thread. PostMessage(v_hwndTray, WMTRAY_REGISTERHOTKEY, i, 0); } } return S_OK; } /*---------------------------------------------------------- Purpose: IShellHotKey::RegisterHotKey method */ STDMETHODIMP CHotKey::RegisterHotKey(IShellFolder * psf, LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidl) { WORD wHotkey; HRESULT hr = S_OK; wHotkey = _GetHotkeyFromFolderItem(psf, pidl); if (wHotkey) { hr = ::Tray_RegisterHotKey(wHotkey, pidlParent, pidl); } return hr; } STDAPI CHotKey_Create(IShellHotKey ** ppshk) { HRESULT hres = E_OUTOFMEMORY; CHotKey * photkey = new CHotKey; if (photkey) { hres = S_OK; } *ppshk = SAFECAST(photkey, IShellHotKey *); return hres; }