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.

1612 lines
54 KiB

  1. //
  2. // CleanupWiz.cpp
  3. //
  4. #include "CleanupWiz.h"
  5. #include "resource.h"
  6. #include "dblnul.h"
  7. #include <windowsx.h> // for SetWindowFont
  8. #include <varutil.h>
  9. #include <commctrl.h>
  10. #include <shlwapi.h>
  11. #include <shguidp.h>
  12. #include <ieguidp.h>
  13. // UEM stuff: including this source file is the
  14. // recommended way of using it in your project
  15. // (see comments in the file itself for the reason)
  16. #include "..\inc\uassist.cpp"
  17. ////////////////////////////////////////////
  18. //
  19. // Globals, constants, externs etc...
  20. //
  21. ////////////////////////////////////////////
  22. // none of these strings are ever localized, so it's safe to use them
  23. const LPTSTR c_szRegStrSHELLFOLDERS = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
  24. const LPTSTR c_szRegStrDESKTOPNAMESPACE = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace");
  25. const LPTSTR c_szRegStrPROFILELIST = TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList");
  26. const LPTSTR c_szRegStrMSNCODES = TEXT("Software\\Microsoft\\MSN6\\Setup\\MSN\\Codes");
  27. const LPTSTR c_szRegStrPATH_OCMANAGER = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Setup\\OC Manager\\Subcomponents");
  28. const LPTSTR c_szRegStrWMP_PATH_SETUP = TEXT("Software\\Microsoft\\MediaPlayer\\Setup");
  29. const LPTSTR c_szRegStrPROFILESDIR = TEXT("ProfilesDirectory");
  30. const LPTSTR c_szRegStrALLUSERS = TEXT("AllUsersProfile");
  31. const LPTSTR c_szRegStrDEFAULTUSER = TEXT("DefaultUserProfile");
  32. const LPTSTR c_szRegStrDESKTOP = TEXT("Desktop");
  33. const LPTSTR c_szRegStrMSN_IAONLY = TEXT("IAOnly");
  34. const LPTSTR c_szDESKTOP_DIR = TEXT("Desktop"); // backup in case we can't get the localized version
  35. const LPTSTR c_szRegStrIEACCESS = TEXT("IEAccess");
  36. const LPTSTR c_szRegStrYES = TEXT("yes");
  37. const LPTSTR c_szRegStrWMP_REGVALUE = TEXT("DesktopShortcut");
  38. const LPTSTR c_szDEFAULT_USER = TEXT("Default User");
  39. const LPTSTR c_szVAL_TIME = TEXT("Last used time");
  40. const LPTSTR c_szVAL_DELTA_DAYS = TEXT("Days between clean up");
  41. const LPTSTR c_szVAL_DONTRUN = TEXT("NoRun");
  42. const LPTSTR c_szVALUE_STARTPANEL = TEXT("NewStartPanel");
  43. const LPTSTR c_szVALUE_CLASSICMENU = TEXT("ClassicStartMenu");
  44. const LPTSTR c_szOEM_TITLEVAL = TEXT("DesktopShortcutsFolderName");
  45. const LPTSTR c_szOEM_DISABLE = TEXT("DesktopShortcutsCleanupDisable");
  46. const LPTSTR c_szOEM_SEVENDAY_DISABLE = TEXT("OemDesktopCleanupDisable");
  47. extern HINSTANCE g_hInst;
  48. STDMETHODIMP GetItemCLSID(IShellFolder2 *psf, LPCITEMIDLIST pidlLast, CLSID *pclsid);
  49. //
  50. // number of items to grow dsa by
  51. //
  52. const int c_GROWBYSIZE = 4;
  53. //
  54. // number of pages in the wizard
  55. //
  56. const int c_NUM_PAGES = 3;
  57. //
  58. // dialog prompt text length
  59. //
  60. const int c_MAX_PROMPT_TEXT = 1024;
  61. const int c_MAX_HEADER_LEN = 64;
  62. const int c_MAX_DATE_LEN = 40;
  63. //
  64. // file modified more than 60 days back is candidate for cleanup
  65. // this value can be overriden by policy
  66. //
  67. const int c_NUMDAYSTODECAY = 60;
  68. //
  69. // Needed for hiding regitems
  70. //
  71. #define DEFINE_SCID(name, fmtid, pid) const SHCOLUMNID name = { fmtid, pid }
  72. DEFINE_SCID(SCID_DESCRIPTIONID, PSGUID_SHELLDETAILS, PID_DESCRIPTIONID);
  73. //
  74. // pointer to member function typedef
  75. //
  76. typedef INT_PTR (STDMETHODCALLTYPE CCleanupWiz::* PCFC_DlgProcFn)(HWND, UINT, WPARAM, LPARAM);
  77. //
  78. // struct for helping us manage our dialog procs
  79. //
  80. typedef struct
  81. {
  82. CCleanupWiz * pcfc;
  83. PCFC_DlgProcFn pfnDlgProc;
  84. } DLGPROCINFO, *PDLGPROCINFO;
  85. //
  86. // enum for columns
  87. //
  88. typedef enum eColIndex
  89. {
  90. FC_COL_SHORTCUT,
  91. FC_COL_DATE
  92. };
  93. //////////////////////////////////////////////////////
  94. //
  95. // iDays can be negative or positive, indicating time in the past or future
  96. //
  97. //
  98. #define FTsPerDayOver1000 (10000*60*60*24) // we've got (1000 x 10,000) 100ns intervals per second
  99. STDAPI_(void) GetFileTimeNDaysFromGivenTime(const FILETIME *pftGiven, FILETIME * pftReturn, int iDays)
  100. {
  101. __int64 i64 = *((__int64 *) pftGiven);
  102. i64 += Int32x32To64(iDays*1000,FTsPerDayOver1000);
  103. *pftReturn = *((FILETIME *) &i64);
  104. }
  105. STDAPI_(void) GetFileTimeNDaysFromCurrentTime(FILETIME *pf, int iDays)
  106. {
  107. SYSTEMTIME st;
  108. FILETIME ftNow;
  109. GetLocalTime(&st);
  110. SystemTimeToFileTime(&st, &ftNow);
  111. GetFileTimeNDaysFromGivenTime(&ftNow, pf, iDays);
  112. }
  113. /////////////////////////////////////////////////
  114. //
  115. //
  116. // The CCleanupWiz class implementation
  117. //
  118. //
  119. /////////////////////////////////////////////////
  120. CCleanupWiz::CCleanupWiz(): _psf(NULL),
  121. _hdsaItems(NULL),
  122. _hTitleFont(NULL),
  123. _iDeltaDays(0),
  124. _dwCleanMode(CLEANUP_MODE_NORMAL)
  125. {
  126. INITCOMMONCONTROLSEX icce;
  127. icce.dwSize = sizeof(icce);
  128. icce.dwICC = ICC_LISTVIEW_CLASSES;
  129. _bInited = InitCommonControlsEx(&icce) && SUCCEEDED(SHGetDesktopFolder(&_psf));
  130. };
  131. CCleanupWiz::~CCleanupWiz()
  132. {
  133. _CleanUpDSA();
  134. ATOMICRELEASE(_psf);
  135. };
  136. STDMETHODIMP CCleanupWiz::Run(DWORD dwCleanMode, HWND hwndParent)
  137. {
  138. HRESULT hr;
  139. if (!_bInited)
  140. {
  141. hr = E_FAIL;
  142. }
  143. else
  144. {
  145. _dwCleanMode = dwCleanMode;
  146. if (CLEANUP_MODE_SILENT == _dwCleanMode)
  147. {
  148. hr = _RunSilent();
  149. }
  150. else
  151. {
  152. hr = _RunInteractive(hwndParent);
  153. }
  154. }
  155. return hr;
  156. }
  157. STDMETHODIMP CCleanupWiz::_RunInteractive(HWND hwndParent)
  158. {
  159. HRESULT hr;
  160. _iDeltaDays = GetNumDaysBetweenCleanup();
  161. if (_iDeltaDays < 0)
  162. {
  163. _iDeltaDays = c_NUMDAYSTODECAY; //initial default value
  164. }
  165. LoadString(g_hInst, IDS_ARCHIVEFOLDER, _szFolderName, MAX_PATH);
  166. // init the common control classes we need
  167. hr = _LoadDesktopContents();
  168. if (SUCCEEDED(hr))
  169. {
  170. UINT cItems = DSA_GetItemCount(_hdsaItems);
  171. if (CLEANUP_MODE_NORMAL == _dwCleanMode)
  172. {
  173. if (cItems > 0) // if there are items, we want to notify and proceed only if the user wants us to.
  174. {
  175. hr = _ShowBalloonNotification();
  176. }
  177. else
  178. {
  179. hr = S_FALSE;
  180. }
  181. }
  182. else
  183. {
  184. ASSERT(CLEANUP_MODE_ALL == _dwCleanMode);
  185. hr = S_OK; // run manually, we show everything
  186. }
  187. if (S_OK == hr)
  188. {
  189. _cItemsOnDesktop = cItems;
  190. hr = _InitializeAndLaunchWizard(hwndParent);
  191. }
  192. _LogUsage(); // set registry values to indicate the last run time
  193. }
  194. return hr;
  195. }
  196. //
  197. // Creates the property pages for the wizard and launches the wizard
  198. //
  199. //
  200. STDMETHODIMP CCleanupWiz::_InitializeAndLaunchWizard(HWND hwndParent)
  201. {
  202. HRESULT hr = S_OK;
  203. DLGPROCINFO adpi[c_NUM_PAGES];
  204. HPROPSHEETPAGE ahpsp[c_NUM_PAGES];
  205. PROPSHEETPAGE psp = {0};
  206. if (!_hTitleFont)
  207. {
  208. NONCLIENTMETRICS ncm = {0};
  209. ncm.cbSize = sizeof(ncm);
  210. SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
  211. LOGFONT TitleLogFont = ncm.lfMessageFont;
  212. TitleLogFont.lfWeight = FW_BOLD;
  213. TCHAR szFont[128];
  214. LoadString(g_hInst, IDS_TITLELOGFONT, szFont, ARRAYSIZE(szFont));
  215. if (SUCCEEDED(StringCchCopy(TitleLogFont.lfFaceName, ARRAYSIZE(TitleLogFont.lfFaceName), szFont)))
  216. {
  217. HDC hdc = GetDC(NULL);
  218. INT FontSize = 12;
  219. TitleLogFont.lfHeight = 0 - GetDeviceCaps(hdc, LOGPIXELSY) * FontSize / 72;
  220. _hTitleFont = CreateFontIndirect(&TitleLogFont);
  221. ReleaseDC(NULL, hdc);
  222. }
  223. }
  224. //
  225. // Intro Page
  226. //
  227. adpi[0].pcfc = this;
  228. adpi[0].pfnDlgProc = &CCleanupWiz::_IntroPageDlgProc;
  229. psp.dwSize = sizeof(psp);
  230. psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
  231. psp.hInstance = g_hInst;
  232. psp.lParam = (LPARAM) &adpi[0];
  233. psp.pfnDlgProc = s_StubDlgProc;
  234. psp.pszTemplate = MAKEINTRESOURCE(IDD_INTRO);
  235. ahpsp[0] = CreatePropertySheetPage(&psp);
  236. //
  237. // Choose files page
  238. //
  239. adpi[1].pcfc = this;
  240. adpi[1].pfnDlgProc = &CCleanupWiz::_ChooseFilesPageDlgProc;
  241. psp.hInstance = g_hInst;
  242. psp.dwFlags = PSP_DEFAULT|PSP_USEHEADERTITLE| PSP_USEHEADERSUBTITLE;
  243. psp.lParam = (LPARAM) &adpi[1];
  244. psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_CHOOSEFILES);
  245. psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_CHOOSEFILES_INFO);
  246. psp.pszTemplate = MAKEINTRESOURCE(IDD_CHOOSEFILES);
  247. psp.pfnDlgProc = s_StubDlgProc;
  248. ahpsp[1] = CreatePropertySheetPage(&psp);
  249. //
  250. // Completion Page
  251. //
  252. adpi[2].pcfc = this;
  253. adpi[2].pfnDlgProc = &CCleanupWiz::_FinishPageDlgProc;
  254. psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
  255. psp.hInstance = g_hInst;
  256. psp.lParam = (LPARAM) &adpi[2];
  257. psp.pfnDlgProc = s_StubDlgProc;
  258. psp.pszTemplate = MAKEINTRESOURCE(IDD_FINISH);
  259. ahpsp[2] = CreatePropertySheetPage(&psp);
  260. //
  261. // The wizard property sheet
  262. //
  263. PROPSHEETHEADER psh = {0};
  264. psh.dwSize = sizeof(psh);
  265. psh.hInstance = g_hInst;
  266. psh.hwndParent = hwndParent;
  267. psh.phpage = ahpsp;
  268. psh.dwFlags = PSH_WIZARD97|PSH_WATERMARK|PSH_HEADER;
  269. psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
  270. psh.pszbmHeader = MAKEINTRESOURCE(IDB_LOGO);
  271. psh.nStartPage = _cItemsOnDesktop ? 0 : c_NUM_PAGES - 1; // if there are no pages on desktop, start on final page
  272. psh.nPages = c_NUM_PAGES;
  273. PropertySheet(&psh);
  274. return hr;
  275. }
  276. //
  277. // Pops up a balloon notification tip which asks the user
  278. // if he wants to clean up the desktop.
  279. //
  280. // returns S_OK if user wants us to cleanup.
  281. //
  282. STDMETHODIMP CCleanupWiz::_ShowBalloonNotification()
  283. {
  284. IUserNotification *pun;
  285. HRESULT hr = CoCreateInstance(CLSID_UserNotification, NULL,
  286. CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUserNotification, &pun));
  287. if (SUCCEEDED(hr))
  288. {
  289. TCHAR szTitle[64], szMsg[256]; // we leave enough room for localization bloat
  290. LoadString(g_hInst, IDS_NOTIFICATION_TITLE, szTitle, ARRAYSIZE(szTitle));
  291. LoadString(g_hInst, IDS_NOTIFICATION_TEXT, szMsg, ARRAYSIZE(szMsg));
  292. // these pun->Set functions can't fail...
  293. pun->SetIconInfo(LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_WIZ_ICON)), szTitle);
  294. pun->SetBalloonInfo(szTitle, szMsg, NIIF_WARNING);
  295. pun->SetBalloonRetry(20 * 1000, -1, 1); // try once, for 20 seconds
  296. // returns S_OK if user wants to continue, ERROR_CANCELLED if timedout
  297. // or canncelled otherwise.
  298. hr = pun->Show(NULL, 0); // we don't support iquerycontinue, we will just wait
  299. pun->Release();
  300. }
  301. return hr;
  302. }
  303. //
  304. // Gets the list of items on the desktop that should be cleaned
  305. //
  306. // if dwCleanMode == CLEANUP_MODE_NORMAL, it only loads items which have not been used recently
  307. // if dwCleanMode == CLEANUP_MODE_ALL, it loads all items on the desktop, marking those which have not been used recently
  308. //
  309. //
  310. STDMETHODIMP CCleanupWiz::_LoadDesktopContents()
  311. {
  312. ASSERT(_psf);
  313. ASSERT((CLEANUP_MODE_ALL == _dwCleanMode) || (CLEANUP_MODE_NORMAL == _dwCleanMode));
  314. IEnumIDList * ppenum;
  315. DWORD grfFlags = SHCONTF_NONFOLDERS;
  316. HRESULT hr = _psf->EnumObjects(NULL, grfFlags, &ppenum);
  317. if (SUCCEEDED(hr))
  318. {
  319. _CleanUpDSA();
  320. _hdsaItems = DSA_Create(sizeof(FOLDERITEMDATA), c_GROWBYSIZE);
  321. if (_hdsaItems)
  322. {
  323. ULONG celtFetched;
  324. FOLDERITEMDATA fid = {0};
  325. hr = S_OK;
  326. while(SUCCEEDED(hr) && (S_OK == ppenum->Next(1,&fid.pidl, &celtFetched)))
  327. {
  328. if (_IsSupportedType(fid.pidl)) // only support links and regitems
  329. {
  330. // note, the call to _IsCandidateForRemoval also obtains the last
  331. // used timestamp for the item
  332. BOOL bShouldRemove = _IsCandidateForRemoval(fid.pidl, &fid.ftLastUsed);
  333. if ( (CLEANUP_MODE_ALL == _dwCleanMode) || bShouldRemove)
  334. {
  335. SHFILEINFO sfi = {0};
  336. if (SHGetFileInfo((LPCTSTR) fid.pidl,
  337. 0,
  338. &sfi,
  339. sizeof(sfi),
  340. SHGFI_PIDL | SHGFI_DISPLAYNAME | SHGFI_ICON | SHGFI_SMALLICON ))
  341. {
  342. if (Str_SetPtr(&(fid.pszName), sfi.szDisplayName))
  343. {
  344. fid.hIcon = sfi.hIcon;
  345. fid.bSelected = bShouldRemove;
  346. if (-1 != DSA_AppendItem(_hdsaItems, &fid))
  347. {
  348. // All is well, the item has succesfully been added
  349. // to the dsa, we zero out the fields so as not to
  350. // free those resources now, they will be freed when the
  351. // dsa is destroyed.
  352. ZeroMemory(&fid, sizeof(fid));
  353. continue;
  354. }
  355. else
  356. {
  357. hr = E_OUTOFMEMORY;
  358. }
  359. }
  360. }
  361. }
  362. }
  363. // Common cleanup path for various failure cases above,
  364. // we did not add this item to the dsa, so cleanup now.
  365. _CleanUpDSAItem(&fid);
  366. }
  367. //
  368. // If we did not load any items into the dsa, we return S_FALSE
  369. //
  370. if (SUCCEEDED(hr))
  371. {
  372. hr = DSA_GetItemCount(_hdsaItems) > 0 ? S_OK : S_FALSE;
  373. }
  374. }
  375. else
  376. {
  377. // we failed to allocate memory for the DSA
  378. hr = E_OUTOFMEMORY;
  379. }
  380. ppenum->Release();
  381. }
  382. return hr;
  383. }
  384. //
  385. // Gets the list of items on the desktop that should be cleaned
  386. //
  387. // if dwCleanMode == CLEANUP_MODE_SILENT, it loads all items on all desktops, marking them all
  388. //
  389. //
  390. STDMETHODIMP CCleanupWiz::_LoadMergedDesktopContents()
  391. {
  392. ASSERT(_psf);
  393. IEnumIDList * ppenum;
  394. DWORD grfFlags = _dwCleanMode == CLEANUP_MODE_SILENT ? SHCONTF_FOLDERS | SHCONTF_NONFOLDERS: SHCONTF_NONFOLDERS;
  395. HRESULT hr = _psf->EnumObjects(NULL, grfFlags, &ppenum);
  396. if (SUCCEEDED(hr))
  397. {
  398. _CleanUpDSA();
  399. _hdsaItems = DSA_Create(sizeof(FOLDERITEMDATA), c_GROWBYSIZE);
  400. if (_hdsaItems)
  401. {
  402. ULONG celtFetched;
  403. FOLDERITEMDATA fid = {0};
  404. hr = S_OK;
  405. while(SUCCEEDED(hr) && (S_OK == ppenum->Next(1,&fid.pidl, &celtFetched)))
  406. {
  407. if (_IsSupportedType(fid.pidl)) // only support links and regitems
  408. {
  409. // note, the call to _IsCandidateForRemoval also obtains the last
  410. // used timestamp for the item
  411. BOOL bShouldRemove = ((CLEANUP_MODE_SILENT == _dwCleanMode) ||
  412. (_IsCandidateForRemoval(fid.pidl, &fid.ftLastUsed)));
  413. if ( (CLEANUP_MODE_ALL == _dwCleanMode) || bShouldRemove)
  414. {
  415. SHFILEINFO sfi = {0};
  416. if (SHGetFileInfo((LPCTSTR) fid.pidl,
  417. 0,
  418. &sfi,
  419. sizeof(sfi),
  420. SHGFI_PIDL | SHGFI_DISPLAYNAME | SHGFI_ICON | SHGFI_SMALLICON ))
  421. {
  422. if (Str_SetPtr(&(fid.pszName), sfi.szDisplayName))
  423. {
  424. fid.hIcon = sfi.hIcon;
  425. fid.bSelected = bShouldRemove;
  426. if (-1 != DSA_AppendItem(_hdsaItems, &fid))
  427. {
  428. // All is well, the item has succesfully been added
  429. // to the dsa, we zero out the fields so as not to
  430. // free those resources now, they will be freed when the
  431. // dsa is destroyed.
  432. ZeroMemory(&fid, sizeof(fid));
  433. continue;
  434. }
  435. else
  436. {
  437. hr = E_OUTOFMEMORY;
  438. }
  439. }
  440. }
  441. }
  442. }
  443. // Common cleanup path for various failure cases above,
  444. // we did not add this item to the dsa, so cleanup now.
  445. _CleanUpDSAItem(&fid);
  446. }
  447. //
  448. // If we did not load any items into the dsa, we return S_FALSE
  449. //
  450. if (SUCCEEDED(hr))
  451. {
  452. hr = DSA_GetItemCount(_hdsaItems) > 0 ? S_OK : S_FALSE;
  453. }
  454. }
  455. else
  456. {
  457. // we failed to allocate memory for the DSA
  458. hr = E_OUTOFMEMORY;
  459. }
  460. ppenum->Release();
  461. }
  462. return hr;
  463. }
  464. //
  465. // Expects the given pidl to be a link or regitem. Determines if it is a candidate for removal based
  466. // on when was the last time it was used.
  467. //
  468. STDMETHODIMP_(BOOL) CCleanupWiz::_IsCandidateForRemoval(LPCITEMIDLIST pidl, FILETIME * pftLastUsed)
  469. {
  470. BOOL bRet = FALSE; // be conservative, if we do not know anything about the
  471. // object we will not volunteer to remove it
  472. int cHit = 0;
  473. TCHAR szName[MAX_PATH];
  474. ASSERT(_psf);
  475. //
  476. // we store UEM usage info for the regitems and links on the desktop
  477. //
  478. if (SUCCEEDED(DisplayNameOf(_psf,
  479. pidl,
  480. SHGDN_INFOLDER | SHGDN_FORPARSING,
  481. szName,
  482. ARRAYSIZE(szName))))
  483. {
  484. if (SUCCEEDED(_GetUEMInfo(-1, (LPARAM) szName, &cHit, pftLastUsed)))
  485. {
  486. FILETIME ft;
  487. GetFileTimeNDaysFromCurrentTime(&ft, -_iDeltaDays);
  488. #ifdef DEBUG
  489. SYSTEMTIME st;
  490. FileTimeToSystemTime(&ft, &st);
  491. #endif
  492. bRet = (CompareFileTime(pftLastUsed, &ft) < 0);
  493. }
  494. }
  495. return bRet;
  496. }
  497. STDMETHODIMP CCleanupWiz::_ShouldShow(IShellFolder* psf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem)
  498. {
  499. HRESULT hr;
  500. IShellFolder2 *psf2;
  501. hr = psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2));
  502. if (SUCCEEDED(hr))
  503. {
  504. // Get the GUID in the pidl, which requires IShellFolder2.
  505. CLSID guidItem;
  506. hr = GetItemCLSID(psf2, pidlItem, &guidItem);
  507. if (SUCCEEDED(hr))
  508. {
  509. SHELLSTATE ss = {0};
  510. SHGetSetSettings(&ss, SSF_STARTPANELON, FALSE); //See if the StartPanel is on!
  511. TCHAR szRegPath[MAX_PATH];
  512. //Get the proper registry path based on if StartPanel is ON/OFF
  513. hr = StringCchPrintf(szRegPath, ARRAYSIZE(szRegPath), REGSTR_PATH_HIDDEN_DESKTOP_ICONS, (ss.fStartPanelOn ? c_szVALUE_STARTPANEL : c_szVALUE_CLASSICMENU));
  514. if (SUCCEEDED(hr))
  515. {
  516. //Convert the guid to a string
  517. TCHAR szGuidValue[MAX_GUID_STRING_LEN];
  518. SHStringFromGUID(guidItem, szGuidValue, ARRAYSIZE(szGuidValue));
  519. //See if this item is turned off in the registry.
  520. if (SHRegGetBoolUSValue(szRegPath, szGuidValue, FALSE, /* default */FALSE))
  521. hr = S_FALSE; //They want to hide it; So, return S_FALSE.
  522. if (SHRestricted(REST_NOMYCOMPUTERICON) && IsEqualCLSID(CLSID_MyComputer, guidItem))
  523. hr = S_FALSE;
  524. }
  525. }
  526. psf2->Release();
  527. }
  528. // if we fail for some reason, be generous and say that we should offer to clean this up
  529. if (FAILED(hr))
  530. {
  531. hr = S_OK;
  532. }
  533. return hr;
  534. }
  535. //
  536. // Normal, All: We only support removing regitems and links from the desktop
  537. // Silent: WE NEVER GET HERE FROM SILENT
  538. //
  539. STDMETHODIMP_(BOOL) CCleanupWiz::_IsSupportedType(LPCITEMIDLIST pidl)
  540. {
  541. ASSERT(_dwCleanMode != CLEANUP_MODE_SILENT);
  542. BOOL fRetVal = FALSE;
  543. eFILETYPE eType = _GetItemType(pidl);
  544. if (FC_TYPE_REGITEM == eType) // handle regitems
  545. {
  546. fRetVal = TRUE;
  547. if (S_OK != _ShouldShow(_psf, NULL, pidl)) // regitems must succeed _ShouldShow
  548. {
  549. fRetVal = FALSE;
  550. }
  551. else
  552. {
  553. IShellFolder2 *psf2;
  554. CLSID guidItem;
  555. if (SUCCEEDED(_psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2)))) // must not be one of the shell desktop items
  556. {
  557. if (SUCCEEDED(GetItemCLSID(psf2, pidl, &guidItem)) &&
  558. (IsEqualCLSID(CLSID_MyComputer, guidItem) ||
  559. IsEqualCLSID(CLSID_MyDocuments, guidItem) ||
  560. IsEqualCLSID(CLSID_NetworkPlaces, guidItem) ||
  561. IsEqualCLSID(CLSID_RecycleBin, guidItem)))
  562. {
  563. fRetVal = FALSE;
  564. }
  565. psf2->Release();
  566. }
  567. }
  568. }
  569. else if (FC_TYPE_LINK == eType) // handle links
  570. {
  571. fRetVal = TRUE;
  572. TCHAR szName[MAX_PATH];
  573. if (SUCCEEDED(SHGetNameAndFlags(pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, szName, ARRAYSIZE(szName), NULL)))
  574. {
  575. if (!lstrcmp(szName, _szFolderName)) // must not be the folder we're cleaning things up to
  576. {
  577. fRetVal = FALSE;
  578. }
  579. }
  580. }
  581. return fRetVal;
  582. }
  583. //
  584. // Returns the type of the pidl.
  585. // We are only interested in Links and Regitems, so we return FC_TYPE_OTHER for
  586. // all other items.
  587. //
  588. STDMETHODIMP_(eFILETYPE) CCleanupWiz::_GetItemType(LPCITEMIDLIST pidl)
  589. {
  590. eFILETYPE eftVal = FC_TYPE_OTHER;
  591. TCHAR szName[MAX_PATH];
  592. TCHAR szPath[MAX_PATH];
  593. IShellLink *psl;
  594. ASSERT(_psf);
  595. if (FAILED(SHGetNameAndFlags(pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, szName, ARRAYSIZE(szName), NULL)))
  596. {
  597. szName[0] = 0;
  598. }
  599. if (FAILED(SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), NULL)))
  600. {
  601. szPath[0] = 0;
  602. }
  603. if(_IsRegItemName(szName))
  604. {
  605. eftVal = FC_TYPE_REGITEM;
  606. }
  607. else if (SUCCEEDED( _psf->GetUIObjectOf(NULL,
  608. 1,
  609. &pidl,
  610. IID_PPV_ARG_NULL(IShellLink, &psl))))
  611. {
  612. eftVal = FC_TYPE_LINK;
  613. psl->Release();
  614. }
  615. else if (PathIsDirectory(szPath))
  616. {
  617. eftVal = FC_TYPE_FOLDER;
  618. }
  619. else
  620. {
  621. //
  622. // Maybe this item is a kind of .{GUID} object we created to restore
  623. // regitems. In that case we want to actually restore the regitem
  624. // at this point by marking it as unhidden.
  625. //
  626. LPTSTR pszExt = PathFindExtension(szName);
  627. if (TEXT('.') == *pszExt // it is a file extension
  628. && lstrlen(++pszExt) == (GUIDSTR_MAX - 1) // AND the extension is of the right length
  629. // note: GUIDSTR_MAX includes the terminating NULL
  630. // while lstrlen does not, hence the expression
  631. && TEXT('{') == *pszExt) // AND looks like it is a guid...
  632. {
  633. // we most prob have a bonafide guid string
  634. // pszExt now points to the beginning of the GUID string
  635. TCHAR szGUIDName[ARRAYSIZE(TEXT("::")) + GUIDSTR_MAX];
  636. // put it in the regitem SHGDN_FORPARSING name format, which is like
  637. //
  638. // "::{16B280C6-EE70-11D1-9066-00C04FD9189D}"
  639. //
  640. if (SUCCEEDED(StringCchPrintf(szGUIDName, ARRAYSIZE(szGUIDName), TEXT("::%s"), pszExt)))
  641. {
  642. LPITEMIDLIST pidlGUID;
  643. DWORD dwAttrib = SFGAO_NONENUMERATED;
  644. //
  645. // get the pidl of the regitem, if this call succeeds, it means we do have
  646. // a corresponding regitem in the desktop's namespace
  647. //
  648. if (SUCCEEDED(_psf->ParseDisplayName(NULL,
  649. NULL,
  650. szGUIDName,
  651. NULL,
  652. &pidlGUID,
  653. &dwAttrib)))
  654. {
  655. //
  656. // check if the regitem is marked as hidden
  657. //
  658. if (dwAttrib & SFGAO_NONENUMERATED)
  659. {
  660. //
  661. // One last check before we enable the regitem:
  662. // Does the regitem have the same display name as the .CLSID file.
  663. // In case the user has restored this .CLSID file and renamed it we will
  664. // not attempt to restore the regitem as it may confuse the user.
  665. //
  666. TCHAR szNameRegItem[MAX_PATH];
  667. if (SUCCEEDED((DisplayNameOf(_psf,
  668. pidl,
  669. SHGDN_NORMAL,
  670. szName,
  671. ARRAYSIZE(szName)))) &&
  672. SUCCEEDED((DisplayNameOf(_psf,
  673. pidlGUID,
  674. SHGDN_NORMAL,
  675. szNameRegItem,
  676. ARRAYSIZE(szNameRegItem)))) &&
  677. lstrcmp(szName, szNameRegItem) == 0)
  678. {
  679. if (SUCCEEDED(_HideRegPidl(pidlGUID, FALSE)))
  680. {
  681. // delete the file corresponding to the regitem
  682. if (SUCCEEDED(DisplayNameOf(_psf,
  683. pidl,
  684. SHGDN_NORMAL | SHGDN_FORPARSING,
  685. szName,
  686. ARRAYSIZE(szName))))
  687. {
  688. DeleteFile(szName); // too bad if we fail, we will just
  689. // have two identical icons on the desktop
  690. }
  691. //
  692. // Log the current time as the last used time of the regitem.
  693. // We just re-enabled this regitem but we do not have the
  694. // usage info for the corresponding .{CLSID} which the user had
  695. // been using so far. So we will be conservative and say that
  696. // it was used right now, so that it does not become a candidate
  697. // for removal soon. As this is a regitem that the user restored
  698. // after the wizard removed it, so it is a fair assumption that
  699. // the user has used it after restoring it and is not a candidate
  700. // for cleanup right now.
  701. //
  702. UEMFireEvent(&UEMIID_SHELL, UEME_RUNPATH, UEMF_XEVENT, -1, (LPARAM)szGUIDName);
  703. }
  704. }
  705. }
  706. ILFree(pidlGUID);
  707. }
  708. }
  709. }
  710. }
  711. return eftVal;
  712. }
  713. //
  714. // Determines if a filename is that of a regitem
  715. //
  716. // a regitem's SHGDN_INFOLDER | SHGDN_FORPARSING name is always "::{someguid}"
  717. //
  718. // CDefview::_LogDesktopLinksAndRegitems() uses the same test to determine
  719. // if a given pidl is a regitem. This case can lead to false positives if
  720. // you have other items on the desktop which have infoder parsing names
  721. // beginning with "::{", but as ':' is not presently allowed in filenames
  722. // it should not be a problem.
  723. //
  724. STDMETHODIMP_(BOOL) CCleanupWiz::_IsRegItemName(LPTSTR pszName)
  725. {
  726. return (pszName[0] == TEXT(':') && pszName[1] == TEXT(':') && pszName[2] == TEXT('{'));
  727. }
  728. STDMETHODIMP_(BOOL) CCleanupWiz::_CreateFakeRegItem(LPCTSTR pszDestPath, LPCTSTR pszName, LPCTSTR pszGUID)
  729. {
  730. BOOL fRetVal = FALSE;
  731. TCHAR szLinkName[MAX_PATH];
  732. if (SUCCEEDED(StringCchCopy(szLinkName, ARRAYSIZE(szLinkName), pszDestPath)) &&
  733. PathAppend(szLinkName, pszName) &&
  734. SUCCEEDED(StringCchCat(szLinkName, ARRAYSIZE(szLinkName), TEXT("."))) &&
  735. SUCCEEDED(StringCchCat(szLinkName, ARRAYSIZE(szLinkName), pszGUID)))
  736. {
  737. //
  738. // We use the CREATE_ALWAYS flag so that if the file already exists
  739. // in the Unused Desktop Files folder, we will go ahead and hide the
  740. // regitem.
  741. //
  742. HANDLE hFile = CreateFile(szLinkName, 0, FILE_SHARE_READ, NULL,
  743. CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  744. if (hFile != INVALID_HANDLE_VALUE)
  745. {
  746. // we created/opened the shortcut, now hide the regitem and close
  747. // the shortcut file
  748. fRetVal = TRUE;
  749. CloseHandle(hFile);
  750. }
  751. }
  752. return fRetVal;
  753. }
  754. //
  755. // Given path to an exe, returns the UEM hit count and last used date for it
  756. //
  757. STDMETHODIMP CCleanupWiz::_GetUEMInfo(WPARAM wParam, LPARAM lParam, int * pcHit, FILETIME * pftLastUsed)
  758. {
  759. UEMINFO uei;
  760. uei.cbSize = sizeof(uei);
  761. uei.dwMask = UEIM_HIT | UEIM_FILETIME;
  762. HRESULT hr = UEMQueryEvent(&UEMIID_SHELL, UEME_RUNPATH, wParam, lParam, &uei);
  763. if (SUCCEEDED(hr))
  764. {
  765. *pcHit = uei.cHit;
  766. *pftLastUsed = uei.ftExecute;
  767. }
  768. return hr;
  769. }
  770. STDMETHODIMP_(BOOL) CCleanupWiz::_ShouldProcess()
  771. {
  772. BOOL fRetVal = FALSE;
  773. if (_dwCleanMode == CLEANUP_MODE_SILENT)
  774. {
  775. fRetVal = TRUE;
  776. }
  777. else
  778. {
  779. int cItems = DSA_GetItemCount(_hdsaItems);
  780. for (int i = 0; i < cItems; i++)
  781. {
  782. FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems, i);
  783. if (pfid && pfid->bSelected)
  784. {
  785. fRetVal = TRUE;
  786. break;
  787. }
  788. }
  789. }
  790. return fRetVal;
  791. }
  792. //
  793. // Process the list of items. At this point _hdsaItems only contains the
  794. // items that the user wants to delete
  795. //
  796. STDMETHODIMP CCleanupWiz::_ProcessItems()
  797. {
  798. TCHAR szFolderLocation[MAX_PATH]; // desktop folder
  799. HRESULT hr = S_OK;
  800. if (_ShouldProcess())
  801. {
  802. LPITEMIDLIST pidlCommonDesktop = NULL;
  803. ASSERT(_psf);
  804. // use the archive folder on the desktop
  805. if (CLEANUP_MODE_SILENT != _dwCleanMode)
  806. {
  807. hr = DisplayNameOf(_psf, NULL, SHGDN_FORPARSING, szFolderLocation, ARRAYSIZE(szFolderLocation));
  808. }
  809. else // use the archive folder in Program Files
  810. {
  811. hr = SHGetFolderLocation(NULL, CSIDL_PROGRAM_FILES , NULL, 0, &pidlCommonDesktop);
  812. if (SUCCEEDED(hr))
  813. {
  814. hr = DisplayNameOf(_psf, pidlCommonDesktop, SHGDN_FORPARSING, szFolderLocation, ARRAYSIZE(szFolderLocation));
  815. }
  816. }
  817. if (SUCCEEDED(hr))
  818. {
  819. ASSERTMSG(*_szFolderName, "Desktop Cleaner: Archive Folder Name not present");
  820. // create the full path of the archive folder
  821. TCHAR szFolderPath[MAX_PATH];
  822. hr = StringCchCopy(szFolderPath, ARRAYSIZE(szFolderPath), szFolderLocation);
  823. if (SUCCEEDED(hr))
  824. {
  825. if (!PathAppend(szFolderPath, _szFolderName))
  826. {
  827. hr = E_FAIL;
  828. }
  829. else
  830. {
  831. //
  832. // We have to make sure that this folder exists, as otherwise, if we try to move
  833. // a single shortcut using SHFileOperation, that file will be renamed to the target
  834. // name instead of being put in a folder with that name.
  835. //
  836. SECURITY_ATTRIBUTES sa;
  837. sa.nLength = sizeof (sa);
  838. sa.lpSecurityDescriptor = NULL; // we get the default attributes for this process
  839. sa.bInheritHandle = FALSE;
  840. int iRetVal = SHCreateDirectoryEx(NULL, szFolderPath, &sa);
  841. if (ERROR_SUCCESS == iRetVal || ERROR_FILE_EXISTS == iRetVal || ERROR_ALREADY_EXISTS == iRetVal)
  842. {
  843. DblNulTermList dnSourceFiles;
  844. TCHAR szFileName[MAX_PATH + 1]; // to pad an extra null char for SHFileOpStruct
  845. //
  846. //
  847. // now we can start on the files we need to move
  848. //
  849. int cItems = DSA_GetItemCount(_hdsaItems);
  850. for (int i = 0; i < cItems; i++)
  851. {
  852. FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems, i);
  853. if (pfid && (pfid->bSelected || _dwCleanMode == CLEANUP_MODE_SILENT) &&
  854. SUCCEEDED(DisplayNameOf(_psf,pfid->pidl,
  855. SHGDN_FORPARSING,
  856. szFileName,
  857. ARRAYSIZE(szFileName) - 1)))
  858. {
  859. if (_IsRegItemName(szFileName))
  860. {
  861. // if its a regitem, we create a "Item Name.{GUID}" file
  862. // and mark the regitem as hidden.
  863. //
  864. if (_CreateFakeRegItem(szFolderPath, pfid->pszName, szFileName+2))
  865. {
  866. _HideRegPidl(pfid->pidl, TRUE);
  867. }
  868. }
  869. else // not a regitem, will move it
  870. {
  871. dnSourceFiles.AddString(szFileName);
  872. }
  873. }
  874. }
  875. if (dnSourceFiles.Count() > 0)
  876. {
  877. DblNulTermList dnTargetFolder;
  878. dnTargetFolder.AddString(szFolderPath);
  879. SHFILEOPSTRUCT sfo;
  880. sfo.hwnd = NULL;
  881. sfo.wFunc = FO_MOVE;
  882. sfo.pFrom = (LPCTSTR) dnSourceFiles;
  883. sfo.pTo = (LPCTSTR) dnTargetFolder;
  884. sfo.fFlags = FOF_NORECURSION | FOF_NOCONFIRMMKDIR | FOF_ALLOWUNDO ;
  885. hr = SHFileOperation(&sfo);
  886. }
  887. SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH, (LPCVOID) szFolderPath, 0);
  888. SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH, (LPCVOID) szFolderLocation, 0);
  889. }
  890. else
  891. {
  892. // we failed to create the Unused Desktop Files folder
  893. hr = E_FAIL;
  894. }
  895. }
  896. }
  897. }
  898. }
  899. return hr;
  900. }
  901. ////////////////////////////////////////////////////////
  902. //
  903. // DialogProcs
  904. //
  905. // TODO: test for accessibilty issues
  906. //
  907. ////////////////////////////////////////////////////////
  908. INT_PTR STDMETHODCALLTYPE CCleanupWiz::_IntroPageDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
  909. {
  910. INT_PTR ipRet = FALSE;
  911. switch (wMsg)
  912. {
  913. case WM_INITDIALOG:
  914. {
  915. HWND hWnd = GetDlgItem(hDlg, IDC_TEXT_TITLE_WELCOME);
  916. if (_hTitleFont)
  917. {
  918. SetWindowFont(hWnd, _hTitleFont, TRUE);
  919. }
  920. }
  921. break;
  922. case WM_NOTIFY :
  923. {
  924. LPNMHDR lpnm = (LPNMHDR) lParam;
  925. switch (lpnm->code)
  926. {
  927. case PSN_SETACTIVE:
  928. PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT);
  929. break;
  930. }
  931. break;
  932. }
  933. }
  934. return ipRet;
  935. }
  936. INT_PTR STDMETHODCALLTYPE CCleanupWiz::_ChooseFilesPageDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
  937. {
  938. INT_PTR ipRet = FALSE;
  939. HWND hwLV = NULL;
  940. switch (wMsg)
  941. {
  942. case WM_INITDIALOG:
  943. _InitChoosePage(hDlg);
  944. ipRet = TRUE;
  945. break;
  946. case WM_NOTIFY :
  947. LPNMHDR lpnm = (LPNMHDR) lParam;
  948. switch (lpnm->code)
  949. {
  950. case PSN_SETACTIVE:
  951. PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT | PSWIZB_BACK);
  952. hwLV = GetDlgItem(hDlg, IDC_LV_PROMPT);
  953. _SetCheckedState(hwLV);
  954. break;
  955. case PSN_WIZNEXT:
  956. // remember the items the user selected
  957. hwLV = GetDlgItem(hDlg, IDC_LV_PROMPT);
  958. _MarkSelectedItems(hwLV);
  959. break;
  960. case PSN_WIZBACK:
  961. // remember the items the user selected
  962. hwLV = GetDlgItem(hDlg, IDC_LV_PROMPT);
  963. _MarkSelectedItems(hwLV);
  964. break;
  965. }
  966. break;
  967. }
  968. return ipRet;
  969. }
  970. INT_PTR STDMETHODCALLTYPE CCleanupWiz::_FinishPageDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
  971. {
  972. INT_PTR ipRet = FALSE;
  973. switch (wMsg)
  974. {
  975. case WM_INITDIALOG:
  976. _InitFinishPage(hDlg);
  977. ipRet = TRUE;
  978. break;
  979. case WM_NOTIFY :
  980. {
  981. LPNMHDR lpnm = (LPNMHDR) lParam;
  982. switch (lpnm->code)
  983. {
  984. case PSN_SETACTIVE:
  985. PropSheet_SetWizButtons(GetParent(hDlg), _cItemsOnDesktop ? PSWIZB_BACK | PSWIZB_FINISH : PSWIZB_FINISH);
  986. // selection can change so need to do this everytime you come to this page
  987. _RefreshFinishPage(hDlg);
  988. break;
  989. case PSN_WIZFINISH:
  990. // process the items now
  991. _ProcessItems();
  992. break;
  993. }
  994. break;
  995. }
  996. }
  997. return ipRet;
  998. }
  999. //
  1000. // stub dialog proc which redirects calls to the right dialog procs
  1001. //
  1002. INT_PTR CALLBACK CCleanupWiz::s_StubDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
  1003. {
  1004. PDLGPROCINFO pInfo = (PDLGPROCINFO) GetWindowLongPtr(hDlg, DWLP_USER);
  1005. if (WM_INITDIALOG == wMsg)
  1006. {
  1007. pInfo = (PDLGPROCINFO) ((LPPROPSHEETPAGE) lParam) -> lParam;
  1008. SetWindowLongPtr(hDlg, DWLP_USER, (LPARAM) pInfo);
  1009. }
  1010. if (pInfo)
  1011. {
  1012. CCleanupWiz * pThis = pInfo->pcfc;
  1013. PCFC_DlgProcFn pfn = pInfo->pfnDlgProc;
  1014. return (pThis->*pfn)(hDlg, wMsg, wParam, lParam);
  1015. }
  1016. return FALSE;
  1017. }
  1018. STDMETHODIMP CCleanupWiz::_InitListBox(HWND hWndListView)
  1019. {
  1020. ListView_SetExtendedListViewStyle(hWndListView, LVS_EX_SUBITEMIMAGES);
  1021. //
  1022. // add the columns
  1023. //
  1024. LVCOLUMN lvcDate;
  1025. TCHAR szDateHeader[c_MAX_HEADER_LEN];
  1026. lvcDate.mask = LVCF_SUBITEM | LVCF_WIDTH | LVCF_TEXT ;
  1027. lvcDate.iSubItem = FC_COL_SHORTCUT;
  1028. lvcDate.cx = 200;
  1029. LoadString(g_hInst, IDS_HEADER_ITEM, szDateHeader, ARRAYSIZE(szDateHeader));
  1030. lvcDate.pszText = szDateHeader;
  1031. ListView_InsertColumn(hWndListView, FC_COL_SHORTCUT, &lvcDate);
  1032. lvcDate.mask = LVCF_SUBITEM | LVCF_FMT | LVCF_WIDTH | LVCF_TEXT ;
  1033. lvcDate.iSubItem = FC_COL_DATE;
  1034. lvcDate.fmt = LVCFMT_LEFT;
  1035. lvcDate.cx = 1;
  1036. LoadString(g_hInst, IDS_HEADER_DATE, szDateHeader, ARRAYSIZE(szDateHeader));
  1037. lvcDate.pszText = szDateHeader;
  1038. ListView_InsertColumn(hWndListView, FC_COL_DATE, &lvcDate);
  1039. ListView_SetColumnWidth(hWndListView, FC_COL_DATE, LVSCW_AUTOSIZE_USEHEADER);
  1040. return S_OK;
  1041. }
  1042. STDMETHODIMP CCleanupWiz::_InitChoosePage(HWND hDlg)
  1043. {
  1044. HWND hWndListView = GetDlgItem(hDlg, IDC_LV_PROMPT);
  1045. _InitListBox(hWndListView);
  1046. //
  1047. // add the images
  1048. //
  1049. HIMAGELIST hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
  1050. GetSystemMetrics(SM_CYSMICON),
  1051. ILC_MASK | ILC_COLOR32 , c_GROWBYSIZE, c_GROWBYSIZE);
  1052. int cItems = DSA_GetItemCount(_hdsaItems);
  1053. for (int i = 0; i < cItems; i++)
  1054. {
  1055. FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems, i);
  1056. if (pfid)
  1057. {
  1058. ImageList_AddIcon(hSmall, pfid->hIcon);
  1059. }
  1060. }
  1061. ListView_SetImageList(hWndListView, hSmall, LVSIL_SMALL);
  1062. //
  1063. // set the checkboxes style
  1064. //
  1065. ListView_SetExtendedListViewStyleEx(hWndListView, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
  1066. _PopulateListView(hWndListView);
  1067. return S_OK;
  1068. }
  1069. STDMETHODIMP CCleanupWiz::_InitFinishPage(HWND hDlg)
  1070. {
  1071. HWND hWnd = GetDlgItem(hDlg, IDC_TEXT_TITLE_WELCOME);
  1072. if (_hTitleFont)
  1073. {
  1074. SetWindowFont(hWnd, _hTitleFont, TRUE);
  1075. }
  1076. HIMAGELIST hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
  1077. GetSystemMetrics(SM_CYSMICON),
  1078. ILC_MASK | ILC_COLOR32, c_GROWBYSIZE, c_GROWBYSIZE);
  1079. int cItems = DSA_GetItemCount(_hdsaItems);
  1080. for (int i = 0; i < cItems; i++)
  1081. {
  1082. FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems, i);
  1083. if (pfid)
  1084. {
  1085. ImageList_AddIcon(hSmall, pfid->hIcon);
  1086. }
  1087. }
  1088. ListView_SetImageList(GetDlgItem(hDlg, IDC_LV_INFORM), hSmall, LVSIL_SMALL);
  1089. return S_OK;
  1090. }
  1091. STDMETHODIMP CCleanupWiz::_RefreshFinishPage(HWND hDlg)
  1092. {
  1093. HRESULT hr;
  1094. HWND hWndListView = GetDlgItem(hDlg, IDC_LV_INFORM);
  1095. ListView_DeleteAllItems(hWndListView);
  1096. int cMovedItems = _PopulateListViewFinish(hWndListView);
  1097. // set the informative text to reflect how many items were moved
  1098. HWND hWnd = GetDlgItem(hDlg, IDC_TEXT_INFORM);
  1099. TCHAR szDisplayText[c_MAX_PROMPT_TEXT];
  1100. ShowWindow(GetDlgItem(hDlg, IDC_LV_INFORM), BOOLIFY(cMovedItems));
  1101. ShowWindow(GetDlgItem(hDlg, IDC_TEXT_SHORTCUTS), BOOLIFY(cMovedItems));
  1102. ShowWindow(GetDlgItem(hDlg, IDC_TEXT_CHANGE), BOOLIFY(cMovedItems));
  1103. if ( 0 == cMovedItems)
  1104. {
  1105. LoadString(g_hInst, _cItemsOnDesktop ? IDS_INFORM_NONE : IDS_INFORM_NONEFOUND,
  1106. szDisplayText, ARRAYSIZE(szDisplayText));
  1107. hr = S_OK;
  1108. }
  1109. else if (1 == cMovedItems)
  1110. {
  1111. LoadString(g_hInst, IDS_INFORM_SINGLE,
  1112. szDisplayText, ARRAYSIZE(szDisplayText));
  1113. hr = S_OK;
  1114. }
  1115. else
  1116. {
  1117. TCHAR szRawText[c_MAX_PROMPT_TEXT];
  1118. LoadString(g_hInst, IDS_INFORM, szRawText, ARRAYSIZE(szRawText));
  1119. hr = StringCchPrintf(szDisplayText, ARRAYSIZE(szDisplayText), szRawText, cMovedItems);
  1120. }
  1121. SetWindowText(hWnd, szDisplayText);
  1122. return hr;
  1123. }
  1124. STDMETHODIMP_(int) CCleanupWiz::_PopulateListView(HWND hWndListView)
  1125. {
  1126. LVITEM lvi = {0};
  1127. int cRet = 0;
  1128. int cItems = DSA_GetItemCount(_hdsaItems);
  1129. for (int i = 0; i < cItems; i++)
  1130. {
  1131. FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems, i);
  1132. lvi.mask = LVIF_TEXT | LVIF_IMAGE;
  1133. lvi.pszText = pfid->pszName;
  1134. lvi.iImage = i;
  1135. lvi.iItem = i;
  1136. lvi.iSubItem = FC_COL_SHORTCUT;
  1137. ListView_InsertItem(hWndListView, &lvi);
  1138. cRet++;
  1139. // set the last used date
  1140. TCHAR szDate[c_MAX_DATE_LEN];
  1141. if (SUCCEEDED(_GetDateFromFileTime(pfid->ftLastUsed, szDate, ARRAYSIZE(szDate))))
  1142. {
  1143. ListView_SetItemText(hWndListView, i, FC_COL_DATE, szDate);
  1144. }
  1145. }
  1146. return cRet;
  1147. }
  1148. STDMETHODIMP_(int) CCleanupWiz::_PopulateListViewFinish(HWND hWndListView)
  1149. {
  1150. LVITEM lvi = {0};
  1151. lvi.mask = LVIF_TEXT | LVIF_IMAGE ;
  1152. int cRet = 0;
  1153. int cItems = DSA_GetItemCount(_hdsaItems);
  1154. for (int i = 0; i < cItems; i++)
  1155. {
  1156. FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems, i);
  1157. //
  1158. // it's the Finish Page, we only show the items we were asked to move
  1159. //
  1160. if (pfid && pfid->bSelected)
  1161. {
  1162. lvi.pszText = pfid->pszName;
  1163. lvi.iImage = i;
  1164. lvi.iItem = i;
  1165. ListView_InsertItem(hWndListView, &lvi);
  1166. cRet++;
  1167. }
  1168. }
  1169. return cRet;
  1170. }
  1171. //
  1172. // Converts a given FILETIME date into s displayable string
  1173. //
  1174. STDMETHODIMP CCleanupWiz::_GetDateFromFileTime(FILETIME ftLastUsed, LPTSTR pszDate, int cch )
  1175. {
  1176. HRESULT hr;
  1177. if (0 == ftLastUsed.dwHighDateTime && 0 == ftLastUsed.dwLowDateTime)
  1178. {
  1179. LoadString(g_hInst, IDS_NEVER, pszDate, cch);
  1180. hr = S_OK;
  1181. {
  1182. hr = E_FAIL;
  1183. }
  1184. }
  1185. else
  1186. {
  1187. DWORD dwFlags = FDTF_SHORTDATE;
  1188. if (0 == SHFormatDateTime(&ftLastUsed, &dwFlags, pszDate, cch))
  1189. {
  1190. hr = E_FAIL;
  1191. }
  1192. else
  1193. {
  1194. hr = S_OK;
  1195. }
  1196. }
  1197. return hr;
  1198. }
  1199. //
  1200. // Marks listview items as checked or unchecked
  1201. //
  1202. STDMETHODIMP CCleanupWiz::_SetCheckedState(HWND hWndListView)
  1203. {
  1204. int cItems = DSA_GetItemCount(_hdsaItems);
  1205. for (int i = 0; i < cItems; i++)
  1206. {
  1207. FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems, i);
  1208. if (pfid)
  1209. {
  1210. ListView_SetCheckState(hWndListView, i, pfid->bSelected);
  1211. }
  1212. }
  1213. return S_OK;
  1214. }
  1215. //
  1216. // Reverse of above, updates our list based on user selection.
  1217. //
  1218. STDMETHODIMP CCleanupWiz::_MarkSelectedItems(HWND hWndListView)
  1219. {
  1220. int cItems = ListView_GetItemCount(hWndListView);
  1221. for (int iLV = 0; iLV < cItems; iLV++)
  1222. {
  1223. FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems, iLV);
  1224. if (pfid)
  1225. {
  1226. pfid->bSelected = ListView_GetCheckState(hWndListView, iLV);
  1227. }
  1228. }
  1229. return S_OK;
  1230. }
  1231. //
  1232. // These methods clean up _hdsaItems and free the allocated memory
  1233. //
  1234. STDMETHODIMP_(void) CCleanupWiz::_CleanUpDSA()
  1235. {
  1236. if (_hdsaItems != NULL)
  1237. {
  1238. for (int i = DSA_GetItemCount(_hdsaItems)-1; i >= 0; i--)
  1239. {
  1240. FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems,i);
  1241. if (pfid)
  1242. {
  1243. _CleanUpDSAItem(pfid);
  1244. }
  1245. }
  1246. DSA_Destroy(_hdsaItems);
  1247. _hdsaItems = NULL;
  1248. }
  1249. }
  1250. STDMETHODIMP CCleanupWiz::_CleanUpDSAItem(FOLDERITEMDATA * pfid)
  1251. {
  1252. if (pfid->pidl)
  1253. {
  1254. ILFree(pfid->pidl);
  1255. }
  1256. if (pfid->pszName)
  1257. {
  1258. Str_SetPtr(&(pfid->pszName), NULL);
  1259. }
  1260. if (pfid->hIcon)
  1261. {
  1262. DestroyIcon(pfid->hIcon);
  1263. }
  1264. ZeroMemory(pfid, sizeof(*pfid));
  1265. return S_OK;
  1266. }
  1267. //////////////////////
  1268. //
  1269. // Hide regitems
  1270. //
  1271. //////////////////////
  1272. //
  1273. // Helper routines used below.
  1274. // Cloned from shell32/util.cpp
  1275. //
  1276. STDMETHODIMP GetItemCLSID(IShellFolder2 *psf, LPCITEMIDLIST pidlLast, CLSID *pclsid)
  1277. {
  1278. VARIANT var;
  1279. HRESULT hr = psf->GetDetailsEx(pidlLast, &SCID_DESCRIPTIONID, &var);
  1280. if (SUCCEEDED(hr))
  1281. {
  1282. SHDESCRIPTIONID did;
  1283. hr = VariantToBuffer(&var, (void *)&did, sizeof(did));
  1284. if (SUCCEEDED(hr))
  1285. *pclsid = did.clsid;
  1286. VariantClear(&var);
  1287. }
  1288. return hr;
  1289. }
  1290. //
  1291. // Given a regitem, it sets the SFGAO_NONENUMERATED bit on it so that
  1292. // that it no longer shows up in the folder.
  1293. //
  1294. // Since we are primarily only interested in cleaning up desktop clutter,
  1295. // that means we don't have to worry about all possible kinds of regitems.
  1296. // Our main target is apps like Outlook which create regitems instead of
  1297. // .lnk shortcuts. So our code does not have to be as complex as the
  1298. // regfldr.cpp version for deleting regitems, which has to account for
  1299. // everything, from legacy regitems to delegate folders.
  1300. //
  1301. //
  1302. STDMETHODIMP CCleanupWiz::_HideRegPidl(LPCITEMIDLIST pidlr, BOOL fHide)
  1303. {
  1304. IShellFolder2 *psf2;
  1305. HRESULT hr = _psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2));
  1306. if (SUCCEEDED(hr))
  1307. {
  1308. CLSID clsid;
  1309. hr = GetItemCLSID(psf2, pidlr, &clsid);
  1310. if (SUCCEEDED(hr))
  1311. {
  1312. hr = _HideRegItem(&clsid, fHide, NULL);
  1313. }
  1314. psf2->Release();
  1315. }
  1316. return hr;
  1317. }
  1318. STDMETHODIMP CCleanupWiz::_HideRegItem(CLSID* pclsid, BOOL fHide, BOOL* pfWasVisible)
  1319. {
  1320. HKEY hkey;
  1321. if (pfWasVisible)
  1322. {
  1323. *pfWasVisible = FALSE;
  1324. }
  1325. HRESULT hr = SHRegGetCLSIDKey(*pclsid, TEXT("ShellFolder"), FALSE, TRUE, &hkey);
  1326. if(SUCCEEDED(hr))
  1327. {
  1328. DWORD dwAttr, dwErr;
  1329. DWORD dwType = 0;
  1330. DWORD cbSize = sizeof(dwAttr);
  1331. if (ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("Attributes"), NULL, &dwType, (BYTE *) &dwAttr, &cbSize))
  1332. {
  1333. if (pfWasVisible)
  1334. {
  1335. *pfWasVisible = !(dwAttr & SFGAO_NONENUMERATED);
  1336. }
  1337. fHide ? dwAttr |= SFGAO_NONENUMERATED : dwAttr &= ~SFGAO_NONENUMERATED;
  1338. }
  1339. else
  1340. {
  1341. // attributes do not exist, so we will try creating them
  1342. fHide ? dwAttr = SFGAO_NONENUMERATED : dwAttr = 0;
  1343. }
  1344. dwErr = RegSetValueEx(hkey, TEXT("Attributes"), NULL, dwType, (BYTE *) &dwAttr, cbSize);
  1345. hr = HRESULT_FROM_WIN32(dwErr);
  1346. RegCloseKey(hkey);
  1347. }
  1348. return hr;
  1349. }
  1350. //
  1351. // Method writes out the last used time in the registry and the
  1352. // number of days it was checkin for
  1353. //
  1354. STDMETHODIMP CCleanupWiz::_LogUsage()
  1355. {
  1356. FILETIME ft;
  1357. SYSTEMTIME st;
  1358. GetLocalTime(&st);
  1359. SystemTimeToFileTime(&st, &ft);
  1360. //
  1361. // we ignore if any of these calls fail, as we cannot really do anything
  1362. // in that case. the next time we run, we will run maybe sooner that expected.
  1363. //
  1364. SHRegSetUSValue(REGSTR_PATH_CLEANUPWIZ, c_szVAL_TIME,
  1365. REG_BINARY, &ft, sizeof(ft),
  1366. SHREGSET_FORCE_HKCU);
  1367. SHRegSetUSValue(REGSTR_PATH_CLEANUPWIZ, c_szVAL_DELTA_DAYS,
  1368. REG_DWORD,(DWORD *) &_iDeltaDays, sizeof(_iDeltaDays),
  1369. SHREGSET_FORCE_HKCU);
  1370. //
  1371. // TODO: also write out to log file here
  1372. //
  1373. return S_OK;
  1374. }
  1375. //
  1376. // returns the current value from the policy key or the user settings
  1377. //
  1378. STDMETHODIMP_(int) CCleanupWiz::GetNumDaysBetweenCleanup()
  1379. {
  1380. DWORD dwData;
  1381. DWORD dwType;
  1382. DWORD cch = sizeof (DWORD);
  1383. int iDays = -1; // if the value does not exist
  1384. //
  1385. // ISSUE-2000/12/01-AIDANL Removed REGSTR_POLICY_CLEANUP, don't think we need both, but want to
  1386. // leave this note in case issues come up later.
  1387. //
  1388. if (ERROR_SUCCESS == (SHRegGetUSValue(REGSTR_PATH_CLEANUPWIZ, c_szVAL_DELTA_DAYS,
  1389. &dwType, &dwData, &cch,FALSE, NULL, 0)))
  1390. {
  1391. iDays = dwData;
  1392. }
  1393. return iDays;
  1394. }
  1395. // helper functions
  1396. STDAPI_(BOOL) IsUserAGuest()
  1397. {
  1398. return SHTestTokenMembership(NULL, DOMAIN_ALIAS_RID_GUESTS);
  1399. }