Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

484 lines
13 KiB

  1. #include "stdafx.h"
  2. #include "sfthost.h"
  3. #include "uemapp.h"
  4. #include <desktray.h>
  5. #include "tray.h"
  6. #include "rcids.h"
  7. #include "mfulist.h"
  8. #define STRSAFE_NO_CB_FUNCTIONS
  9. #define STRSAFE_NO_DEPRECATE
  10. #include <strsafe.h>
  11. //---------------------------------------------------------------------------
  12. //
  13. // Create the initial MFU.
  14. //
  15. // Due to the way sysprep works, we cannot do this work in
  16. // per-user install because "reseal" copies the already-installed user
  17. // to the default hive, so all new users will bypass per-user install
  18. // since ActiveSetup thinks that they have already been installed...
  19. //
  20. //
  21. // We need a parallel list of hard-coded English links so we can get
  22. // the correct shortcut name on MUI systems.
  23. //
  24. #define MAX_MSMFUENTRIES 16
  25. struct MFULIST {
  26. UINT idsBase;
  27. LPCTSTR rgpszEnglish[MAX_MSMFUENTRIES];
  28. };
  29. #define MAKEMFU(name) \
  30. const MFULIST c_mfu##name = { IDS_MFU_##name##_00, { MFU_ENUMC(name) } };
  31. #ifdef _WIN64
  32. MAKEMFU(PRO64ALL)
  33. MAKEMFU(SRV64ADM)
  34. #define c_mfuPROALL c_mfuPRO64ALL
  35. #define c_mfuSRVADM c_mfuSRV64ADM
  36. #else
  37. MAKEMFU(PRO32ALL)
  38. MAKEMFU(SRV32ADM)
  39. #define c_mfuPROALL c_mfuPRO32ALL
  40. #define c_mfuSRVADM c_mfuSRV32ADM
  41. #endif
  42. //---------------------------------------------------------------------------
  43. //
  44. // _GetPinnedItemTarget
  45. //
  46. // Given a pidl, find the executable that will ultimately be launched.
  47. //
  48. // This tunnels through shortcuts and resolves the magical "Internet"
  49. // and "Email" icons to the respective registered programs.
  50. //
  51. BOOL _GetPinnedItemTarget(LPCITEMIDLIST pidl, LPTSTR *ppszPath)
  52. {
  53. *ppszPath = NULL;
  54. IShellFolder *psf;
  55. LPCITEMIDLIST pidlChild;
  56. if (SUCCEEDED(SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild)))
  57. {
  58. IShellLink *psl;
  59. IExtractIcon *pxi;
  60. if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, &pidlChild,
  61. IID_PPV_ARG_NULL(IShellLink, &psl))))
  62. {
  63. TCHAR szPath[MAX_PATH];
  64. TCHAR szPathExpanded[MAX_PATH];
  65. if (psl->GetPath(szPath, ARRAYSIZE(szPath), 0, SLGP_RAWPATH) == S_OK &&
  66. SHExpandEnvironmentStrings(szPath, szPathExpanded, ARRAYSIZE(szPathExpanded)))
  67. {
  68. SHStrDup(szPathExpanded, ppszPath);
  69. }
  70. psl->Release();
  71. }
  72. else if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, &pidlChild,
  73. IID_PPV_ARG_NULL(IExtractIcon, &pxi))))
  74. {
  75. // There is no way to get the IAssociationElement directly, so
  76. // we get the IExtractIcon and then ask him for the IAssociationElement.
  77. IAssociationElement *pae;
  78. if (SUCCEEDED(IUnknown_QueryService(pxi, IID_IAssociationElement, IID_PPV_ARG(IAssociationElement, &pae))))
  79. {
  80. pae->QueryString(AQVS_APPLICATION_PATH, L"open", ppszPath);
  81. pae->Release();
  82. }
  83. pxi->Release();
  84. }
  85. psf->Release();
  86. }
  87. return *ppszPath != NULL;
  88. }
  89. //---------------------------------------------------------------------------
  90. //
  91. // MFUExclusion
  92. //
  93. // Keep track of apps that should be excluded from the MFU.
  94. class MFUExclusion
  95. {
  96. public:
  97. MFUExclusion();
  98. ~MFUExclusion();
  99. BOOL IsExcluded(LPCITEMIDLIST pidl) const;
  100. private:
  101. // worst-case default pin list size
  102. enum {MAX_EXCLUDED = 3 };
  103. PWSTR _rgpszExclude[MAX_EXCLUDED];
  104. int _cExcluded;
  105. };
  106. MFUExclusion::MFUExclusion() : _cExcluded(0)
  107. {
  108. IStartMenuPin *psmpin;
  109. HRESULT hr;
  110. hr = CoCreateInstance(CLSID_StartMenuPin, NULL, CLSCTX_INPROC_SERVER,
  111. IID_PPV_ARG(IStartMenuPin, &psmpin));
  112. if (SUCCEEDED(hr))
  113. {
  114. IEnumIDList *penum;
  115. if (SUCCEEDED(psmpin->EnumObjects(&penum)))
  116. {
  117. LPITEMIDLIST pidl;
  118. while (_cExcluded < ARRAYSIZE(_rgpszExclude) &&
  119. penum->Next(1, &pidl, NULL) == S_OK)
  120. {
  121. if (_GetPinnedItemTarget(pidl, &_rgpszExclude[_cExcluded]))
  122. {
  123. _cExcluded++;
  124. }
  125. ILFree(pidl);
  126. }
  127. penum->Release();
  128. }
  129. psmpin->Release();
  130. }
  131. }
  132. MFUExclusion::~MFUExclusion()
  133. {
  134. for (int i = 0; i < _cExcluded; i++)
  135. {
  136. SHFree(_rgpszExclude[i]);
  137. }
  138. }
  139. BOOL MFUExclusion::IsExcluded(LPCITEMIDLIST pidl) const
  140. {
  141. BOOL fRc = FALSE;
  142. LPTSTR pszTarget;
  143. if (_GetPinnedItemTarget(pidl, &pszTarget))
  144. {
  145. for (int i = 0; i < _cExcluded; i++)
  146. {
  147. if (lstrcmpi(_rgpszExclude[i], pszTarget) == 0)
  148. {
  149. fRc = TRUE;
  150. break;
  151. }
  152. }
  153. SHFree(pszTarget);
  154. }
  155. return fRc;
  156. }
  157. extern "C" HKEY g_hkeyExplorer;
  158. void ClearUEMData();
  159. //---------------------------------------------------------------------------
  160. //
  161. // MFUEnumerator (and derived OEMMFUEnumerator, MSMFUEnumerator)
  162. //
  163. // Enumerate applications to be added to the default MFU.
  164. #define MAX_OEMMFUENTRIES 4
  165. class MFUEnumerator
  166. {
  167. public:
  168. MFUEnumerator() : _dwIndex(0) { }
  169. protected:
  170. DWORD _dwIndex;
  171. };
  172. #define REGSTR_PATH_SMDEN TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\SMDEn")
  173. class OEMMFUEnumerator : protected MFUEnumerator
  174. {
  175. public:
  176. OEMMFUEnumerator() : _hk(NULL)
  177. {
  178. RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_SMDEN, 0, KEY_READ, &_hk);
  179. }
  180. ~OEMMFUEnumerator()
  181. {
  182. if (_hk)
  183. {
  184. RegCloseKey(_hk);
  185. }
  186. }
  187. LPITEMIDLIST Next(const MFUExclusion *pmex);
  188. private:
  189. HKEY _hk;
  190. };
  191. LPITEMIDLIST OEMMFUEnumerator::Next(const MFUExclusion *pmex)
  192. {
  193. if (!_hk)
  194. {
  195. return NULL; // No entries at all
  196. }
  197. restart:
  198. if (_dwIndex >= MAX_OEMMFUENTRIES)
  199. {
  200. return NULL; // No more entries
  201. }
  202. TCHAR szKey[20];
  203. DWORD dwCurrentIndex = _dwIndex++;
  204. wnsprintf(szKey, ARRAYSIZE(szKey), TEXT("OEM%d"), dwCurrentIndex);
  205. TCHAR szPath[MAX_PATH];
  206. HRESULT hr = SHLoadRegUIStringW(_hk, szKey, szPath, ARRAYSIZE(szPath));
  207. if (FAILED(hr))
  208. {
  209. goto restart;
  210. }
  211. TCHAR szPathExpanded[MAX_PATH];
  212. SHExpandEnvironmentStrings(szPath, szPathExpanded, ARRAYSIZE(szPathExpanded));
  213. LPITEMIDLIST pidl = ILCreateFromPath(szPathExpanded);
  214. if (!pidl)
  215. {
  216. goto restart;
  217. }
  218. if (pmex->IsExcluded(pidl))
  219. {
  220. // Excluded - skip it
  221. ILFree(pidl);
  222. goto restart;
  223. }
  224. return pidl;
  225. }
  226. class MSMFUEnumerator : protected MFUEnumerator
  227. {
  228. public:
  229. LPITEMIDLIST Next(const MFULIST *pmfu, const MFUExclusion *pmex);
  230. };
  231. LPITEMIDLIST MSMFUEnumerator::Next(const MFULIST *pmfu, const MFUExclusion *pmex)
  232. {
  233. restart:
  234. if (_dwIndex >= MAX_MSMFUENTRIES)
  235. {
  236. return NULL; // No more entries
  237. }
  238. DWORD dwCurrentIndex = _dwIndex++;
  239. //
  240. // If this is excluded by policy, then skip it.
  241. //
  242. if (StrCmpC(pmfu->rgpszEnglish[dwCurrentIndex], TEXT(MFU_SETDEFAULTS)) == 0 &&
  243. SHRestricted(REST_NOSMCONFIGUREPROGRAMS))
  244. {
  245. goto restart;
  246. }
  247. //
  248. // If this entry is blank, then skip it.
  249. //
  250. TCHAR szPath[MAX_PATH];
  251. if (!LoadString(_Module.GetModuleInstance(), pmfu->idsBase + dwCurrentIndex,
  252. szPath, ARRAYSIZE(szPath)))
  253. {
  254. goto restart;
  255. }
  256. TCHAR szPathExpanded[MAX_PATH];
  257. SHExpandEnvironmentStrings(szPath, szPathExpanded, ARRAYSIZE(szPathExpanded));
  258. LPITEMIDLIST pidl = ILCreateFromPath(szPathExpanded);
  259. if (!pidl)
  260. {
  261. // Doesn't exist under localized name; try the English name
  262. SHExpandEnvironmentStrings(pmfu->rgpszEnglish[dwCurrentIndex], szPathExpanded, ARRAYSIZE(szPathExpanded));
  263. pidl = ILCreateFromPath(szPathExpanded);
  264. }
  265. if (!pidl)
  266. {
  267. // Doesn't exist at all - skip it
  268. goto restart;
  269. }
  270. if (pmex->IsExcluded(pidl))
  271. {
  272. // Excluded - skip it
  273. ILFree(pidl);
  274. goto restart;
  275. }
  276. return pidl;
  277. }
  278. #ifdef DEBUG
  279. void ValidateMFUList(const MFULIST *pmfu)
  280. {
  281. for (int i = 0; i < MAX_MSMFUENTRIES; i++)
  282. {
  283. TCHAR szBuf[MAX_PATH];
  284. LoadString(_Module.GetModuleInstance(), pmfu->idsBase + i, szBuf, ARRAYSIZE(szBuf));
  285. ASSERT(StrCmpC(szBuf, pmfu->rgpszEnglish[i]) == 0);
  286. }
  287. }
  288. void ValidateInitialMFUTables()
  289. {
  290. // If this is the English build, then validate that the resources match
  291. // the hard-coded table.
  292. if (GetUserDefaultUILanguage() == MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US))
  293. {
  294. ValidateMFUList(&c_mfuPROALL);
  295. ValidateMFUList(&c_mfuSRVADM);
  296. }
  297. // The PRO list must contain a copy of MFU_SETDEFAULTS for the
  298. // policy exclusion code to work.
  299. BOOL fFound = FALSE;
  300. for (int i = 0; i < MAX_MSMFUENTRIES; i++)
  301. {
  302. if (StrCmpC(c_mfuPROALL.rgpszEnglish[i], TEXT(MFU_SETDEFAULTS)) == 0)
  303. {
  304. fFound = TRUE;
  305. break;
  306. }
  307. }
  308. ASSERT(fFound);
  309. }
  310. #endif
  311. void CreateInitialMFU(BOOL fReset)
  312. {
  313. #ifdef DEBUG
  314. ValidateInitialMFUTables();
  315. #endif
  316. HRESULT hrInit = SHCoInitialize();
  317. // Delete any dregs left over from "sysprep -reseal".
  318. // This also prevents OEMs from spamming the pin list.
  319. SHDeleteKey(g_hkeyExplorer, TEXT("StartPage"));
  320. SHDeleteValue(g_hkeyExplorer, TEXT("Advanced"), TEXT("StartButtonBalloonTip"));
  321. // Start with a clean slate if so requested
  322. if (fReset)
  323. {
  324. ClearUEMData();
  325. }
  326. // Okay now build the default MFU
  327. {
  328. // nested scope so MFUExclusion gets destructed before we
  329. // SHCoUninitialize()
  330. MFUExclusion mex;
  331. int iSlot;
  332. LPITEMIDLIST rgpidlMFU[REGSTR_VAL_DV2_MINMFU_DEFAULT] = { 0 };
  333. // Assert that the slots are evenly shared between MSFT and the OEM
  334. COMPILETIME_ASSERT(ARRAYSIZE(rgpidlMFU) % 2 == 0);
  335. // The OEM can provide up to four apps, and we will put as many
  336. // as fit into into bottom half.
  337. {
  338. OEMMFUEnumerator mfuOEM;
  339. for (iSlot = ARRAYSIZE(rgpidlMFU)/2; iSlot < ARRAYSIZE(rgpidlMFU); iSlot++)
  340. {
  341. rgpidlMFU[iSlot] = mfuOEM.Next(&mex);
  342. }
  343. }
  344. // The top half (and any unused slots in the bottom half)
  345. // go to MSFT (up to MAX_MSMFUENTRIES MSFT apps); which list
  346. // we use depends on the SKU and whether we are an administrator.
  347. const MFULIST *pmfu = NULL;
  348. if (IsOS(OS_ANYSERVER))
  349. {
  350. // On Server SKUs, only administrators get a default MFU
  351. // and they get the special server administrator MFU
  352. if (IsOS(OS_SERVERADMINUI))
  353. {
  354. pmfu = &c_mfuSRVADM;
  355. }
  356. }
  357. else
  358. {
  359. // On Workstation SKUs, everybody gets a default MFU.
  360. pmfu = &c_mfuPROALL;
  361. }
  362. if (pmfu)
  363. {
  364. MSMFUEnumerator mfuMSFT;
  365. for (iSlot = 0; iSlot < ARRAYSIZE(rgpidlMFU); iSlot++)
  366. {
  367. if (!rgpidlMFU[iSlot])
  368. {
  369. rgpidlMFU[iSlot] = mfuMSFT.Next(pmfu, &mex);
  370. }
  371. }
  372. }
  373. // Now build up the new MFU given this information
  374. UEMINFO uei;
  375. uei.cbSize = sizeof(uei);
  376. uei.dwMask = UEIM_HIT | UEIM_FILETIME;
  377. GetSystemTimeAsFileTime(&uei.ftExecute);
  378. // All apps get the same timestamp of "now minus one UEM unit"
  379. // 1 UEM unit = 1<<30 FILETIME units
  380. DecrementFILETIME(&uei.ftExecute, 1 << 30);
  381. for (iSlot = 0; iSlot < ARRAYSIZE(rgpidlMFU); iSlot++)
  382. {
  383. if (!rgpidlMFU[iSlot])
  384. {
  385. continue;
  386. }
  387. // Number of points decrease as you go down the list, with
  388. // the bottom slot getting 14 points.
  389. uei.cHit = 14 + ARRAYSIZE(rgpidlMFU) - 1 - iSlot;
  390. // Shortcut points are read via UEME_RUNPIDL so that's
  391. // how we have to set them.
  392. IShellFolder *psf;
  393. LPCITEMIDLIST pidlChild;
  394. if (SUCCEEDED(SHBindToIDListParent(rgpidlMFU[iSlot], IID_PPV_ARG(IShellFolder, &psf), &pidlChild)))
  395. {
  396. _SetUEMPidlInfo(psf, pidlChild, &uei);
  397. psf->Release();
  398. }
  399. }
  400. // Clean up
  401. for (iSlot = 0; iSlot < ARRAYSIZE(rgpidlMFU); iSlot++)
  402. {
  403. ILFree(rgpidlMFU[iSlot]);
  404. }
  405. // MFUExclusion destructor runs here
  406. }
  407. SHCoUninitialize(hrInit);
  408. }