Source code of Windows XP (NT5)
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.

1555 lines
51 KiB

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