#include "stdafx.h" #include "sfthost.h" #include "uemapp.h" #include #include "tray.h" #include "rcids.h" #include "mfulist.h" #define STRSAFE_NO_CB_FUNCTIONS #define STRSAFE_NO_DEPRECATE #include //--------------------------------------------------------------------------- // // Create the initial MFU. // // Due to the way sysprep works, we cannot do this work in // per-user install because "reseal" copies the already-installed user // to the default hive, so all new users will bypass per-user install // since ActiveSetup thinks that they have already been installed... // // // We need a parallel list of hard-coded English links so we can get // the correct shortcut name on MUI systems. // #define MAX_MSMFUENTRIES 16 struct MFULIST { UINT idsBase; LPCTSTR rgpszEnglish[MAX_MSMFUENTRIES]; }; #define MAKEMFU(name) \ const MFULIST c_mfu##name = { IDS_MFU_##name##_00, { MFU_ENUMC(name) } }; #ifdef _WIN64 MAKEMFU(PRO64ALL) MAKEMFU(SRV64ADM) #define c_mfuPROALL c_mfuPRO64ALL #define c_mfuSRVADM c_mfuSRV64ADM #else MAKEMFU(PRO32ALL) MAKEMFU(SRV32ADM) #define c_mfuPROALL c_mfuPRO32ALL #define c_mfuSRVADM c_mfuSRV32ADM #endif //--------------------------------------------------------------------------- // // _GetPinnedItemTarget // // Given a pidl, find the executable that will ultimately be launched. // // This tunnels through shortcuts and resolves the magical "Internet" // and "Email" icons to the respective registered programs. // BOOL _GetPinnedItemTarget(LPCITEMIDLIST pidl, LPTSTR *ppszPath) { *ppszPath = NULL; IShellFolder *psf; LPCITEMIDLIST pidlChild; if (SUCCEEDED(SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild))) { IShellLink *psl; IExtractIcon *pxi; if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, &pidlChild, IID_PPV_ARG_NULL(IShellLink, &psl)))) { TCHAR szPath[MAX_PATH]; TCHAR szPathExpanded[MAX_PATH]; if (psl->GetPath(szPath, ARRAYSIZE(szPath), 0, SLGP_RAWPATH) == S_OK && SHExpandEnvironmentStrings(szPath, szPathExpanded, ARRAYSIZE(szPathExpanded))) { SHStrDup(szPathExpanded, ppszPath); } psl->Release(); } else if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, &pidlChild, IID_PPV_ARG_NULL(IExtractIcon, &pxi)))) { // There is no way to get the IAssociationElement directly, so // we get the IExtractIcon and then ask him for the IAssociationElement. IAssociationElement *pae; if (SUCCEEDED(IUnknown_QueryService(pxi, IID_IAssociationElement, IID_PPV_ARG(IAssociationElement, &pae)))) { pae->QueryString(AQVS_APPLICATION_PATH, L"open", ppszPath); pae->Release(); } pxi->Release(); } psf->Release(); } return *ppszPath != NULL; } //--------------------------------------------------------------------------- // // MFUExclusion // // Keep track of apps that should be excluded from the MFU. class MFUExclusion { public: MFUExclusion(); ~MFUExclusion(); BOOL IsExcluded(LPCITEMIDLIST pidl) const; private: // worst-case default pin list size enum {MAX_EXCLUDED = 3 }; PWSTR _rgpszExclude[MAX_EXCLUDED]; int _cExcluded; }; MFUExclusion::MFUExclusion() : _cExcluded(0) { IStartMenuPin *psmpin; HRESULT hr; hr = CoCreateInstance(CLSID_StartMenuPin, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IStartMenuPin, &psmpin)); if (SUCCEEDED(hr)) { IEnumIDList *penum; if (SUCCEEDED(psmpin->EnumObjects(&penum))) { LPITEMIDLIST pidl; while (_cExcluded < ARRAYSIZE(_rgpszExclude) && penum->Next(1, &pidl, NULL) == S_OK) { if (_GetPinnedItemTarget(pidl, &_rgpszExclude[_cExcluded])) { _cExcluded++; } ILFree(pidl); } penum->Release(); } psmpin->Release(); } } MFUExclusion::~MFUExclusion() { for (int i = 0; i < _cExcluded; i++) { SHFree(_rgpszExclude[i]); } } BOOL MFUExclusion::IsExcluded(LPCITEMIDLIST pidl) const { BOOL fRc = FALSE; LPTSTR pszTarget; if (_GetPinnedItemTarget(pidl, &pszTarget)) { for (int i = 0; i < _cExcluded; i++) { if (lstrcmpi(_rgpszExclude[i], pszTarget) == 0) { fRc = TRUE; break; } } SHFree(pszTarget); } return fRc; } extern "C" HKEY g_hkeyExplorer; void ClearUEMData(); //--------------------------------------------------------------------------- // // MFUEnumerator (and derived OEMMFUEnumerator, MSMFUEnumerator) // // Enumerate applications to be added to the default MFU. #define MAX_OEMMFUENTRIES 4 class MFUEnumerator { public: MFUEnumerator() : _dwIndex(0) { } protected: DWORD _dwIndex; }; #define REGSTR_PATH_SMDEN TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\SMDEn") class OEMMFUEnumerator : protected MFUEnumerator { public: OEMMFUEnumerator() : _hk(NULL) { RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_SMDEN, 0, KEY_READ, &_hk); } ~OEMMFUEnumerator() { if (_hk) { RegCloseKey(_hk); } } LPITEMIDLIST Next(const MFUExclusion *pmex); private: HKEY _hk; }; LPITEMIDLIST OEMMFUEnumerator::Next(const MFUExclusion *pmex) { if (!_hk) { return NULL; // No entries at all } restart: if (_dwIndex >= MAX_OEMMFUENTRIES) { return NULL; // No more entries } TCHAR szKey[20]; DWORD dwCurrentIndex = _dwIndex++; wnsprintf(szKey, ARRAYSIZE(szKey), TEXT("OEM%d"), dwCurrentIndex); TCHAR szPath[MAX_PATH]; HRESULT hr = SHLoadRegUIStringW(_hk, szKey, szPath, ARRAYSIZE(szPath)); if (FAILED(hr)) { goto restart; } TCHAR szPathExpanded[MAX_PATH]; SHExpandEnvironmentStrings(szPath, szPathExpanded, ARRAYSIZE(szPathExpanded)); LPITEMIDLIST pidl = ILCreateFromPath(szPathExpanded); if (!pidl) { goto restart; } if (pmex->IsExcluded(pidl)) { // Excluded - skip it ILFree(pidl); goto restart; } return pidl; } class MSMFUEnumerator : protected MFUEnumerator { public: LPITEMIDLIST Next(const MFULIST *pmfu, const MFUExclusion *pmex); }; LPITEMIDLIST MSMFUEnumerator::Next(const MFULIST *pmfu, const MFUExclusion *pmex) { restart: if (_dwIndex >= MAX_MSMFUENTRIES) { return NULL; // No more entries } DWORD dwCurrentIndex = _dwIndex++; // // If this is excluded by policy, then skip it. // if (StrCmpC(pmfu->rgpszEnglish[dwCurrentIndex], TEXT(MFU_SETDEFAULTS)) == 0 && SHRestricted(REST_NOSMCONFIGUREPROGRAMS)) { goto restart; } // // If this entry is blank, then skip it. // TCHAR szPath[MAX_PATH]; if (!LoadString(_Module.GetModuleInstance(), pmfu->idsBase + dwCurrentIndex, szPath, ARRAYSIZE(szPath))) { goto restart; } TCHAR szPathExpanded[MAX_PATH]; SHExpandEnvironmentStrings(szPath, szPathExpanded, ARRAYSIZE(szPathExpanded)); LPITEMIDLIST pidl = ILCreateFromPath(szPathExpanded); if (!pidl) { // Doesn't exist under localized name; try the English name SHExpandEnvironmentStrings(pmfu->rgpszEnglish[dwCurrentIndex], szPathExpanded, ARRAYSIZE(szPathExpanded)); pidl = ILCreateFromPath(szPathExpanded); } if (!pidl) { // Doesn't exist at all - skip it goto restart; } if (pmex->IsExcluded(pidl)) { // Excluded - skip it ILFree(pidl); goto restart; } return pidl; } #ifdef DEBUG void ValidateMFUList(const MFULIST *pmfu) { for (int i = 0; i < MAX_MSMFUENTRIES; i++) { TCHAR szBuf[MAX_PATH]; LoadString(_Module.GetModuleInstance(), pmfu->idsBase + i, szBuf, ARRAYSIZE(szBuf)); ASSERT(StrCmpC(szBuf, pmfu->rgpszEnglish[i]) == 0); } } void ValidateInitialMFUTables() { // If this is the English build, then validate that the resources match // the hard-coded table. if (GetUserDefaultUILanguage() == MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)) { ValidateMFUList(&c_mfuPROALL); ValidateMFUList(&c_mfuSRVADM); } // The PRO list must contain a copy of MFU_SETDEFAULTS for the // policy exclusion code to work. BOOL fFound = FALSE; for (int i = 0; i < MAX_MSMFUENTRIES; i++) { if (StrCmpC(c_mfuPROALL.rgpszEnglish[i], TEXT(MFU_SETDEFAULTS)) == 0) { fFound = TRUE; break; } } ASSERT(fFound); } #endif void CreateInitialMFU(BOOL fReset) { #ifdef DEBUG ValidateInitialMFUTables(); #endif HRESULT hrInit = SHCoInitialize(); // Delete any dregs left over from "sysprep -reseal". // This also prevents OEMs from spamming the pin list. SHDeleteKey(g_hkeyExplorer, TEXT("StartPage")); SHDeleteValue(g_hkeyExplorer, TEXT("Advanced"), TEXT("StartButtonBalloonTip")); // Start with a clean slate if so requested if (fReset) { ClearUEMData(); } // Okay now build the default MFU { // nested scope so MFUExclusion gets destructed before we // SHCoUninitialize() MFUExclusion mex; int iSlot; LPITEMIDLIST rgpidlMFU[REGSTR_VAL_DV2_MINMFU_DEFAULT] = { 0 }; // Assert that the slots are evenly shared between MSFT and the OEM COMPILETIME_ASSERT(ARRAYSIZE(rgpidlMFU) % 2 == 0); // The OEM can provide up to four apps, and we will put as many // as fit into into bottom half. { OEMMFUEnumerator mfuOEM; for (iSlot = ARRAYSIZE(rgpidlMFU)/2; iSlot < ARRAYSIZE(rgpidlMFU); iSlot++) { rgpidlMFU[iSlot] = mfuOEM.Next(&mex); } } // The top half (and any unused slots in the bottom half) // go to MSFT (up to MAX_MSMFUENTRIES MSFT apps); which list // we use depends on the SKU and whether we are an administrator. const MFULIST *pmfu = NULL; if (IsOS(OS_ANYSERVER)) { // On Server SKUs, only administrators get a default MFU // and they get the special server administrator MFU if (IsOS(OS_SERVERADMINUI)) { pmfu = &c_mfuSRVADM; } } else { // On Workstation SKUs, everybody gets a default MFU. pmfu = &c_mfuPROALL; } if (pmfu) { MSMFUEnumerator mfuMSFT; for (iSlot = 0; iSlot < ARRAYSIZE(rgpidlMFU); iSlot++) { if (!rgpidlMFU[iSlot]) { rgpidlMFU[iSlot] = mfuMSFT.Next(pmfu, &mex); } } } // Now build up the new MFU given this information UEMINFO uei; uei.cbSize = sizeof(uei); uei.dwMask = UEIM_HIT | UEIM_FILETIME; GetSystemTimeAsFileTime(&uei.ftExecute); // All apps get the same timestamp of "now minus one UEM unit" // 1 UEM unit = 1<<30 FILETIME units DecrementFILETIME(&uei.ftExecute, 1 << 30); for (iSlot = 0; iSlot < ARRAYSIZE(rgpidlMFU); iSlot++) { if (!rgpidlMFU[iSlot]) { continue; } // Number of points decrease as you go down the list, with // the bottom slot getting 14 points. uei.cHit = 14 + ARRAYSIZE(rgpidlMFU) - 1 - iSlot; // Shortcut points are read via UEME_RUNPIDL so that's // how we have to set them. IShellFolder *psf; LPCITEMIDLIST pidlChild; if (SUCCEEDED(SHBindToIDListParent(rgpidlMFU[iSlot], IID_PPV_ARG(IShellFolder, &psf), &pidlChild))) { _SetUEMPidlInfo(psf, pidlChild, &uei); psf->Release(); } } // Clean up for (iSlot = 0; iSlot < ARRAYSIZE(rgpidlMFU); iSlot++) { ILFree(rgpidlMFU[iSlot]); } // MFUExclusion destructor runs here } SHCoUninitialize(hrInit); }