windows-nt/Source/XPSP1/NT/shell/applets/cleanup/dataclen/dataclen.cpp
2020-09-26 16:20:57 +08:00

1318 lines
36 KiB
C++

#include "common.h"
#include "dataguid.h"
#include "compguid.h"
#include <emptyvc.h>
#include "dataclen.h"
#include "compclen.h"
#include <regstr.h>
#include <olectl.h>
#include <tlhelp32.h>
#define DECL_CRTFREE
#include <crtfree.h>
#ifndef RESOURCE_H
#include "resource.h"
#endif
#include <winsvc.h>
#include <shlwapi.h>
#include <shlwapip.h>
#include <advpub.h>
HINSTANCE g_hDllModule = NULL; // Handle to this DLL itself.
LONG g_cDllObjects = 0; // Count number of existing objects
STDAPI_(int) LibMain(HINSTANCE hInstance, DWORD fdwReason, LPVOID pvRes)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
g_hDllModule = hInstance;
break;;
}
return TRUE;
}
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
DWORD dw;
HRESULT hr;
*ppv = NULL;
//
// Is the request for one of our cleaner objects?
//
if (IsEqualCLSID(rclsid, CLSID_DataDrivenCleaner))
{
dw = ID_SYSTEMDATACLEANER;
}
else if (IsEqualCLSID(rclsid, CLSID_ContentIndexerCleaner))
{
dw = ID_CONTENTINDEXCLEANER;
}
else if (IsEqualCLSID(rclsid, CLSID_CompCleaner))
{
dw = ID_COMPCLEANER;
}
else if (IsEqualCLSID(rclsid, CLSID_OldFilesInRootPropBag))
{
dw = ID_OLDFILESINROOTPROPBAG;
}
else if (IsEqualCLSID(rclsid, CLSID_TempFilesPropBag))
{
dw = ID_TEMPFILESPROPBAG;
}
else if (IsEqualCLSID(rclsid, CLSID_SetupFilesPropBag))
{
dw = ID_SETUPFILESPROPBAG;
}
else if (IsEqualCLSID(rclsid, CLSID_UninstalledFilesPropBag))
{
dw = ID_UNINSTALLEDFILESPROPBAG;
}
else if (IsEqualCLSID(rclsid, CLSID_IndexCleanerPropBag))
{
dw = ID_INDEXCLEANERPROPBAG;
}
else
{
return CLASS_E_CLASSNOTAVAILABLE;
}
if (ID_COMPCLEANER == dw)
{
CCompCleanerClassFactory *pcf = new CCompCleanerClassFactory();
if (pcf)
{
hr = pcf->QueryInterface(riid, ppv);
pcf->Release();
}
else
{
hr = E_OUTOFMEMORY;
}
}
else
{
CCleanerClassFactory * pcf = new CCleanerClassFactory(dw);
if (pcf)
{
hr = pcf->QueryInterface(riid, ppv);
pcf->Release();
}
else
{
hr = E_OUTOFMEMORY;
}
}
return hr;
}
STDAPI DllCanUnloadNow(void)
{
return (0 == g_cDllObjects) ? S_OK : S_FALSE;
}
HRESULT CallRegInstall(LPCSTR szSection)
{
HRESULT hr = E_FAIL;
HINSTANCE hinstAdvPack = LoadLibrary(TEXT("ADVPACK.DLL"));
if (hinstAdvPack)
{
REGINSTALL pfnri = (REGINSTALL)GetProcAddress(hinstAdvPack, "RegInstall");
if (pfnri)
{
STRENTRY seReg[] = {
{ "25", "%SystemRoot%" },
{ "11", "%SystemRoot%\\system32" },
};
STRTABLE stReg = { ARRAYSIZE(seReg), seReg };
hr = pfnri(g_hDllModule, szSection, &stReg);
}
// since we only do this from DllInstall() don't load and unload advpack over and over
// FreeLibrary(hinstAdvPack);
}
return hr;
}
STDAPI DllRegisterServer()
{
return CallRegInstall("RegDll");
}
STDAPI DllUnregisterServer()
{
return CallRegInstall("UnregDll");
}
UINT incDllObjectCount(void)
{
return InterlockedIncrement(&g_cDllObjects);
}
UINT decDllObjectCount(void)
{
return InterlockedDecrement(&g_cDllObjects);
}
CCleanerClassFactory::CCleanerClassFactory(DWORD dw) : _cRef(1), _dwID(dw)
{
incDllObjectCount();
}
CCleanerClassFactory::~CCleanerClassFactory()
{
decDllObjectCount();
}
STDMETHODIMP CCleanerClassFactory::QueryInterface(REFIID riid, void **ppv)
{
*ppv = NULL;
if (IsEqualIID(riid, IID_IUnknown) ||
IsEqualIID(riid, IID_IClassFactory))
{
*ppv = (IClassFactory *)this;
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) CCleanerClassFactory::AddRef()
{
return ++_cRef;
}
STDMETHODIMP_(ULONG) CCleanerClassFactory::Release()
{
if (--_cRef)
return _cRef;
delete this;
return 0;
}
STDMETHODIMP CCleanerClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv)
{
HRESULT hr = E_NOINTERFACE;
*ppv = NULL;
if (pUnkOuter)
{
return CLASS_E_NOAGGREGATION;
}
if (_dwID == ID_CONTENTINDEXCLEANER)
{
CContentIndexCleaner *pContentIndexCleaner = new CContentIndexCleaner();
if (pContentIndexCleaner)
{
hr = pContentIndexCleaner->QueryInterface(riid, ppv);
pContentIndexCleaner->Release();
}
else
{
hr = E_OUTOFMEMORY;
}
}
else if (IsEqualIID(riid, IID_IEmptyVolumeCache))
{
CDataDrivenCleaner *pDataDrivenCleaner = new CDataDrivenCleaner();
if (pDataDrivenCleaner)
{
hr = pDataDrivenCleaner->QueryInterface(riid, ppv);
pDataDrivenCleaner->Release();
}
else
{
hr = E_OUTOFMEMORY;
}
}
else if (IsEqualIID(riid, IID_IPropertyBag))
{
CDataDrivenPropBag *pDataDrivenPropBag = new CDataDrivenPropBag(_dwID);
if (pDataDrivenPropBag)
{
hr = pDataDrivenPropBag->QueryInterface(riid, ppv);
pDataDrivenPropBag->Release();
}
else
{
hr = E_OUTOFMEMORY;
}
}
else
{
MiDebugMsg((0, TEXT("CDataDrivenCleanerClassFactory::CreateInstance called for unknown riid (%d)"), (_dwID)));
}
return hr;
}
STDMETHODIMP CCleanerClassFactory::LockServer(BOOL fLock)
{
if (fLock)
incDllObjectCount();
else
decDllObjectCount();
return S_OK;
}
CDataDrivenCleaner::CDataDrivenCleaner() : _cRef(1)
{
_cbSpaceUsed.QuadPart = 0;
_cbSpaceFreed.QuadPart = 0;
_szVolume[0] = 0;
_szFolder[0] = 0;
_filelist[0] = 0;
_dwFlags = 0;
_head = NULL;
incDllObjectCount();
}
CDataDrivenCleaner::~CDataDrivenCleaner()
{
FreeList(_head);
_head = NULL;
decDllObjectCount();
}
STDMETHODIMP CDataDrivenCleaner::QueryInterface(REFIID riid, void **ppv)
{
*ppv = NULL;
if (IsEqualIID(riid, IID_IUnknown) ||
IsEqualIID(riid, IID_IEmptyVolumeCache))
{
*ppv = (IEmptyVolumeCache*) this;
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) CDataDrivenCleaner::AddRef()
{
return ++_cRef;
}
STDMETHODIMP_(ULONG) CDataDrivenCleaner::Release()
{
if (--_cRef)
return _cRef;
delete this;
return 0;
}
// Initializes the System Data Driven Cleaner and returns the
// specified IEmptyVolumeCache flags to the cache manager.
STDMETHODIMP CDataDrivenCleaner::Initialize(HKEY hRegKey, LPCWSTR pszVolume,
LPWSTR *ppszDisplayName, LPWSTR *ppszDescription, DWORD *pdwFlags)
{
TCHAR szTempFolder[MAX_PATH];
ULONG DaysLastAccessed = 0;
PTSTR pTemp;
BOOL bFolderOnVolume;
_bPurged = FALSE;
*ppszDisplayName = NULL; // cleanmgr.exe will get these values from
*ppszDescription = NULL;
_ftMinLastAccessTime.dwLowDateTime = 0;
_ftMinLastAccessTime.dwHighDateTime = 0;
if (*pdwFlags & EVCF_SETTINGSMODE)
{
return S_OK;
}
_szFolder[0] = 0;
_dwFlags = 0;
_filelist[0] = 0;
_szCleanupCmdLine[0] = 0;
if (hRegKey)
{
DWORD dwType, cbData;
DWORD dwCSIDL;
cbData = sizeof(dwCSIDL);
if (ERROR_SUCCESS == RegQueryValueEx(hRegKey, REGSTR_VAL_CSIDL, NULL, &dwType, (LPBYTE)&dwCSIDL, &cbData))
{
// CSIDL=<hex CSIDL_ value>
SHGetFolderPath(NULL, dwCSIDL, NULL, 0, _szFolder);
if (_szFolder[0])
{
TCHAR szRelPath[MAX_PATH];
cbData = sizeof(szRelPath);
if (ERROR_SUCCESS == RegQueryValueEx(hRegKey, REGSTR_VAL_FOLDER, NULL, &dwType, (LPBYTE)szRelPath, &cbData))
{
// optionally append "folder" as a relative path
PathAppend(_szFolder, szRelPath);
}
}
}
if (0 == _szFolder[0])
{
// still nothing, try "folder"=<path1>|<path2>
cbData = sizeof(_szFolder);
if (ERROR_SUCCESS == RegQueryValueEx(hRegKey, REGSTR_VAL_FOLDER, NULL, &dwType, (LPBYTE)_szFolder, &cbData))
{
if (REG_SZ == dwType)
{
// REG_SZ that needs to be converted to a MULTI_SZ
//
// paths separated by '|' like ?:\foo|?:\bar
for (pTemp = _szFolder; *pTemp; pTemp++)
{
if (*pTemp == TEXT('|'))
{
*pTemp++ = NULL;
}
}
// double NULL terminated
pTemp++;
*pTemp = 0;
}
else if (REG_EXPAND_SZ == dwType)
{
// single folder with environment expantion
if (SHExpandEnvironmentStrings(_szFolder, szTempFolder, (ARRAYSIZE(szTempFolder) - 1))) // leave extra space for double NULL
{
lstrcpy(_szFolder, szTempFolder);
}
_szFolder[lstrlen(_szFolder) + 1] = 0; // double NULL terminated.
}
else if (REG_MULTI_SZ == dwType)
{
// nothing else to do, we're done
}
else
{
// invalid data
_szFolder[0] = NULL;
}
}
}
cbData = sizeof(_dwFlags);
RegQueryValueEx(hRegKey, REGSTR_VAL_FLAGS, NULL, &dwType, (LPBYTE)&_dwFlags, &cbData);
cbData = sizeof(_filelist);
RegQueryValueEx(hRegKey, REGSTR_VAL_FILELIST, NULL, &dwType, (LPBYTE)_filelist, &cbData);
cbData = sizeof(DaysLastAccessed);
RegQueryValueEx(hRegKey, REGSTR_VAL_LASTACCESS, NULL, &dwType, (LPBYTE)&DaysLastAccessed, &cbData);
cbData = sizeof(_szCleanupCmdLine);
RegQueryValueEx(hRegKey, REGSTR_VAL_CLEANUPSTRING, NULL, &dwType, (LPBYTE)_szCleanupCmdLine, &cbData);
}
// If the DDEVCF_RUNIFOUTOFDISKSPACE bit is set then make sure the EVCF_OUTOFDISKSPACE flag
// was passed in. If it was not then return S_FALSE so we won't run.
if ((_dwFlags & DDEVCF_RUNIFOUTOFDISKSPACE) &&
(!(*pdwFlags & EVCF_OUTOFDISKSPACE)))
{
return S_FALSE;
}
lstrcpy(_szVolume, pszVolume);
// Fix up the filelist. The file list can either be a MULTI_SZ list of files or
// a list of files separated by the ':' colon character or a '|' bar character.
// These characters were choosen because they are invalid filename characters.
for (pTemp = _filelist; *pTemp; pTemp++)
{
if (*pTemp == TEXT(':') || *pTemp == TEXT('|'))
{
*pTemp++ = 0;
}
}
pTemp++; // double null terminate
*pTemp = 0;
bFolderOnVolume = FALSE;
if (_szFolder[0] == 0)
{
// If no folder value is given so use the current volume
lstrcpy(_szFolder, pszVolume);
bFolderOnVolume = TRUE;
}
else
{
// A valid folder value was given, loop over each folder to check for "?" and ensure that
// we are on a drive that contains some of the specified folders
for (LPTSTR pszFolder = _szFolder; *pszFolder; pszFolder += lstrlen(pszFolder) + 1)
{
// Replace the first character of each folder (driver letter) if it is a '?'
// with the current volume.
if (*pszFolder == TEXT('?'))
{
*pszFolder = *pszVolume;
bFolderOnVolume = TRUE;
}
// If there is a valid "folder" value in the registry make sure that it is
// on the specified volume. If it is not then return S_FALSE so that we are
// not displayed on the list of items that can be freed.
if (!bFolderOnVolume)
{
lstrcpy(szTempFolder, pszFolder);
szTempFolder[lstrlen(pszVolume)] = 0;
if (lstrcmpi(pszVolume, szTempFolder) == 0)
{
bFolderOnVolume = TRUE;
}
}
}
}
if (bFolderOnVolume == FALSE)
{
return S_FALSE; //Don't display us in the list
}
//
// Determine the LastAccessedTime
//
if (DaysLastAccessed)
{
ULARGE_INTEGER ulTemp, ulLastAccessTime;
//Determine the number of days in 100ns units
ulTemp.LowPart = FILETIME_HOUR_LOW;
ulTemp.HighPart = FILETIME_HOUR_HIGH;
ulTemp.QuadPart *= DaysLastAccessed;
//Get the current FILETIME
SYSTEMTIME st;
GetSystemTime(&st);
FILETIME ft;
SystemTimeToFileTime(&st, &ft);
ulLastAccessTime.LowPart = ft.dwLowDateTime;
ulLastAccessTime.HighPart = ft.dwHighDateTime;
//Subtract the Last Access number of days (in 100ns units) from
//the current system time.
ulLastAccessTime.QuadPart -= ulTemp.QuadPart;
//Save this minimal Last Access time in the FILETIME member variable
//ftMinLastAccessTime.
_ftMinLastAccessTime.dwLowDateTime = ulLastAccessTime.LowPart;
_ftMinLastAccessTime.dwHighDateTime = ulLastAccessTime.HighPart;
_dwFlags |= DDEVCF_PRIVATE_LASTACCESS;
}
*pdwFlags = 0; // disable this item by default
if (_dwFlags & DDEVCF_DONTSHOWIFZERO)
*pdwFlags |= EVCF_DONTSHOWIFZERO;
return S_OK;
}
// Returns the total amount of space that the data driven cleaner can remove.
STDMETHODIMP CDataDrivenCleaner::GetSpaceUsed(DWORDLONG *pdwSpaceUsed, IEmptyVolumeCacheCallBack *picb)
{
_cbSpaceUsed.QuadPart = 0;
//
// Walk all of the folders in the folders list scanning for disk space.
//
for (LPTSTR pszFolder = _szFolder; *pszFolder; pszFolder += lstrlen(pszFolder) + 1)
WalkForUsedSpace(pszFolder, picb);
picb->ScanProgress(_cbSpaceUsed.QuadPart, EVCCBF_LASTNOTIFICATION, NULL);
*pdwSpaceUsed = _cbSpaceUsed.QuadPart;
return S_OK;
}
// Purges (deletes) all of the files specified in the "filelist" portion of the registry.
STDMETHODIMP CDataDrivenCleaner::Purge(DWORDLONG dwSpaceToFree, IEmptyVolumeCacheCallBack *picb)
{
_bPurged = TRUE;
//
//Delete the files
//
PurgeFiles(picb, FALSE);
PurgeFiles(picb, TRUE);
//
//Send the last notification to the cleanup manager
//
picb->PurgeProgress(_cbSpaceFreed.QuadPart, (_cbSpaceUsed.QuadPart - _cbSpaceFreed.QuadPart),
EVCCBF_LASTNOTIFICATION, NULL);
//
//Free the list of files
//
FreeList(_head);
_head = NULL;
//
//Run the "CleanupString" command line if one was provided
//
if (*_szCleanupCmdLine)
{
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si);
if (CreateProcess(NULL, _szCleanupCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
{
MiDebugMsg((0, TEXT("CreateProcess(%s) failed with error %d"), _szCleanupCmdLine, GetLastError()));
}
}
return S_OK;
}
STDMETHODIMP CDataDrivenCleaner::ShowProperties(HWND hwnd)
{
return S_OK;
}
// Deactivates the System Driven Data cleaner...this basically does nothing.
STDMETHODIMP CDataDrivenCleaner::Deactivate(DWORD *pdwFlags)
{
*pdwFlags = 0;
//
//See if this object should be removed.
//Note that we will only remove a cleaner if it's Purge() method was run.
//
if (_bPurged && (_dwFlags & DDEVCF_REMOVEAFTERCLEAN))
*pdwFlags |= EVCF_REMOVEFROMLIST;
return S_OK;
}
/*
** checks if the file is a specified number of days
** old (if the "lastaccess" DWORD is in the registry for this cleaner).
** If the file has not been accessed in the specified number of days
** then it can safely be deleted. If the file has been accessed in
** that number of days then the file will not be deleted.
*/
BOOL CDataDrivenCleaner::LastAccessisOK(FILETIME ftFileLastAccess)
{
if (_dwFlags & DDEVCF_PRIVATE_LASTACCESS)
{
//Is the last access FILETIME for this file less than the current
//FILETIME minus the number of specified days?
return (CompareFileTime(&ftFileLastAccess, &_ftMinLastAccessTime) == -1);
}
return TRUE;
}
// checks if a file is open by doing a CreateFile
// with fdwShareMode of 0. If GetLastError() retuns
// ERROR_SHARING_VIOLATION then this function retuns TRUE because
// someone has the file open. Otherwise this function retuns false.
BOOL TestFileIsOpen(LPCTSTR lpFile, FILETIME *pftFileLastAccess)
{
#if 0
// too slow, disable this
HANDLE hFile = CreateFile(lpFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if ((hFile == INVALID_HANDLE_VALUE) &&
(GetLastError() == ERROR_SHARING_VIOLATION))
{
return TRUE; //File is currently open by someone
}
//
// File is not currently open
//
SetFileTime(hFile, NULL, pftFileLastAccess, NULL);
CloseHandle(hFile);
#endif
return FALSE;
}
// recursively walk the specified directory and
// add all the files under this directory to the delete list.
BOOL CDataDrivenCleaner::WalkAllFiles(LPCTSTR lpPath, IEmptyVolumeCacheCallBack *picb)
{
BOOL bRet = TRUE;
BOOL bFind = TRUE;
HANDLE hFind;
WIN32_FIND_DATA wd;
TCHAR szFindPath[MAX_PATH];
TCHAR szAddFile[MAX_PATH];
ULARGE_INTEGER dwFileSize;
static DWORD dwCount = 0;
//
//If this is a directory then tack a *.* onto the end of the path
//and recurse through the rest of the directories
//
DWORD dwAttributes = GetFileAttributes(lpPath);
if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (PathCombine(szFindPath, lpPath, TEXT("*.*")))
{
bFind = TRUE;
hFind = FindFirstFile(szFindPath, &wd);
while (hFind != INVALID_HANDLE_VALUE && bFind)
{
//
//First check if the attributes of this file are OK for us to delete.
//
if (((!(wd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) ||
(_dwFlags & DDEVCF_REMOVEREADONLY)) &&
((!(wd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)) ||
(_dwFlags & DDEVCF_REMOVESYSTEM)) &&
((!(wd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) ||
(_dwFlags & DDEVCF_REMOVEHIDDEN)))
{
if (PathCombine(szAddFile, lpPath, wd.cFileName))
{
//
//This is a file so check if it is open
//
if ((!(wd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) &&
(TestFileIsOpen(szAddFile, &wd.ftLastAccessTime) == FALSE) &&
(LastAccessisOK(wd.ftLastAccessTime)))
{
//
//File is not open so add it to the list
//
dwFileSize.HighPart = wd.nFileSizeHigh;
dwFileSize.LowPart = wd.nFileSizeLow;
AddFileToList(szAddFile, dwFileSize, FALSE);
}
}
//
//CallBack the cleanup Manager to update the UI
//
if ((dwCount++ % 10) == 0)
{
if (picb->ScanProgress(_cbSpaceUsed.QuadPart, 0, NULL) == E_ABORT)
{
//
//User aborted
//
FindClose(hFind);
return FALSE;
}
}
}
bFind = FindNextFile(hFind, &wd);
}
FindClose(hFind);
}
//
//Recurse through all of the directories
//
if (PathCombine(szFindPath, lpPath, TEXT("*.*")))
{
bFind = TRUE;
hFind = FindFirstFile(szFindPath, &wd);
while (hFind != INVALID_HANDLE_VALUE && bFind)
{
//
//This is a directory
//
if ((wd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
(lstrcmp(wd.cFileName, TEXT(".")) != 0) &&
(lstrcmp(wd.cFileName, TEXT("..")) != 0))
{
if (PathCombine(szAddFile, lpPath, wd.cFileName))
{
dwFileSize.QuadPart = 0;
AddFileToList(szAddFile, dwFileSize, TRUE);
if (WalkAllFiles(szAddFile, picb) == FALSE)
{
//
//User cancled
//
FindClose(hFind);
return FALSE;
}
}
}
bFind = FindNextFile(hFind, &wd);
}
FindClose(hFind);
}
}
return bRet;
}
// walk the specified directory and create a
// linked list of files that can be deleted. It will also
// increment the member variable to indicate how much disk space
// these files are taking.
// It will look at the dwFlags member variable to determine if it
// needs to recursively walk the tree or not.
BOOL CDataDrivenCleaner::WalkForUsedSpace(LPCTSTR lpPath, IEmptyVolumeCacheCallBack *picb)
{
BOOL bRet = TRUE;
BOOL bFind = TRUE;
HANDLE hFind;
WIN32_FIND_DATA wd;
TCHAR szFindPath[MAX_PATH];
TCHAR szAddFile[MAX_PATH];
ULARGE_INTEGER dwFileSize;
static DWORD dwCount = 0;
LPTSTR lpSingleFile;
//
//If this is a directory then tack a *.* onto the end of the path
//and recurse through the rest of the directories
//
DWORD dwAttributes = GetFileAttributes(lpPath);
if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
//
//Enum through the MULTI_SZ filelist
//
for (lpSingleFile = _filelist; *lpSingleFile; lpSingleFile += lstrlen(lpSingleFile) + 1)
{
lstrcpy(szFindPath, lpPath);
PathAppend(szFindPath, lpSingleFile);
bFind = TRUE;
hFind = FindFirstFile(szFindPath, &wd);
while (hFind != INVALID_HANDLE_VALUE && bFind)
{
if (StrCmp(wd.cFileName, TEXT(".")) == 0 || StrCmp(wd.cFileName, TEXT("..")) == 0)
{
// ignore these two, otherwise we'll cover the whole disk..
bFind = FindNextFile(hFind, &wd);
continue;
}
//
//First check if the attributes of this file are OK for us to delete.
//
if (((!(wd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) ||
(_dwFlags & DDEVCF_REMOVEREADONLY)) &&
((!(wd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)) ||
(_dwFlags & DDEVCF_REMOVESYSTEM)) &&
((!(wd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) ||
(_dwFlags & DDEVCF_REMOVEHIDDEN)))
{
lstrcpy(szAddFile, lpPath);
PathAppend(szAddFile, wd.cFileName);
//
//Check if this is a subdirectory
//
if (wd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (_dwFlags & DDEVCF_REMOVEDIRS)
{
dwFileSize.QuadPart = 0;
AddFileToList(szAddFile, dwFileSize, TRUE);
if (WalkAllFiles(szAddFile, picb) == FALSE)
{
//
//User cancled
//
FindClose(hFind);
return FALSE;
}
}
}
//
//This is a file so check if it is open
//
else if ((TestFileIsOpen(szAddFile, &wd.ftLastAccessTime) == FALSE) &&
(LastAccessisOK(wd.ftLastAccessTime)))
{
//
//File is not open so add it to the list
//
dwFileSize.HighPart = wd.nFileSizeHigh;
dwFileSize.LowPart = wd.nFileSizeLow;
AddFileToList(szAddFile, dwFileSize, FALSE);
}
//
//CallBack the cleanup Manager to update the UI
//
if ((dwCount++ % 10) == 0)
{
if (picb->ScanProgress(_cbSpaceUsed.QuadPart, 0, NULL) == E_ABORT)
{
//
//User aborted
//
FindClose(hFind);
return FALSE;
}
}
}
bFind = FindNextFile(hFind, &wd);
}
FindClose(hFind);
}
if (_dwFlags & DDEVCF_DOSUBDIRS)
{
//
//Recurse through all of the directories
//
lstrcpy(szFindPath, lpPath);
PathAppend(szFindPath, TEXT("*.*"));
bFind = TRUE;
hFind = FindFirstFile(szFindPath, &wd);
while (hFind != INVALID_HANDLE_VALUE && bFind)
{
//
//This is a directory
//
if ((wd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
(lstrcmp(wd.cFileName, TEXT(".")) != 0) &&
(lstrcmp(wd.cFileName, TEXT("..")) != 0))
{
lstrcpy(szAddFile, lpPath);
PathAppend(szAddFile, wd.cFileName);
if (WalkForUsedSpace(szAddFile, picb) == FALSE)
{
//
//User cancled
//
FindClose(hFind);
return FALSE;
}
}
bFind = FindNextFile(hFind, &wd);
}
FindClose(hFind);
}
if (_dwFlags & DDEVCF_REMOVEPARENTDIR)
{
// add the parent directory to the list if we are told to remove them....
dwFileSize.QuadPart = 0;
AddFileToList(lpPath, dwFileSize, TRUE);
}
}
else
{
MiDebugMsg((0, TEXT("CDataDrivenCleaner::WalkForUsedSpace -> %s is NOT a directory!"),
lpPath));
}
return bRet;
}
// Adds a file to the linked list of files.
BOOL CDataDrivenCleaner::AddFileToList(LPCTSTR lpFile, ULARGE_INTEGER filesize, BOOL bDirectory)
{
BOOL bRet = TRUE;
CLEANFILESTRUCT *pNew = (CLEANFILESTRUCT *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pNew));
if (pNew == NULL)
{
MiDebugMsg((0, TEXT("CDataDrivenCleaner::AddFileToList -> ERROR HeapAlloc() failed with error %d"),
GetLastError()));
return FALSE;
}
lstrcpy(pNew->file, lpFile);
pNew->ulFileSize.QuadPart = filesize.QuadPart;
pNew->bSelected = TRUE;
pNew->bDirectory = bDirectory;
if (_head)
pNew->pNext = _head;
else
pNew->pNext = NULL;
_head = pNew;
_cbSpaceUsed.QuadPart += filesize.QuadPart;
return bRet;
}
// Removes the files from the disk.
void CDataDrivenCleaner::PurgeFiles(IEmptyVolumeCacheCallBack *picb, BOOL bDoDirectories)
{
CLEANFILESTRUCT *pCleanFile = _head;
_cbSpaceFreed.QuadPart = 0;
while (pCleanFile)
{
//
//Remove a directory
//
if (bDoDirectories && pCleanFile->bDirectory)
{
SetFileAttributes(pCleanFile->file, FILE_ATTRIBUTE_NORMAL);
if (!RemoveDirectory(pCleanFile->file))
{
MiDebugMsg((0, TEXT("Error RemoveDirectory(%s) returned error %d"),
pCleanFile->file, GetLastError()));
}
}
//
//Remove a file
//
else if (!bDoDirectories && !pCleanFile->bDirectory)
{
SetFileAttributes(pCleanFile->file, FILE_ATTRIBUTE_NORMAL);
if (!DeleteFile(pCleanFile->file))
{
MiDebugMsg((0, TEXT("Error DeleteFile(%s) returned error %d"),
pCleanFile->file, GetLastError()));
}
}
//
//Adjust the cbSpaceFreed
//
_cbSpaceFreed.QuadPart += pCleanFile->ulFileSize.QuadPart;
//
//Call back the cleanup manager to update the progress bar
//
if (picb->PurgeProgress(_cbSpaceFreed.QuadPart, (_cbSpaceUsed.QuadPart - _cbSpaceFreed.QuadPart),
0, NULL) == E_ABORT)
{
//
//User aborted so stop removing files
//
return;
}
pCleanFile = pCleanFile->pNext;
}
}
// Frees the memory allocated by AddFileToList.
void CDataDrivenCleaner::FreeList(CLEANFILESTRUCT *pCleanFile)
{
if (pCleanFile == NULL)
return;
if (pCleanFile->pNext)
FreeList(pCleanFile->pNext);
HeapFree(GetProcessHeap(), 0, pCleanFile);
}
CDataDrivenPropBag::CDataDrivenPropBag(DWORD dw) : _cRef(1), _dwFilter(dw)
{
incDllObjectCount();
}
CDataDrivenPropBag::~CDataDrivenPropBag()
{
decDllObjectCount();
}
STDMETHODIMP CDataDrivenPropBag::QueryInterface(REFIID riid, void **ppv)
{
*ppv = NULL;
if (IsEqualIID(riid, IID_IUnknown) ||
IsEqualIID(riid, IID_IPropertyBag))
{
*ppv = (IPropertyBag*) this;
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) CDataDrivenPropBag::AddRef()
{
return ++_cRef;
}
STDMETHODIMP_(ULONG) CDataDrivenPropBag::Release()
{
if (--_cRef)
return _cRef;
delete this;
return 0;
}
STDMETHODIMP CDataDrivenPropBag::Read(LPCOLESTR pwszProp, VARIANT *pvar, IErrorLog *)
{
if (pvar->vt != VT_BSTR) // in/out
{
return E_FAIL;
}
DWORD dwID = 0;
DWORD dwDisplay;
DWORD dwDesc;
TCHAR szBuf[MAX_PATH];
switch (_dwFilter)
{
case ID_OLDFILESINROOTPROPBAG:
dwDisplay = IDS_OLDFILESINROOT_DISP;
dwDesc = IDS_OLDFILESINROOT_DESC;
break;
case ID_TEMPFILESPROPBAG:
dwDisplay = IDS_TEMPFILES_DISP;
dwDesc = IDS_TEMPFILES_DESC;
break;
case ID_SETUPFILESPROPBAG:
dwDisplay = IDS_SETUPFILES_DISP;
dwDesc = IDS_SETUPFILES_DESC;
break;
case ID_UNINSTALLEDFILESPROPBAG:
dwDisplay = IDS_UNINSTALLFILES_DISP;
dwDesc = IDS_UNINSTALLFILES_DESC;
break;
case ID_INDEXCLEANERPROPBAG:
dwDisplay = IDS_INDEXERFILES_DISP;
dwDesc = IDS_INDEXERFILES_DESC;
break;
default:
return E_UNEXPECTED;
}
if (0 == lstrcmpiW(pwszProp, L"display"))
{
dwID = dwDisplay;
}
else if (0 == lstrcmpiW(pwszProp, L"description"))
{
dwID = dwDesc;
}
else
{
return E_INVALIDARG;
}
if (LoadString(g_hDllModule, dwID, szBuf, ARRAYSIZE(szBuf)))
{
pvar->bstrVal = SysAllocString(szBuf);
if (pvar->bstrVal)
{
return S_OK;
}
}
return E_OUTOFMEMORY;
}
STDMETHODIMP CDataDrivenPropBag::Write(LPCOLESTR, VARIANT *)
{
return E_NOTIMPL;
}
CContentIndexCleaner::CContentIndexCleaner(void) : _cRef(1)
{
_pDataDriven = NULL;
incDllObjectCount();
}
CContentIndexCleaner::~CContentIndexCleaner(void)
{
if (_pDataDriven)
{
_pDataDriven->Release();
}
decDllObjectCount();
}
STDMETHODIMP CContentIndexCleaner::QueryInterface(REFIID riid, void **ppv)
{
*ppv = NULL;
if (riid == IID_IUnknown || riid == IID_IEmptyVolumeCache)
{
*ppv = (IEmptyVolumeCache *) this;
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) CContentIndexCleaner::AddRef(void)
{
return ++_cRef;
}
STDMETHODIMP_(ULONG) CContentIndexCleaner::Release(void)
{
if (--_cRef)
return _cRef;
delete this;
return 0;
}
STDMETHODIMP CContentIndexCleaner::Initialize(HKEY hRegKey, LPCWSTR pszVolume,
LPWSTR *ppszDisplayName, LPWSTR *ppszDescription, DWORD *pdwFlags)
{
// check the volume first to see if it is in the list of cache's know about.
// If it isn't, then we can just go ahead. If the volume is a known cache, then
// we must check to see if the service is running...
HKEY hkeyCatalogs;
BOOL fFound = FALSE;
LONG lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\ContentIndex\\Catalogs", 0, KEY_READ, &hkeyCatalogs);
if (lRes != ERROR_SUCCESS)
{
return S_FALSE;
}
int iIndex = 0;
do
{
WCHAR szBuffer[MAX_PATH];
DWORD dwSize = ARRAYSIZE(szBuffer);
lRes = RegEnumKeyExW(hkeyCatalogs, iIndex ++, szBuffer, &dwSize, NULL, NULL, NULL, NULL);
if (lRes != ERROR_SUCCESS)
{
break;
}
WCHAR szData[MAX_PATH];
dwSize = sizeof(szData);
lRes = SHGetValueW(hkeyCatalogs, szBuffer, L"Location", NULL, szData, &dwSize);
if (lRes == ERROR_SUCCESS)
{
// check to see if it is the same volume... (two characters letter and colon)
if (StrCmpNIW(pszVolume, szData , 2) == 0)
{
fFound = TRUE;
}
}
}
while (TRUE);
RegCloseKey(hkeyCatalogs);
if (fFound)
{
// check to see if the index is on or off, if the indexer is on, then we should not allow the user to blow
// this off their hard drive...
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, GENERIC_READ | SC_MANAGER_ENUMERATE_SERVICE);
if (hSCM)
{
SC_HANDLE hCI;
hCI = OpenService(hSCM, TEXT("cisvc"), SERVICE_QUERY_STATUS);
if (hCI)
{
SERVICE_STATUS rgSs;
if (QueryServiceStatus(hCI, &rgSs))
{
if (rgSs.dwCurrentState != SERVICE_RUNNING)
fFound = FALSE;
}
CloseServiceHandle(hCI);
}
CloseServiceHandle(hSCM);
}
// if it wasn't inactive, then we can't delete it...
if (fFound)
return S_FALSE;
}
CDataDrivenCleaner *pDataDrivenCleaner = new CDataDrivenCleaner;
if (pDataDrivenCleaner)
{
pDataDrivenCleaner->QueryInterface(IID_IEmptyVolumeCache, (void **)&_pDataDriven);
pDataDrivenCleaner->Release();
}
return _pDataDriven ? _pDataDriven->Initialize(hRegKey, pszVolume, ppszDisplayName, ppszDescription, pdwFlags) : E_FAIL;
}
STDMETHODIMP CContentIndexCleaner::GetSpaceUsed(DWORDLONG *pdwSpaceUsed, IEmptyVolumeCacheCallBack *picb)
{
if (_pDataDriven)
return _pDataDriven->GetSpaceUsed(pdwSpaceUsed, picb);
return E_FAIL;
}
STDMETHODIMP CContentIndexCleaner::Purge(DWORDLONG dwSpaceToFree, IEmptyVolumeCacheCallBack *picb)
{
if (_pDataDriven)
return _pDataDriven->Purge(dwSpaceToFree, picb);
return E_FAIL;
}
STDMETHODIMP CContentIndexCleaner::ShowProperties(HWND hwnd)
{
if (_pDataDriven)
return _pDataDriven->ShowProperties(hwnd);
return E_FAIL;
}
STDMETHODIMP CContentIndexCleaner::Deactivate(DWORD *pdwFlags)
{
if (_pDataDriven)
return _pDataDriven->Deactivate(pdwFlags);
return E_FAIL;
}