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.

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