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.

1317 lines
36 KiB

  1. #include "common.h"
  2. #include "dataguid.h"
  3. #include "compguid.h"
  4. #include <emptyvc.h>
  5. #include "dataclen.h"
  6. #include "compclen.h"
  7. #include <regstr.h>
  8. #include <olectl.h>
  9. #include <tlhelp32.h>
  10. #define DECL_CRTFREE
  11. #include <crtfree.h>
  12. #ifndef RESOURCE_H
  13. #include "resource.h"
  14. #endif
  15. #include <winsvc.h>
  16. #include <shlwapi.h>
  17. #include <shlwapip.h>
  18. #include <advpub.h>
  19. HINSTANCE g_hDllModule = NULL; // Handle to this DLL itself.
  20. LONG g_cDllObjects = 0; // Count number of existing objects
  21. STDAPI_(int) LibMain(HINSTANCE hInstance, DWORD fdwReason, LPVOID pvRes)
  22. {
  23. switch (fdwReason)
  24. {
  25. case DLL_PROCESS_ATTACH:
  26. g_hDllModule = hInstance;
  27. break;;
  28. }
  29. return TRUE;
  30. }
  31. STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
  32. {
  33. DWORD dw;
  34. HRESULT hr;
  35. *ppv = NULL;
  36. //
  37. // Is the request for one of our cleaner objects?
  38. //
  39. if (IsEqualCLSID(rclsid, CLSID_DataDrivenCleaner))
  40. {
  41. dw = ID_SYSTEMDATACLEANER;
  42. }
  43. else if (IsEqualCLSID(rclsid, CLSID_ContentIndexerCleaner))
  44. {
  45. dw = ID_CONTENTINDEXCLEANER;
  46. }
  47. else if (IsEqualCLSID(rclsid, CLSID_CompCleaner))
  48. {
  49. dw = ID_COMPCLEANER;
  50. }
  51. else if (IsEqualCLSID(rclsid, CLSID_OldFilesInRootPropBag))
  52. {
  53. dw = ID_OLDFILESINROOTPROPBAG;
  54. }
  55. else if (IsEqualCLSID(rclsid, CLSID_TempFilesPropBag))
  56. {
  57. dw = ID_TEMPFILESPROPBAG;
  58. }
  59. else if (IsEqualCLSID(rclsid, CLSID_SetupFilesPropBag))
  60. {
  61. dw = ID_SETUPFILESPROPBAG;
  62. }
  63. else if (IsEqualCLSID(rclsid, CLSID_UninstalledFilesPropBag))
  64. {
  65. dw = ID_UNINSTALLEDFILESPROPBAG;
  66. }
  67. else if (IsEqualCLSID(rclsid, CLSID_IndexCleanerPropBag))
  68. {
  69. dw = ID_INDEXCLEANERPROPBAG;
  70. }
  71. else
  72. {
  73. return CLASS_E_CLASSNOTAVAILABLE;
  74. }
  75. if (ID_COMPCLEANER == dw)
  76. {
  77. CCompCleanerClassFactory *pcf = new CCompCleanerClassFactory();
  78. if (pcf)
  79. {
  80. hr = pcf->QueryInterface(riid, ppv);
  81. pcf->Release();
  82. }
  83. else
  84. {
  85. hr = E_OUTOFMEMORY;
  86. }
  87. }
  88. else
  89. {
  90. CCleanerClassFactory * pcf = new CCleanerClassFactory(dw);
  91. if (pcf)
  92. {
  93. hr = pcf->QueryInterface(riid, ppv);
  94. pcf->Release();
  95. }
  96. else
  97. {
  98. hr = E_OUTOFMEMORY;
  99. }
  100. }
  101. return hr;
  102. }
  103. STDAPI DllCanUnloadNow(void)
  104. {
  105. return (0 == g_cDllObjects) ? S_OK : S_FALSE;
  106. }
  107. HRESULT CallRegInstall(LPCSTR szSection)
  108. {
  109. HRESULT hr = E_FAIL;
  110. HINSTANCE hinstAdvPack = LoadLibrary(TEXT("ADVPACK.DLL"));
  111. if (hinstAdvPack)
  112. {
  113. REGINSTALL pfnri = (REGINSTALL)GetProcAddress(hinstAdvPack, "RegInstall");
  114. if (pfnri)
  115. {
  116. STRENTRY seReg[] = {
  117. { "25", "%SystemRoot%" },
  118. { "11", "%SystemRoot%\\system32" },
  119. };
  120. STRTABLE stReg = { ARRAYSIZE(seReg), seReg };
  121. hr = pfnri(g_hDllModule, szSection, &stReg);
  122. }
  123. // since we only do this from DllInstall() don't load and unload advpack over and over
  124. // FreeLibrary(hinstAdvPack);
  125. }
  126. return hr;
  127. }
  128. STDAPI DllRegisterServer()
  129. {
  130. return CallRegInstall("RegDll");
  131. }
  132. STDAPI DllUnregisterServer()
  133. {
  134. return CallRegInstall("UnregDll");
  135. }
  136. UINT incDllObjectCount(void)
  137. {
  138. return InterlockedIncrement(&g_cDllObjects);
  139. }
  140. UINT decDllObjectCount(void)
  141. {
  142. return InterlockedDecrement(&g_cDllObjects);
  143. }
  144. CCleanerClassFactory::CCleanerClassFactory(DWORD dw) : _cRef(1), _dwID(dw)
  145. {
  146. incDllObjectCount();
  147. }
  148. CCleanerClassFactory::~CCleanerClassFactory()
  149. {
  150. decDllObjectCount();
  151. }
  152. STDMETHODIMP CCleanerClassFactory::QueryInterface(REFIID riid, void **ppv)
  153. {
  154. *ppv = NULL;
  155. if (IsEqualIID(riid, IID_IUnknown) ||
  156. IsEqualIID(riid, IID_IClassFactory))
  157. {
  158. *ppv = (IClassFactory *)this;
  159. AddRef();
  160. return S_OK;
  161. }
  162. return E_NOINTERFACE;
  163. }
  164. STDMETHODIMP_(ULONG) CCleanerClassFactory::AddRef()
  165. {
  166. return ++_cRef;
  167. }
  168. STDMETHODIMP_(ULONG) CCleanerClassFactory::Release()
  169. {
  170. if (--_cRef)
  171. return _cRef;
  172. delete this;
  173. return 0;
  174. }
  175. STDMETHODIMP CCleanerClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv)
  176. {
  177. HRESULT hr = E_NOINTERFACE;
  178. *ppv = NULL;
  179. if (pUnkOuter)
  180. {
  181. return CLASS_E_NOAGGREGATION;
  182. }
  183. if (_dwID == ID_CONTENTINDEXCLEANER)
  184. {
  185. CContentIndexCleaner *pContentIndexCleaner = new CContentIndexCleaner();
  186. if (pContentIndexCleaner)
  187. {
  188. hr = pContentIndexCleaner->QueryInterface(riid, ppv);
  189. pContentIndexCleaner->Release();
  190. }
  191. else
  192. {
  193. hr = E_OUTOFMEMORY;
  194. }
  195. }
  196. else if (IsEqualIID(riid, IID_IEmptyVolumeCache))
  197. {
  198. CDataDrivenCleaner *pDataDrivenCleaner = new CDataDrivenCleaner();
  199. if (pDataDrivenCleaner)
  200. {
  201. hr = pDataDrivenCleaner->QueryInterface(riid, ppv);
  202. pDataDrivenCleaner->Release();
  203. }
  204. else
  205. {
  206. hr = E_OUTOFMEMORY;
  207. }
  208. }
  209. else if (IsEqualIID(riid, IID_IPropertyBag))
  210. {
  211. CDataDrivenPropBag *pDataDrivenPropBag = new CDataDrivenPropBag(_dwID);
  212. if (pDataDrivenPropBag)
  213. {
  214. hr = pDataDrivenPropBag->QueryInterface(riid, ppv);
  215. pDataDrivenPropBag->Release();
  216. }
  217. else
  218. {
  219. hr = E_OUTOFMEMORY;
  220. }
  221. }
  222. else
  223. {
  224. MiDebugMsg((0, TEXT("CDataDrivenCleanerClassFactory::CreateInstance called for unknown riid (%d)"), (_dwID)));
  225. }
  226. return hr;
  227. }
  228. STDMETHODIMP CCleanerClassFactory::LockServer(BOOL fLock)
  229. {
  230. if (fLock)
  231. incDllObjectCount();
  232. else
  233. decDllObjectCount();
  234. return S_OK;
  235. }
  236. CDataDrivenCleaner::CDataDrivenCleaner() : _cRef(1)
  237. {
  238. _cbSpaceUsed.QuadPart = 0;
  239. _cbSpaceFreed.QuadPart = 0;
  240. _szVolume[0] = 0;
  241. _szFolder[0] = 0;
  242. _filelist[0] = 0;
  243. _dwFlags = 0;
  244. _head = NULL;
  245. incDllObjectCount();
  246. }
  247. CDataDrivenCleaner::~CDataDrivenCleaner()
  248. {
  249. FreeList(_head);
  250. _head = NULL;
  251. decDllObjectCount();
  252. }
  253. STDMETHODIMP CDataDrivenCleaner::QueryInterface(REFIID riid, void **ppv)
  254. {
  255. *ppv = NULL;
  256. if (IsEqualIID(riid, IID_IUnknown) ||
  257. IsEqualIID(riid, IID_IEmptyVolumeCache))
  258. {
  259. *ppv = (IEmptyVolumeCache*) this;
  260. AddRef();
  261. return S_OK;
  262. }
  263. return E_NOINTERFACE;
  264. }
  265. STDMETHODIMP_(ULONG) CDataDrivenCleaner::AddRef()
  266. {
  267. return ++_cRef;
  268. }
  269. STDMETHODIMP_(ULONG) CDataDrivenCleaner::Release()
  270. {
  271. if (--_cRef)
  272. return _cRef;
  273. delete this;
  274. return 0;
  275. }
  276. // Initializes the System Data Driven Cleaner and returns the
  277. // specified IEmptyVolumeCache flags to the cache manager.
  278. STDMETHODIMP CDataDrivenCleaner::Initialize(HKEY hRegKey, LPCWSTR pszVolume,
  279. LPWSTR *ppszDisplayName, LPWSTR *ppszDescription, DWORD *pdwFlags)
  280. {
  281. TCHAR szTempFolder[MAX_PATH];
  282. ULONG DaysLastAccessed = 0;
  283. PTSTR pTemp;
  284. BOOL bFolderOnVolume;
  285. _bPurged = FALSE;
  286. *ppszDisplayName = NULL; // cleanmgr.exe will get these values from
  287. *ppszDescription = NULL;
  288. _ftMinLastAccessTime.dwLowDateTime = 0;
  289. _ftMinLastAccessTime.dwHighDateTime = 0;
  290. if (*pdwFlags & EVCF_SETTINGSMODE)
  291. {
  292. return S_OK;
  293. }
  294. _szFolder[0] = 0;
  295. _dwFlags = 0;
  296. _filelist[0] = 0;
  297. _szCleanupCmdLine[0] = 0;
  298. if (hRegKey)
  299. {
  300. DWORD dwType, cbData;
  301. DWORD dwCSIDL;
  302. cbData = sizeof(dwCSIDL);
  303. if (ERROR_SUCCESS == RegQueryValueEx(hRegKey, REGSTR_VAL_CSIDL, NULL, &dwType, (LPBYTE)&dwCSIDL, &cbData))
  304. {
  305. // CSIDL=<hex CSIDL_ value>
  306. SHGetFolderPath(NULL, dwCSIDL, NULL, 0, _szFolder);
  307. if (_szFolder[0])
  308. {
  309. TCHAR szRelPath[MAX_PATH];
  310. cbData = sizeof(szRelPath);
  311. if (ERROR_SUCCESS == RegQueryValueEx(hRegKey, REGSTR_VAL_FOLDER, NULL, &dwType, (LPBYTE)szRelPath, &cbData))
  312. {
  313. // optionally append "folder" as a relative path
  314. PathAppend(_szFolder, szRelPath);
  315. }
  316. }
  317. }
  318. if (0 == _szFolder[0])
  319. {
  320. // still nothing, try "folder"=<path1>|<path2>
  321. cbData = sizeof(_szFolder);
  322. if (ERROR_SUCCESS == RegQueryValueEx(hRegKey, REGSTR_VAL_FOLDER, NULL, &dwType, (LPBYTE)_szFolder, &cbData))
  323. {
  324. if (REG_SZ == dwType)
  325. {
  326. // REG_SZ that needs to be converted to a MULTI_SZ
  327. //
  328. // paths separated by '|' like ?:\foo|?:\bar
  329. for (pTemp = _szFolder; *pTemp; pTemp++)
  330. {
  331. if (*pTemp == TEXT('|'))
  332. {
  333. *pTemp++ = NULL;
  334. }
  335. }
  336. // double NULL terminated
  337. pTemp++;
  338. *pTemp = 0;
  339. }
  340. else if (REG_EXPAND_SZ == dwType)
  341. {
  342. // single folder with environment expantion
  343. if (SHExpandEnvironmentStrings(_szFolder, szTempFolder, (ARRAYSIZE(szTempFolder) - 1))) // leave extra space for double NULL
  344. {
  345. lstrcpy(_szFolder, szTempFolder);
  346. }
  347. _szFolder[lstrlen(_szFolder) + 1] = 0; // double NULL terminated.
  348. }
  349. else if (REG_MULTI_SZ == dwType)
  350. {
  351. // nothing else to do, we're done
  352. }
  353. else
  354. {
  355. // invalid data
  356. _szFolder[0] = NULL;
  357. }
  358. }
  359. }
  360. cbData = sizeof(_dwFlags);
  361. RegQueryValueEx(hRegKey, REGSTR_VAL_FLAGS, NULL, &dwType, (LPBYTE)&_dwFlags, &cbData);
  362. cbData = sizeof(_filelist);
  363. RegQueryValueEx(hRegKey, REGSTR_VAL_FILELIST, NULL, &dwType, (LPBYTE)_filelist, &cbData);
  364. cbData = sizeof(DaysLastAccessed);
  365. RegQueryValueEx(hRegKey, REGSTR_VAL_LASTACCESS, NULL, &dwType, (LPBYTE)&DaysLastAccessed, &cbData);
  366. cbData = sizeof(_szCleanupCmdLine);
  367. RegQueryValueEx(hRegKey, REGSTR_VAL_CLEANUPSTRING, NULL, &dwType, (LPBYTE)_szCleanupCmdLine, &cbData);
  368. }
  369. // If the DDEVCF_RUNIFOUTOFDISKSPACE bit is set then make sure the EVCF_OUTOFDISKSPACE flag
  370. // was passed in. If it was not then return S_FALSE so we won't run.
  371. if ((_dwFlags & DDEVCF_RUNIFOUTOFDISKSPACE) &&
  372. (!(*pdwFlags & EVCF_OUTOFDISKSPACE)))
  373. {
  374. return S_FALSE;
  375. }
  376. lstrcpy(_szVolume, pszVolume);
  377. // Fix up the filelist. The file list can either be a MULTI_SZ list of files or
  378. // a list of files separated by the ':' colon character or a '|' bar character.
  379. // These characters were choosen because they are invalid filename characters.
  380. for (pTemp = _filelist; *pTemp; pTemp++)
  381. {
  382. if (*pTemp == TEXT(':') || *pTemp == TEXT('|'))
  383. {
  384. *pTemp++ = 0;
  385. }
  386. }
  387. pTemp++; // double null terminate
  388. *pTemp = 0;
  389. bFolderOnVolume = FALSE;
  390. if (_szFolder[0] == 0)
  391. {
  392. // If no folder value is given so use the current volume
  393. lstrcpy(_szFolder, pszVolume);
  394. bFolderOnVolume = TRUE;
  395. }
  396. else
  397. {
  398. // A valid folder value was given, loop over each folder to check for "?" and ensure that
  399. // we are on a drive that contains some of the specified folders
  400. for (LPTSTR pszFolder = _szFolder; *pszFolder; pszFolder += lstrlen(pszFolder) + 1)
  401. {
  402. // Replace the first character of each folder (driver letter) if it is a '?'
  403. // with the current volume.
  404. if (*pszFolder == TEXT('?'))
  405. {
  406. *pszFolder = *pszVolume;
  407. bFolderOnVolume = TRUE;
  408. }
  409. // If there is a valid "folder" value in the registry make sure that it is
  410. // on the specified volume. If it is not then return S_FALSE so that we are
  411. // not displayed on the list of items that can be freed.
  412. if (!bFolderOnVolume)
  413. {
  414. lstrcpy(szTempFolder, pszFolder);
  415. szTempFolder[lstrlen(pszVolume)] = 0;
  416. if (lstrcmpi(pszVolume, szTempFolder) == 0)
  417. {
  418. bFolderOnVolume = TRUE;
  419. }
  420. }
  421. }
  422. }
  423. if (bFolderOnVolume == FALSE)
  424. {
  425. return S_FALSE; //Don't display us in the list
  426. }
  427. //
  428. // Determine the LastAccessedTime
  429. //
  430. if (DaysLastAccessed)
  431. {
  432. ULARGE_INTEGER ulTemp, ulLastAccessTime;
  433. //Determine the number of days in 100ns units
  434. ulTemp.LowPart = FILETIME_HOUR_LOW;
  435. ulTemp.HighPart = FILETIME_HOUR_HIGH;
  436. ulTemp.QuadPart *= DaysLastAccessed;
  437. //Get the current FILETIME
  438. SYSTEMTIME st;
  439. GetSystemTime(&st);
  440. FILETIME ft;
  441. SystemTimeToFileTime(&st, &ft);
  442. ulLastAccessTime.LowPart = ft.dwLowDateTime;
  443. ulLastAccessTime.HighPart = ft.dwHighDateTime;
  444. //Subtract the Last Access number of days (in 100ns units) from
  445. //the current system time.
  446. ulLastAccessTime.QuadPart -= ulTemp.QuadPart;
  447. //Save this minimal Last Access time in the FILETIME member variable
  448. //ftMinLastAccessTime.
  449. _ftMinLastAccessTime.dwLowDateTime = ulLastAccessTime.LowPart;
  450. _ftMinLastAccessTime.dwHighDateTime = ulLastAccessTime.HighPart;
  451. _dwFlags |= DDEVCF_PRIVATE_LASTACCESS;
  452. }
  453. *pdwFlags = 0; // disable this item by default
  454. if (_dwFlags & DDEVCF_DONTSHOWIFZERO)
  455. *pdwFlags |= EVCF_DONTSHOWIFZERO;
  456. return S_OK;
  457. }
  458. // Returns the total amount of space that the data driven cleaner can remove.
  459. STDMETHODIMP CDataDrivenCleaner::GetSpaceUsed(DWORDLONG *pdwSpaceUsed, IEmptyVolumeCacheCallBack *picb)
  460. {
  461. _cbSpaceUsed.QuadPart = 0;
  462. //
  463. // Walk all of the folders in the folders list scanning for disk space.
  464. //
  465. for (LPTSTR pszFolder = _szFolder; *pszFolder; pszFolder += lstrlen(pszFolder) + 1)
  466. WalkForUsedSpace(pszFolder, picb);
  467. picb->ScanProgress(_cbSpaceUsed.QuadPart, EVCCBF_LASTNOTIFICATION, NULL);
  468. *pdwSpaceUsed = _cbSpaceUsed.QuadPart;
  469. return S_OK;
  470. }
  471. // Purges (deletes) all of the files specified in the "filelist" portion of the registry.
  472. STDMETHODIMP CDataDrivenCleaner::Purge(DWORDLONG dwSpaceToFree, IEmptyVolumeCacheCallBack *picb)
  473. {
  474. _bPurged = TRUE;
  475. //
  476. //Delete the files
  477. //
  478. PurgeFiles(picb, FALSE);
  479. PurgeFiles(picb, TRUE);
  480. //
  481. //Send the last notification to the cleanup manager
  482. //
  483. picb->PurgeProgress(_cbSpaceFreed.QuadPart, (_cbSpaceUsed.QuadPart - _cbSpaceFreed.QuadPart),
  484. EVCCBF_LASTNOTIFICATION, NULL);
  485. //
  486. //Free the list of files
  487. //
  488. FreeList(_head);
  489. _head = NULL;
  490. //
  491. //Run the "CleanupString" command line if one was provided
  492. //
  493. if (*_szCleanupCmdLine)
  494. {
  495. STARTUPINFO si = {0};
  496. PROCESS_INFORMATION pi = {0};
  497. si.cb = sizeof(si);
  498. if (CreateProcess(NULL, _szCleanupCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
  499. {
  500. CloseHandle(pi.hThread);
  501. CloseHandle(pi.hProcess);
  502. }
  503. else
  504. {
  505. MiDebugMsg((0, TEXT("CreateProcess(%s) failed with error %d"), _szCleanupCmdLine, GetLastError()));
  506. }
  507. }
  508. return S_OK;
  509. }
  510. STDMETHODIMP CDataDrivenCleaner::ShowProperties(HWND hwnd)
  511. {
  512. return S_OK;
  513. }
  514. // Deactivates the System Driven Data cleaner...this basically does nothing.
  515. STDMETHODIMP CDataDrivenCleaner::Deactivate(DWORD *pdwFlags)
  516. {
  517. *pdwFlags = 0;
  518. //
  519. //See if this object should be removed.
  520. //Note that we will only remove a cleaner if it's Purge() method was run.
  521. //
  522. if (_bPurged && (_dwFlags & DDEVCF_REMOVEAFTERCLEAN))
  523. *pdwFlags |= EVCF_REMOVEFROMLIST;
  524. return S_OK;
  525. }
  526. /*
  527. ** checks if the file is a specified number of days
  528. ** old (if the "lastaccess" DWORD is in the registry for this cleaner).
  529. ** If the file has not been accessed in the specified number of days
  530. ** then it can safely be deleted. If the file has been accessed in
  531. ** that number of days then the file will not be deleted.
  532. */
  533. BOOL CDataDrivenCleaner::LastAccessisOK(FILETIME ftFileLastAccess)
  534. {
  535. if (_dwFlags & DDEVCF_PRIVATE_LASTACCESS)
  536. {
  537. //Is the last access FILETIME for this file less than the current
  538. //FILETIME minus the number of specified days?
  539. return (CompareFileTime(&ftFileLastAccess, &_ftMinLastAccessTime) == -1);
  540. }
  541. return TRUE;
  542. }
  543. // checks if a file is open by doing a CreateFile
  544. // with fdwShareMode of 0. If GetLastError() retuns
  545. // ERROR_SHARING_VIOLATION then this function retuns TRUE because
  546. // someone has the file open. Otherwise this function retuns false.
  547. BOOL TestFileIsOpen(LPCTSTR lpFile, FILETIME *pftFileLastAccess)
  548. {
  549. #if 0
  550. // too slow, disable this
  551. HANDLE hFile = CreateFile(lpFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
  552. FILE_ATTRIBUTE_NORMAL, NULL);
  553. if ((hFile == INVALID_HANDLE_VALUE) &&
  554. (GetLastError() == ERROR_SHARING_VIOLATION))
  555. {
  556. return TRUE; //File is currently open by someone
  557. }
  558. //
  559. // File is not currently open
  560. //
  561. SetFileTime(hFile, NULL, pftFileLastAccess, NULL);
  562. CloseHandle(hFile);
  563. #endif
  564. return FALSE;
  565. }
  566. // recursively walk the specified directory and
  567. // add all the files under this directory to the delete list.
  568. BOOL CDataDrivenCleaner::WalkAllFiles(LPCTSTR lpPath, IEmptyVolumeCacheCallBack *picb)
  569. {
  570. BOOL bRet = TRUE;
  571. BOOL bFind = TRUE;
  572. HANDLE hFind;
  573. WIN32_FIND_DATA wd;
  574. TCHAR szFindPath[MAX_PATH];
  575. TCHAR szAddFile[MAX_PATH];
  576. ULARGE_INTEGER dwFileSize;
  577. static DWORD dwCount = 0;
  578. //
  579. //If this is a directory then tack a *.* onto the end of the path
  580. //and recurse through the rest of the directories
  581. //
  582. DWORD dwAttributes = GetFileAttributes(lpPath);
  583. if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
  584. {
  585. if (PathCombine(szFindPath, lpPath, TEXT("*.*")))
  586. {
  587. bFind = TRUE;
  588. hFind = FindFirstFile(szFindPath, &wd);
  589. while (hFind != INVALID_HANDLE_VALUE && bFind)
  590. {
  591. //
  592. //First check if the attributes of this file are OK for us to delete.
  593. //
  594. if (((!(wd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) ||
  595. (_dwFlags & DDEVCF_REMOVEREADONLY)) &&
  596. ((!(wd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)) ||
  597. (_dwFlags & DDEVCF_REMOVESYSTEM)) &&
  598. ((!(wd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) ||
  599. (_dwFlags & DDEVCF_REMOVEHIDDEN)))
  600. {
  601. if (PathCombine(szAddFile, lpPath, wd.cFileName))
  602. {
  603. //
  604. //This is a file so check if it is open
  605. //
  606. if ((!(wd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) &&
  607. (TestFileIsOpen(szAddFile, &wd.ftLastAccessTime) == FALSE) &&
  608. (LastAccessisOK(wd.ftLastAccessTime)))
  609. {
  610. //
  611. //File is not open so add it to the list
  612. //
  613. dwFileSize.HighPart = wd.nFileSizeHigh;
  614. dwFileSize.LowPart = wd.nFileSizeLow;
  615. AddFileToList(szAddFile, dwFileSize, FALSE);
  616. }
  617. }
  618. //
  619. //CallBack the cleanup Manager to update the UI
  620. //
  621. if ((dwCount++ % 10) == 0)
  622. {
  623. if (picb->ScanProgress(_cbSpaceUsed.QuadPart, 0, NULL) == E_ABORT)
  624. {
  625. //
  626. //User aborted
  627. //
  628. FindClose(hFind);
  629. return FALSE;
  630. }
  631. }
  632. }
  633. bFind = FindNextFile(hFind, &wd);
  634. }
  635. FindClose(hFind);
  636. }
  637. //
  638. //Recurse through all of the directories
  639. //
  640. if (PathCombine(szFindPath, lpPath, TEXT("*.*")))
  641. {
  642. bFind = TRUE;
  643. hFind = FindFirstFile(szFindPath, &wd);
  644. while (hFind != INVALID_HANDLE_VALUE && bFind)
  645. {
  646. //
  647. //This is a directory
  648. //
  649. if ((wd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  650. (lstrcmp(wd.cFileName, TEXT(".")) != 0) &&
  651. (lstrcmp(wd.cFileName, TEXT("..")) != 0))
  652. {
  653. if (PathCombine(szAddFile, lpPath, wd.cFileName))
  654. {
  655. dwFileSize.QuadPart = 0;
  656. AddFileToList(szAddFile, dwFileSize, TRUE);
  657. if (WalkAllFiles(szAddFile, picb) == FALSE)
  658. {
  659. //
  660. //User cancled
  661. //
  662. FindClose(hFind);
  663. return FALSE;
  664. }
  665. }
  666. }
  667. bFind = FindNextFile(hFind, &wd);
  668. }
  669. FindClose(hFind);
  670. }
  671. }
  672. return bRet;
  673. }
  674. // walk the specified directory and create a
  675. // linked list of files that can be deleted. It will also
  676. // increment the member variable to indicate how much disk space
  677. // these files are taking.
  678. // It will look at the dwFlags member variable to determine if it
  679. // needs to recursively walk the tree or not.
  680. BOOL CDataDrivenCleaner::WalkForUsedSpace(LPCTSTR lpPath, IEmptyVolumeCacheCallBack *picb)
  681. {
  682. BOOL bRet = TRUE;
  683. BOOL bFind = TRUE;
  684. HANDLE hFind;
  685. WIN32_FIND_DATA wd;
  686. TCHAR szFindPath[MAX_PATH];
  687. TCHAR szAddFile[MAX_PATH];
  688. ULARGE_INTEGER dwFileSize;
  689. static DWORD dwCount = 0;
  690. LPTSTR lpSingleFile;
  691. //
  692. //If this is a directory then tack a *.* onto the end of the path
  693. //and recurse through the rest of the directories
  694. //
  695. DWORD dwAttributes = GetFileAttributes(lpPath);
  696. if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
  697. {
  698. //
  699. //Enum through the MULTI_SZ filelist
  700. //
  701. for (lpSingleFile = _filelist; *lpSingleFile; lpSingleFile += lstrlen(lpSingleFile) + 1)
  702. {
  703. lstrcpy(szFindPath, lpPath);
  704. PathAppend(szFindPath, lpSingleFile);
  705. bFind = TRUE;
  706. hFind = FindFirstFile(szFindPath, &wd);
  707. while (hFind != INVALID_HANDLE_VALUE && bFind)
  708. {
  709. if (StrCmp(wd.cFileName, TEXT(".")) == 0 || StrCmp(wd.cFileName, TEXT("..")) == 0)
  710. {
  711. // ignore these two, otherwise we'll cover the whole disk..
  712. bFind = FindNextFile(hFind, &wd);
  713. continue;
  714. }
  715. //
  716. //First check if the attributes of this file are OK for us to delete.
  717. //
  718. if (((!(wd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) ||
  719. (_dwFlags & DDEVCF_REMOVEREADONLY)) &&
  720. ((!(wd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)) ||
  721. (_dwFlags & DDEVCF_REMOVESYSTEM)) &&
  722. ((!(wd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) ||
  723. (_dwFlags & DDEVCF_REMOVEHIDDEN)))
  724. {
  725. lstrcpy(szAddFile, lpPath);
  726. PathAppend(szAddFile, wd.cFileName);
  727. //
  728. //Check if this is a subdirectory
  729. //
  730. if (wd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  731. {
  732. if (_dwFlags & DDEVCF_REMOVEDIRS)
  733. {
  734. dwFileSize.QuadPart = 0;
  735. AddFileToList(szAddFile, dwFileSize, TRUE);
  736. if (WalkAllFiles(szAddFile, picb) == FALSE)
  737. {
  738. //
  739. //User cancled
  740. //
  741. FindClose(hFind);
  742. return FALSE;
  743. }
  744. }
  745. }
  746. //
  747. //This is a file so check if it is open
  748. //
  749. else if ((TestFileIsOpen(szAddFile, &wd.ftLastAccessTime) == FALSE) &&
  750. (LastAccessisOK(wd.ftLastAccessTime)))
  751. {
  752. //
  753. //File is not open so add it to the list
  754. //
  755. dwFileSize.HighPart = wd.nFileSizeHigh;
  756. dwFileSize.LowPart = wd.nFileSizeLow;
  757. AddFileToList(szAddFile, dwFileSize, FALSE);
  758. }
  759. //
  760. //CallBack the cleanup Manager to update the UI
  761. //
  762. if ((dwCount++ % 10) == 0)
  763. {
  764. if (picb->ScanProgress(_cbSpaceUsed.QuadPart, 0, NULL) == E_ABORT)
  765. {
  766. //
  767. //User aborted
  768. //
  769. FindClose(hFind);
  770. return FALSE;
  771. }
  772. }
  773. }
  774. bFind = FindNextFile(hFind, &wd);
  775. }
  776. FindClose(hFind);
  777. }
  778. if (_dwFlags & DDEVCF_DOSUBDIRS)
  779. {
  780. //
  781. //Recurse through all of the directories
  782. //
  783. lstrcpy(szFindPath, lpPath);
  784. PathAppend(szFindPath, TEXT("*.*"));
  785. bFind = TRUE;
  786. hFind = FindFirstFile(szFindPath, &wd);
  787. while (hFind != INVALID_HANDLE_VALUE && bFind)
  788. {
  789. //
  790. //This is a directory
  791. //
  792. if ((wd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  793. (lstrcmp(wd.cFileName, TEXT(".")) != 0) &&
  794. (lstrcmp(wd.cFileName, TEXT("..")) != 0))
  795. {
  796. lstrcpy(szAddFile, lpPath);
  797. PathAppend(szAddFile, wd.cFileName);
  798. if (WalkForUsedSpace(szAddFile, picb) == FALSE)
  799. {
  800. //
  801. //User cancled
  802. //
  803. FindClose(hFind);
  804. return FALSE;
  805. }
  806. }
  807. bFind = FindNextFile(hFind, &wd);
  808. }
  809. FindClose(hFind);
  810. }
  811. if (_dwFlags & DDEVCF_REMOVEPARENTDIR)
  812. {
  813. // add the parent directory to the list if we are told to remove them....
  814. dwFileSize.QuadPart = 0;
  815. AddFileToList(lpPath, dwFileSize, TRUE);
  816. }
  817. }
  818. else
  819. {
  820. MiDebugMsg((0, TEXT("CDataDrivenCleaner::WalkForUsedSpace -> %s is NOT a directory!"),
  821. lpPath));
  822. }
  823. return bRet;
  824. }
  825. // Adds a file to the linked list of files.
  826. BOOL CDataDrivenCleaner::AddFileToList(LPCTSTR lpFile, ULARGE_INTEGER filesize, BOOL bDirectory)
  827. {
  828. BOOL bRet = TRUE;
  829. CLEANFILESTRUCT *pNew = (CLEANFILESTRUCT *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pNew));
  830. if (pNew == NULL)
  831. {
  832. MiDebugMsg((0, TEXT("CDataDrivenCleaner::AddFileToList -> ERROR HeapAlloc() failed with error %d"),
  833. GetLastError()));
  834. return FALSE;
  835. }
  836. lstrcpy(pNew->file, lpFile);
  837. pNew->ulFileSize.QuadPart = filesize.QuadPart;
  838. pNew->bSelected = TRUE;
  839. pNew->bDirectory = bDirectory;
  840. if (_head)
  841. pNew->pNext = _head;
  842. else
  843. pNew->pNext = NULL;
  844. _head = pNew;
  845. _cbSpaceUsed.QuadPart += filesize.QuadPart;
  846. return bRet;
  847. }
  848. // Removes the files from the disk.
  849. void CDataDrivenCleaner::PurgeFiles(IEmptyVolumeCacheCallBack *picb, BOOL bDoDirectories)
  850. {
  851. CLEANFILESTRUCT *pCleanFile = _head;
  852. _cbSpaceFreed.QuadPart = 0;
  853. while (pCleanFile)
  854. {
  855. //
  856. //Remove a directory
  857. //
  858. if (bDoDirectories && pCleanFile->bDirectory)
  859. {
  860. SetFileAttributes(pCleanFile->file, FILE_ATTRIBUTE_NORMAL);
  861. if (!RemoveDirectory(pCleanFile->file))
  862. {
  863. MiDebugMsg((0, TEXT("Error RemoveDirectory(%s) returned error %d"),
  864. pCleanFile->file, GetLastError()));
  865. }
  866. }
  867. //
  868. //Remove a file
  869. //
  870. else if (!bDoDirectories && !pCleanFile->bDirectory)
  871. {
  872. SetFileAttributes(pCleanFile->file, FILE_ATTRIBUTE_NORMAL);
  873. if (!DeleteFile(pCleanFile->file))
  874. {
  875. MiDebugMsg((0, TEXT("Error DeleteFile(%s) returned error %d"),
  876. pCleanFile->file, GetLastError()));
  877. }
  878. }
  879. //
  880. //Adjust the cbSpaceFreed
  881. //
  882. _cbSpaceFreed.QuadPart += pCleanFile->ulFileSize.QuadPart;
  883. //
  884. //Call back the cleanup manager to update the progress bar
  885. //
  886. if (picb->PurgeProgress(_cbSpaceFreed.QuadPart, (_cbSpaceUsed.QuadPart - _cbSpaceFreed.QuadPart),
  887. 0, NULL) == E_ABORT)
  888. {
  889. //
  890. //User aborted so stop removing files
  891. //
  892. return;
  893. }
  894. pCleanFile = pCleanFile->pNext;
  895. }
  896. }
  897. // Frees the memory allocated by AddFileToList.
  898. void CDataDrivenCleaner::FreeList(CLEANFILESTRUCT *pCleanFile)
  899. {
  900. if (pCleanFile == NULL)
  901. return;
  902. if (pCleanFile->pNext)
  903. FreeList(pCleanFile->pNext);
  904. HeapFree(GetProcessHeap(), 0, pCleanFile);
  905. }
  906. CDataDrivenPropBag::CDataDrivenPropBag(DWORD dw) : _cRef(1), _dwFilter(dw)
  907. {
  908. incDllObjectCount();
  909. }
  910. CDataDrivenPropBag::~CDataDrivenPropBag()
  911. {
  912. decDllObjectCount();
  913. }
  914. STDMETHODIMP CDataDrivenPropBag::QueryInterface(REFIID riid, void **ppv)
  915. {
  916. *ppv = NULL;
  917. if (IsEqualIID(riid, IID_IUnknown) ||
  918. IsEqualIID(riid, IID_IPropertyBag))
  919. {
  920. *ppv = (IPropertyBag*) this;
  921. AddRef();
  922. return S_OK;
  923. }
  924. return E_NOINTERFACE;
  925. }
  926. STDMETHODIMP_(ULONG) CDataDrivenPropBag::AddRef()
  927. {
  928. return ++_cRef;
  929. }
  930. STDMETHODIMP_(ULONG) CDataDrivenPropBag::Release()
  931. {
  932. if (--_cRef)
  933. return _cRef;
  934. delete this;
  935. return 0;
  936. }
  937. STDMETHODIMP CDataDrivenPropBag::Read(LPCOLESTR pwszProp, VARIANT *pvar, IErrorLog *)
  938. {
  939. if (pvar->vt != VT_BSTR) // in/out
  940. {
  941. return E_FAIL;
  942. }
  943. DWORD dwID = 0;
  944. DWORD dwDisplay;
  945. DWORD dwDesc;
  946. TCHAR szBuf[MAX_PATH];
  947. switch (_dwFilter)
  948. {
  949. case ID_OLDFILESINROOTPROPBAG:
  950. dwDisplay = IDS_OLDFILESINROOT_DISP;
  951. dwDesc = IDS_OLDFILESINROOT_DESC;
  952. break;
  953. case ID_TEMPFILESPROPBAG:
  954. dwDisplay = IDS_TEMPFILES_DISP;
  955. dwDesc = IDS_TEMPFILES_DESC;
  956. break;
  957. case ID_SETUPFILESPROPBAG:
  958. dwDisplay = IDS_SETUPFILES_DISP;
  959. dwDesc = IDS_SETUPFILES_DESC;
  960. break;
  961. case ID_UNINSTALLEDFILESPROPBAG:
  962. dwDisplay = IDS_UNINSTALLFILES_DISP;
  963. dwDesc = IDS_UNINSTALLFILES_DESC;
  964. break;
  965. case ID_INDEXCLEANERPROPBAG:
  966. dwDisplay = IDS_INDEXERFILES_DISP;
  967. dwDesc = IDS_INDEXERFILES_DESC;
  968. break;
  969. default:
  970. return E_UNEXPECTED;
  971. }
  972. if (0 == lstrcmpiW(pwszProp, L"display"))
  973. {
  974. dwID = dwDisplay;
  975. }
  976. else if (0 == lstrcmpiW(pwszProp, L"description"))
  977. {
  978. dwID = dwDesc;
  979. }
  980. else
  981. {
  982. return E_INVALIDARG;
  983. }
  984. if (LoadString(g_hDllModule, dwID, szBuf, ARRAYSIZE(szBuf)))
  985. {
  986. pvar->bstrVal = SysAllocString(szBuf);
  987. if (pvar->bstrVal)
  988. {
  989. return S_OK;
  990. }
  991. }
  992. return E_OUTOFMEMORY;
  993. }
  994. STDMETHODIMP CDataDrivenPropBag::Write(LPCOLESTR, VARIANT *)
  995. {
  996. return E_NOTIMPL;
  997. }
  998. CContentIndexCleaner::CContentIndexCleaner(void) : _cRef(1)
  999. {
  1000. _pDataDriven = NULL;
  1001. incDllObjectCount();
  1002. }
  1003. CContentIndexCleaner::~CContentIndexCleaner(void)
  1004. {
  1005. if (_pDataDriven)
  1006. {
  1007. _pDataDriven->Release();
  1008. }
  1009. decDllObjectCount();
  1010. }
  1011. STDMETHODIMP CContentIndexCleaner::QueryInterface(REFIID riid, void **ppv)
  1012. {
  1013. *ppv = NULL;
  1014. if (riid == IID_IUnknown || riid == IID_IEmptyVolumeCache)
  1015. {
  1016. *ppv = (IEmptyVolumeCache *) this;
  1017. AddRef();
  1018. return S_OK;
  1019. }
  1020. return E_NOINTERFACE;
  1021. }
  1022. STDMETHODIMP_(ULONG) CContentIndexCleaner::AddRef(void)
  1023. {
  1024. return ++_cRef;
  1025. }
  1026. STDMETHODIMP_(ULONG) CContentIndexCleaner::Release(void)
  1027. {
  1028. if (--_cRef)
  1029. return _cRef;
  1030. delete this;
  1031. return 0;
  1032. }
  1033. STDMETHODIMP CContentIndexCleaner::Initialize(HKEY hRegKey, LPCWSTR pszVolume,
  1034. LPWSTR *ppszDisplayName, LPWSTR *ppszDescription, DWORD *pdwFlags)
  1035. {
  1036. // check the volume first to see if it is in the list of cache's know about.
  1037. // If it isn't, then we can just go ahead. If the volume is a known cache, then
  1038. // we must check to see if the service is running...
  1039. HKEY hkeyCatalogs;
  1040. BOOL fFound = FALSE;
  1041. LONG lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\ContentIndex\\Catalogs", 0, KEY_READ, &hkeyCatalogs);
  1042. if (lRes != ERROR_SUCCESS)
  1043. {
  1044. return S_FALSE;
  1045. }
  1046. int iIndex = 0;
  1047. do
  1048. {
  1049. WCHAR szBuffer[MAX_PATH];
  1050. DWORD dwSize = ARRAYSIZE(szBuffer);
  1051. lRes = RegEnumKeyExW(hkeyCatalogs, iIndex ++, szBuffer, &dwSize, NULL, NULL, NULL, NULL);
  1052. if (lRes != ERROR_SUCCESS)
  1053. {
  1054. break;
  1055. }
  1056. WCHAR szData[MAX_PATH];
  1057. dwSize = sizeof(szData);
  1058. lRes = SHGetValueW(hkeyCatalogs, szBuffer, L"Location", NULL, szData, &dwSize);
  1059. if (lRes == ERROR_SUCCESS)
  1060. {
  1061. // check to see if it is the same volume... (two characters letter and colon)
  1062. if (StrCmpNIW(pszVolume, szData , 2) == 0)
  1063. {
  1064. fFound = TRUE;
  1065. }
  1066. }
  1067. }
  1068. while (TRUE);
  1069. RegCloseKey(hkeyCatalogs);
  1070. if (fFound)
  1071. {
  1072. // check to see if the index is on or off, if the indexer is on, then we should not allow the user to blow
  1073. // this off their hard drive...
  1074. SC_HANDLE hSCM = OpenSCManager(NULL, NULL, GENERIC_READ | SC_MANAGER_ENUMERATE_SERVICE);
  1075. if (hSCM)
  1076. {
  1077. SC_HANDLE hCI;
  1078. hCI = OpenService(hSCM, TEXT("cisvc"), SERVICE_QUERY_STATUS);
  1079. if (hCI)
  1080. {
  1081. SERVICE_STATUS rgSs;
  1082. if (QueryServiceStatus(hCI, &rgSs))
  1083. {
  1084. if (rgSs.dwCurrentState != SERVICE_RUNNING)
  1085. fFound = FALSE;
  1086. }
  1087. CloseServiceHandle(hCI);
  1088. }
  1089. CloseServiceHandle(hSCM);
  1090. }
  1091. // if it wasn't inactive, then we can't delete it...
  1092. if (fFound)
  1093. return S_FALSE;
  1094. }
  1095. CDataDrivenCleaner *pDataDrivenCleaner = new CDataDrivenCleaner;
  1096. if (pDataDrivenCleaner)
  1097. {
  1098. pDataDrivenCleaner->QueryInterface(IID_IEmptyVolumeCache, (void **)&_pDataDriven);
  1099. pDataDrivenCleaner->Release();
  1100. }
  1101. return _pDataDriven ? _pDataDriven->Initialize(hRegKey, pszVolume, ppszDisplayName, ppszDescription, pdwFlags) : E_FAIL;
  1102. }
  1103. STDMETHODIMP CContentIndexCleaner::GetSpaceUsed(DWORDLONG *pdwSpaceUsed, IEmptyVolumeCacheCallBack *picb)
  1104. {
  1105. if (_pDataDriven)
  1106. return _pDataDriven->GetSpaceUsed(pdwSpaceUsed, picb);
  1107. return E_FAIL;
  1108. }
  1109. STDMETHODIMP CContentIndexCleaner::Purge(DWORDLONG dwSpaceToFree, IEmptyVolumeCacheCallBack *picb)
  1110. {
  1111. if (_pDataDriven)
  1112. return _pDataDriven->Purge(dwSpaceToFree, picb);
  1113. return E_FAIL;
  1114. }
  1115. STDMETHODIMP CContentIndexCleaner::ShowProperties(HWND hwnd)
  1116. {
  1117. if (_pDataDriven)
  1118. return _pDataDriven->ShowProperties(hwnd);
  1119. return E_FAIL;
  1120. }
  1121. STDMETHODIMP CContentIndexCleaner::Deactivate(DWORD *pdwFlags)
  1122. {
  1123. if (_pDataDriven)
  1124. return _pDataDriven->Deactivate(pdwFlags);
  1125. return E_FAIL;
  1126. }