976 lines
28 KiB
C++
976 lines
28 KiB
C++
|
#include "common.h"
|
||
|
|
||
|
#include <emptyvc.h>
|
||
|
|
||
|
#ifndef COMPCLEN_H
|
||
|
#include "compclen.h"
|
||
|
#endif
|
||
|
|
||
|
#include <regstr.h>
|
||
|
#include <olectl.h>
|
||
|
#include <tlhelp32.h>
|
||
|
|
||
|
|
||
|
#ifndef RESOURCE_H
|
||
|
#include "resource.h"
|
||
|
#endif
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
#include <stdio.h>
|
||
|
#endif // DEBUG
|
||
|
|
||
|
BOOL g_bSettingsChange = FALSE;
|
||
|
|
||
|
const LPCTSTR g_NoCompressFiles[] =
|
||
|
{
|
||
|
TEXT("NTLDR"),
|
||
|
TEXT("OSLOADER.EXE"),
|
||
|
TEXT("PAGEFILE.SYS"),
|
||
|
TEXT("NTDETECT.COM"),
|
||
|
TEXT("EXPLORER.EXE"),
|
||
|
};
|
||
|
|
||
|
LPCTSTR g_NoCompressExts[] =
|
||
|
{
|
||
|
TEXT(".PAL")
|
||
|
};
|
||
|
|
||
|
extern HINSTANCE g_hDllModule;
|
||
|
|
||
|
extern UINT incDllObjectCount(void);
|
||
|
extern UINT decDllObjectCount(void);
|
||
|
|
||
|
CCompCleanerClassFactory::CCompCleanerClassFactory() : m_cRef(1)
|
||
|
{
|
||
|
incDllObjectCount();
|
||
|
}
|
||
|
|
||
|
CCompCleanerClassFactory::~CCompCleanerClassFactory()
|
||
|
{
|
||
|
decDllObjectCount();
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCompCleanerClassFactory::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) CCompCleanerClassFactory::AddRef()
|
||
|
{
|
||
|
return ++m_cRef;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP_(ULONG) CCompCleanerClassFactory::Release()
|
||
|
{
|
||
|
if (--m_cRef)
|
||
|
return m_cRef;
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCompCleanerClassFactory::CreateInstance(IUnknown * pUnkOuter, REFIID riid, void **ppvObj)
|
||
|
{
|
||
|
*ppvObj = NULL;
|
||
|
|
||
|
if (pUnkOuter)
|
||
|
{
|
||
|
return CLASS_E_NOAGGREGATION;
|
||
|
}
|
||
|
|
||
|
HRESULT hr;
|
||
|
CCompCleaner *pCompCleaner = new CCompCleaner();
|
||
|
if (pCompCleaner)
|
||
|
{
|
||
|
hr = pCompCleaner->QueryInterface (riid, ppvObj);
|
||
|
pCompCleaner->Release();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCompCleanerClassFactory::LockServer(BOOL fLock)
|
||
|
{
|
||
|
if (fLock)
|
||
|
incDllObjectCount();
|
||
|
else
|
||
|
decDllObjectCount();
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
CCompCleaner::CCompCleaner() : m_cRef(1)
|
||
|
{
|
||
|
cbSpaceUsed.QuadPart = 0;
|
||
|
cbSpaceFreed.QuadPart = 0;
|
||
|
szVolume[0] = 0;
|
||
|
szFolder[0] = 0;
|
||
|
|
||
|
incDllObjectCount();
|
||
|
}
|
||
|
|
||
|
CCompCleaner::~CCompCleaner()
|
||
|
{
|
||
|
// Free the list of directories
|
||
|
FreeList(head);
|
||
|
head = NULL;
|
||
|
|
||
|
decDllObjectCount();
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCompCleaner::QueryInterface(REFIID riid, void **ppv)
|
||
|
{
|
||
|
*ppv = NULL;
|
||
|
|
||
|
if (IsEqualIID(riid, IID_IUnknown) ||
|
||
|
IsEqualIID(riid, IID_IEmptyVolumeCache) ||
|
||
|
IsEqualIID(riid, IID_IEmptyVolumeCache2))
|
||
|
{
|
||
|
*ppv = (IEmptyVolumeCache2*) this;
|
||
|
AddRef();
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP_(ULONG) CCompCleaner::AddRef()
|
||
|
{
|
||
|
return ++m_cRef;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP_(ULONG) CCompCleaner::Release()
|
||
|
{
|
||
|
if (--m_cRef)
|
||
|
return m_cRef;
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Initializes the Compression Cleaner and returns flags to the cache manager
|
||
|
|
||
|
STDMETHODIMP CCompCleaner::Initialize(HKEY hRegKey,
|
||
|
LPCWSTR pszVolume,
|
||
|
LPWSTR *ppwszDisplayName,
|
||
|
LPWSTR *ppwszDescription,
|
||
|
DWORD *pdwFlags)
|
||
|
{
|
||
|
TCHAR szFileSystemName[MAX_PATH];
|
||
|
DWORD fFileSystemFlags;
|
||
|
|
||
|
bPurged = FALSE;
|
||
|
|
||
|
//
|
||
|
// Allocate memory for the DisplayName string and load the string.
|
||
|
// If the allocation fails, then we will return NULL which will cause
|
||
|
// cleanmgr.exe to read the name from the registry.
|
||
|
//
|
||
|
if (*ppwszDisplayName = (LPWSTR)CoTaskMemAlloc(DISPLAYNAME_LENGTH * sizeof(WCHAR)))
|
||
|
{
|
||
|
LoadString(g_hDllModule, IDS_COMPCLEANER_DISP, *ppwszDisplayName, DISPLAYNAME_LENGTH);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Allocate memory for the Description string and load the string.
|
||
|
// If the allocation fails, then we will return NULL which will cause
|
||
|
// cleanmgr.exe to read the description from the registry.
|
||
|
//
|
||
|
if (*ppwszDescription = (LPWSTR)CoTaskMemAlloc(DESCRIPTION_LENGTH * sizeof(WCHAR)))
|
||
|
{
|
||
|
LoadString(g_hDllModule, IDS_COMPCLEANER_DESC, *ppwszDescription, DESCRIPTION_LENGTH);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
//If you want your cleaner to run only when the machine is dangerously low on
|
||
|
//disk space then return S_FALSE unless the EVCF_OUTOFDISKSPACE flag is set.
|
||
|
//
|
||
|
//if (!(*pdwFlags & EVCF_OUTOFDISKSPACE))
|
||
|
//{
|
||
|
// return S_FALSE;
|
||
|
//}
|
||
|
|
||
|
if (*pdwFlags & EVCF_SETTINGSMODE)
|
||
|
{
|
||
|
bSettingsMode = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bSettingsMode = FALSE;
|
||
|
}
|
||
|
|
||
|
//Tell the cache manager to disable this item by default
|
||
|
*pdwFlags = 0;
|
||
|
|
||
|
//Tell the Disk Cleanup Manager that we have a Settings button
|
||
|
*pdwFlags |= EVCF_HASSETTINGS;
|
||
|
|
||
|
// If we're in Settings mode no need to do all this other work
|
||
|
//
|
||
|
if (bSettingsMode)
|
||
|
return S_OK;
|
||
|
|
||
|
ftMinLastAccessTime.dwLowDateTime = 0;
|
||
|
ftMinLastAccessTime.dwHighDateTime = 0;
|
||
|
|
||
|
if (GetVolumeInformation(pszVolume, NULL, 0, NULL, NULL, &fFileSystemFlags, szFileSystemName, MAX_PATH) &&
|
||
|
(0 == lstrcmp(szFileSystemName, TEXT("NTFS"))) &&
|
||
|
(fFileSystemFlags & FS_FILE_COMPRESSION))
|
||
|
{
|
||
|
lstrcpy(szFolder, pszVolume);
|
||
|
|
||
|
// Calculate the last access date filetime
|
||
|
CalcLADFileTime();
|
||
|
return S_OK;
|
||
|
}
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCompCleaner::InitializeEx(HKEY hRegKey, LPCWSTR pcwszVolume, LPCWSTR pcwszKeyName,
|
||
|
LPWSTR *ppwszDisplayName, LPWSTR *ppwszDescription,
|
||
|
LPWSTR *ppwszBtnText, DWORD *pdwFlags)
|
||
|
{
|
||
|
// Allocate memory for the ButtonText string and load the string.
|
||
|
// If we can't allocate the memory, leave the pointer NULL.
|
||
|
|
||
|
if (*ppwszBtnText = (LPWSTR)CoTaskMemAlloc(BUTTONTEXT_LENGTH * sizeof(WCHAR)))
|
||
|
{
|
||
|
LoadString(g_hDllModule, IDS_COMPCLEANER_BUTTON, *ppwszBtnText, BUTTONTEXT_LENGTH);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now let the IEmptyVolumeCache version 1 Init function do the rest
|
||
|
//
|
||
|
return Initialize(hRegKey, pcwszVolume, ppwszDisplayName, ppwszDescription, pdwFlags);
|
||
|
}
|
||
|
|
||
|
// Returns the total amount of space that the compression cleaner can free.
|
||
|
|
||
|
STDMETHODIMP CCompCleaner::GetSpaceUsed(DWORDLONG *pdwSpaceUsed, IEmptyVolumeCacheCallBack *picb)
|
||
|
{
|
||
|
cbSpaceUsed.QuadPart = 0;
|
||
|
|
||
|
WalkFileSystem(picb, FALSE);
|
||
|
|
||
|
picb->ScanProgress(cbSpaceUsed.QuadPart, EVCCBF_LASTNOTIFICATION, NULL);
|
||
|
|
||
|
*pdwSpaceUsed = cbSpaceUsed.QuadPart;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
// does the compression
|
||
|
|
||
|
STDMETHODIMP CCompCleaner::Purge(DWORDLONG dwSpaceToFree, IEmptyVolumeCacheCallBack *picb)
|
||
|
{
|
||
|
bPurged = TRUE;
|
||
|
|
||
|
// Compress the files
|
||
|
WalkFileSystem(picb, TRUE);
|
||
|
|
||
|
// Send the last notification to the cleanup manager
|
||
|
picb->PurgeProgress(cbSpaceFreed.QuadPart, (cbSpaceUsed.QuadPart - cbSpaceFreed.QuadPart), EVCCBF_LASTNOTIFICATION, NULL);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Dialog box that displays all of the files that will be compressed by this cleaner.
|
||
|
**
|
||
|
** NOTE: Per the specification for the Compression Cleaner we are not
|
||
|
** providing the view files functionality. However, I will leave
|
||
|
** the framework in place just in case we want to use it.
|
||
|
*/
|
||
|
INT_PTR CALLBACK ViewFilesDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
HWND hwndList;
|
||
|
LV_ITEM lviItem;
|
||
|
CLEANFILESTRUCT *pCleanFile;
|
||
|
|
||
|
switch (Msg)
|
||
|
{
|
||
|
case WM_INITDIALOG:
|
||
|
hwndList = GetDlgItem(hDlg, IDC_COMP_LIST);
|
||
|
pCleanFile = (CLEANFILESTRUCT *)lParam;
|
||
|
|
||
|
ListView_DeleteAllItems(hwndList);
|
||
|
|
||
|
while (pCleanFile)
|
||
|
{
|
||
|
lviItem.mask = LVIF_TEXT | LVIF_IMAGE;
|
||
|
lviItem.iSubItem = 0;
|
||
|
lviItem.iItem = 0;
|
||
|
|
||
|
//
|
||
|
//Only show files
|
||
|
//
|
||
|
if (!pCleanFile->bDirectory)
|
||
|
{
|
||
|
lviItem.pszText = pCleanFile->file;
|
||
|
ListView_InsertItem(hwndList, &lviItem);
|
||
|
}
|
||
|
|
||
|
pCleanFile = pCleanFile->pNext;
|
||
|
lviItem.iItem++;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case WM_COMMAND:
|
||
|
|
||
|
switch (LOWORD(wParam))
|
||
|
{
|
||
|
case IDOK:
|
||
|
case IDCANCEL:
|
||
|
EndDialog(hDlg, 0);
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// Dialog box that displays the settings for this cleaner.
|
||
|
|
||
|
INT_PTR CALLBACK SettingsDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
HKEY hCompClenReg; // Handle to our registry path
|
||
|
DWORD dwDisposition; // stuff for the reg calls
|
||
|
DWORD dwByteCount; // Ditto
|
||
|
DWORD dwNumDays = DEFAULT_DAYS; // Number of days setting from registry
|
||
|
static UINT DaysIn; // Number of days initial setting
|
||
|
UINT DaysOut; // Number of days final setting
|
||
|
#ifdef DEBUG
|
||
|
static CLEANFILESTRUCT *pCleanFile; // Pointer to our file list
|
||
|
#endif // DEBUG
|
||
|
|
||
|
switch(Msg) {
|
||
|
|
||
|
case WM_INITDIALOG:
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
pCleanFile = (CLEANFILESTRUCT *)lParam;
|
||
|
#endif // DEBUG
|
||
|
//
|
||
|
// Set the range for the Days spin control (1 to 500)
|
||
|
//
|
||
|
SendDlgItemMessage(hDlg, IDC_COMP_SPIN, UDM_SETRANGE, 0, (LPARAM) MAKELONG ((short) MAX_DAYS, (short) MIN_DAYS));
|
||
|
|
||
|
//
|
||
|
// Get the current user setting for # days and init
|
||
|
// the spin control edit box
|
||
|
//
|
||
|
if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_LOCAL_MACHINE, COMPCLN_REGPATH,
|
||
|
0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
|
||
|
&hCompClenReg, &dwDisposition))
|
||
|
{
|
||
|
dwByteCount = sizeof(dwNumDays);
|
||
|
|
||
|
if (ERROR_SUCCESS == RegQueryValueEx(hCompClenReg,
|
||
|
TEXT("Days"), NULL, NULL, (LPBYTE) &dwNumDays, &dwByteCount))
|
||
|
{
|
||
|
//
|
||
|
// Got day count from registry, make sure it's
|
||
|
// not too big or too small.
|
||
|
//
|
||
|
if (dwNumDays > MAX_DAYS) dwNumDays = MAX_DAYS;
|
||
|
if (dwNumDays < MIN_DAYS) dwNumDays = MIN_DAYS;
|
||
|
|
||
|
SetDlgItemInt(hDlg, IDC_COMP_EDIT, dwNumDays, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Failed to get the day count from the registry
|
||
|
// so just use the default.
|
||
|
//
|
||
|
|
||
|
SetDlgItemInt(hDlg, IDC_COMP_EDIT, DEFAULT_DAYS, FALSE);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Failed to get the day count from the registry
|
||
|
// so just use the default.
|
||
|
//
|
||
|
|
||
|
SetDlgItemInt(hDlg, IDC_COMP_EDIT, DEFAULT_DAYS, FALSE);
|
||
|
}
|
||
|
|
||
|
RegCloseKey(hCompClenReg);
|
||
|
|
||
|
// Track the initial setting so we can figure out
|
||
|
// if the user has changed the setting on the way
|
||
|
// out.
|
||
|
|
||
|
DaysIn = GetDlgItemInt(hDlg, IDC_COMP_EDIT, NULL, FALSE);
|
||
|
|
||
|
break;
|
||
|
|
||
|
case WM_COMMAND:
|
||
|
|
||
|
switch (LOWORD(wParam))
|
||
|
{
|
||
|
#ifdef DEBUG
|
||
|
case IDC_VIEW:
|
||
|
DialogBoxParam(g_hDllModule, MAKEINTRESOURCE(IDD_COMP_VIEW), hDlg, ViewFilesDlgProc, (LPARAM)pCleanFile);
|
||
|
break;
|
||
|
#endif // DEBUG
|
||
|
|
||
|
case IDOK:
|
||
|
|
||
|
//
|
||
|
// Get the current spin control value and write the
|
||
|
// setting to the registry.
|
||
|
//
|
||
|
|
||
|
DaysOut = GetDlgItemInt(hDlg, IDC_COMP_EDIT, NULL, FALSE);
|
||
|
|
||
|
if (DaysOut > MAX_DAYS) DaysOut = MAX_DAYS;
|
||
|
if (DaysOut < MIN_DAYS) DaysOut = MIN_DAYS;
|
||
|
|
||
|
if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_LOCAL_MACHINE,
|
||
|
COMPCLN_REGPATH,
|
||
|
0,
|
||
|
NULL,
|
||
|
REG_OPTION_NON_VOLATILE,
|
||
|
KEY_ALL_ACCESS,
|
||
|
NULL,
|
||
|
&hCompClenReg,
|
||
|
&dwDisposition))
|
||
|
{
|
||
|
dwNumDays = (DWORD)DaysOut;
|
||
|
RegSetValueEx(hCompClenReg,
|
||
|
TEXT("Days"),
|
||
|
0,
|
||
|
REG_DWORD,
|
||
|
(LPBYTE) &dwNumDays,
|
||
|
sizeof(dwNumDays));
|
||
|
|
||
|
RegCloseKey(hCompClenReg);
|
||
|
}
|
||
|
|
||
|
// Don't care if this failed -- what can we
|
||
|
// do about it anyway...
|
||
|
|
||
|
// If the user has changed the setting we need
|
||
|
// to recalculate the list of files.
|
||
|
|
||
|
if (DaysIn != DaysOut)
|
||
|
{
|
||
|
g_bSettingsChange = TRUE;
|
||
|
}
|
||
|
|
||
|
// Fall thru to IDCANCEL
|
||
|
|
||
|
case IDCANCEL:
|
||
|
EndDialog(hDlg, 0);
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCompCleaner::ShowProperties(HWND hwnd)
|
||
|
{
|
||
|
g_bSettingsChange = FALSE;
|
||
|
|
||
|
DialogBoxParam(g_hDllModule, MAKEINTRESOURCE(IDD_COMP_SETTINGS), hwnd, SettingsDlgProc, (LPARAM)head);
|
||
|
|
||
|
//
|
||
|
// If the settings have changed we need to recalculate the
|
||
|
// LAD Filetime.
|
||
|
//
|
||
|
if (g_bSettingsChange)
|
||
|
{
|
||
|
CalcLADFileTime();
|
||
|
return S_OK; // Tell CleanMgr that settings have changed.
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return S_FALSE; // Tell CleanMgr no settings changed.
|
||
|
}
|
||
|
|
||
|
return S_OK; // Shouldn't hit this but just in case.
|
||
|
}
|
||
|
|
||
|
// Deactivates the cleaner...this basically does nothing.
|
||
|
|
||
|
STDMETHODIMP CCompCleaner::Deactivate(DWORD *pdwFlags)
|
||
|
{
|
||
|
*pdwFlags = 0;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** checks if the file is a specified number of days
|
||
|
** old. 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.
|
||
|
**
|
||
|
** Notes;
|
||
|
** Mod Log: Created by Jason Cobb (7/97)
|
||
|
** Adapted for Compression Cleaner by DSchott (6/98)
|
||
|
*/
|
||
|
BOOL CCompCleaner::LastAccessisOK(FILETIME ftFileLastAccess)
|
||
|
{
|
||
|
//Is the last access FILETIME for this file less than the current
|
||
|
//FILETIME minus the number of specified days?
|
||
|
return (CompareFileTime(&ftFileLastAccess, &ftMinLastAccessTime) == -1);
|
||
|
}
|
||
|
|
||
|
// This function checks if the file is in the g_NoCompressFiles file list.
|
||
|
// If it is, returns TRUE, else FALSE.
|
||
|
|
||
|
BOOL IsDontCompressFile(LPCTSTR lpFullPath)
|
||
|
{
|
||
|
LPCTSTR lpFile = PathFindFileName(lpFullPath);
|
||
|
if (lpFile)
|
||
|
{
|
||
|
for (int i = 0; i < ARRAYSIZE(g_NoCompressFiles); i++)
|
||
|
{
|
||
|
if (!lstrcmpi(lpFile, g_NoCompressFiles[i]))
|
||
|
{
|
||
|
MiDebugMsg((0, TEXT("File is in No Compress list: %s"), lpFile));
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
LPCTSTR lpExt = PathFindExtension(lpFile);
|
||
|
if (lpExt)
|
||
|
{
|
||
|
for (int i = 0; i < ARRAYSIZE(g_NoCompressExts); i++)
|
||
|
{
|
||
|
if (!lstrcmpi(lpExt, g_NoCompressExts[i]))
|
||
|
{
|
||
|
MiDebugMsg((0, TEXT("File has No Compress extension: %s"), lpFile));
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return FALSE; // If we made it here the file must be OK to compress.
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
** 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.
|
||
|
**
|
||
|
** Notes;
|
||
|
** Mod Log: Created by Jason Cobb (7/97)
|
||
|
** Adapted for Compression Cleaner by DSchott (6/98)
|
||
|
**------------------------------------------------------------------------------
|
||
|
*/
|
||
|
BOOL IsFileOpen(LPTSTR lpFile, DWORD dwAttributes, FILETIME *lpftFileLastAccess)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
#if 0
|
||
|
// Need to see if we can open file with WRITE access -- if we
|
||
|
// can't we can't compress it. Of course if the file has R/O
|
||
|
// attribute then we won't be able to open for WRITE. So,
|
||
|
// we need to remove the R/O attribute long enough to try
|
||
|
// opening the file then restore the original attributes.
|
||
|
|
||
|
if (dwAttributes & FILE_ATTRIBUTE_READONLY)
|
||
|
{
|
||
|
SetFileAttributes(lpFile, FILE_ATTRIBUTE_NORMAL);
|
||
|
}
|
||
|
|
||
|
SetLastError(0);
|
||
|
|
||
|
HANDLE hFile = CreateFile(lpFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
|
||
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
||
|
|
||
|
if (INVALID_HANDLE_VALUE == hFile)
|
||
|
{
|
||
|
DWORD dwResult = GetLastError();
|
||
|
|
||
|
if ((ERROR_SHARING_VIOLATION == dwResult) || (ERROR_ACCESS_DENIED == dwResult))
|
||
|
{
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetFileTime(hFile, NULL, lpftFileLastAccess, NULL);
|
||
|
CloseHandle(hFile);
|
||
|
}
|
||
|
|
||
|
if (dwAttributes & FILE_ATTRIBUTE_READONLY)
|
||
|
SetFileAttributes(lpFile, dwAttributes);
|
||
|
|
||
|
#endif
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
**
|
||
|
** Purpose: This function provides a common entry point for
|
||
|
** searching for files to compress and then compressing them
|
||
|
**
|
||
|
** Notes;
|
||
|
** Mod Log: Created by Bret Anderson (1/01)
|
||
|
**
|
||
|
**------------------------------------------------------------------------------
|
||
|
*/
|
||
|
void CCompCleaner::WalkFileSystem(IEmptyVolumeCacheCallBack *picb, BOOL bCompress)
|
||
|
{
|
||
|
MiDebugMsg((0, TEXT("CCompCleaner::WalkFileSystem")));
|
||
|
|
||
|
cbSpaceUsed.QuadPart = 0;
|
||
|
|
||
|
if (!bCompress)
|
||
|
{
|
||
|
//
|
||
|
//Walk all of the folders in the folders list scanning for disk space.
|
||
|
//
|
||
|
for (LPTSTR lpSingleFolder = szFolder; *lpSingleFolder; lpSingleFolder += lstrlen(lpSingleFolder) + 1)
|
||
|
WalkForUsedSpace(lpSingleFolder, picb, bCompress, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Walk through the linked list of directories compressing the necessary files
|
||
|
//
|
||
|
CLEANFILESTRUCT *pCompDir = head;
|
||
|
while (pCompDir)
|
||
|
{
|
||
|
WalkForUsedSpace(pCompDir->file, picb, bCompress, 0);
|
||
|
pCompDir = pCompDir->pNext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Purpose: This function gets the current last access days
|
||
|
** setting from the registry and calculates the magic
|
||
|
** filetime we're looking for when searching for files
|
||
|
** to compress.
|
||
|
**
|
||
|
** Notes;
|
||
|
** Mod Log: Created by David Schott (7/98)
|
||
|
*/
|
||
|
void CCompCleaner::CalcLADFileTime()
|
||
|
{
|
||
|
HKEY hCompClenReg = NULL; // Handle to our registry path
|
||
|
DWORD dwDisposition; // stuff for the reg calls
|
||
|
DWORD dwByteCount; // Ditto
|
||
|
DWORD dwDaysLastAccessed = 0; // Day count from the registry setting
|
||
|
|
||
|
MiDebugMsg((0, TEXT("CCompCleaner::CalcLADFileTime")));
|
||
|
|
||
|
//
|
||
|
// Get the DaysLastAccessed value from the registry.
|
||
|
//
|
||
|
|
||
|
dwDaysLastAccessed = DEFAULT_DAYS;
|
||
|
|
||
|
if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_LOCAL_MACHINE, COMPCLN_REGPATH,
|
||
|
0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
|
||
|
NULL, &hCompClenReg, &dwDisposition))
|
||
|
{
|
||
|
dwByteCount = sizeof(dwDaysLastAccessed);
|
||
|
|
||
|
RegQueryValueEx(hCompClenReg,
|
||
|
TEXT("Days"),
|
||
|
NULL,
|
||
|
NULL,
|
||
|
(LPBYTE) &dwDaysLastAccessed,
|
||
|
&dwByteCount);
|
||
|
|
||
|
RegCloseKey(hCompClenReg);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Verify LD setting is within range
|
||
|
//
|
||
|
if (dwDaysLastAccessed > MAX_DAYS)
|
||
|
dwDaysLastAccessed = MAX_DAYS;
|
||
|
if (dwDaysLastAccessed < MIN_DAYS)
|
||
|
dwDaysLastAccessed = MIN_DAYS;
|
||
|
|
||
|
//
|
||
|
//Determine the LastAccessedTime
|
||
|
//
|
||
|
if (dwDaysLastAccessed != 0)
|
||
|
{
|
||
|
ULARGE_INTEGER ulTemp, ulLastAccessTime;
|
||
|
FILETIME ft;
|
||
|
|
||
|
//Determine the number of days in 100ns units
|
||
|
ulTemp.LowPart = FILETIME_HOUR_LOW;
|
||
|
ulTemp.HighPart = FILETIME_HOUR_HIGH;
|
||
|
|
||
|
ulTemp.QuadPart *= dwDaysLastAccessed;
|
||
|
|
||
|
//Get the current FILETIME
|
||
|
GetSystemTimeAsFileTime(&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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Purpose: This function will walk the specified directory and increment
|
||
|
** the member variable to indicate how much disk space these files
|
||
|
** are taking or it will perform the action of compressing the files
|
||
|
** if the bCompress variable is set.
|
||
|
** It will look at the dwFlags member variable to determine if it
|
||
|
** needs to recursively walk the tree or not.
|
||
|
** We no longer want to store a linked list of all files to compress
|
||
|
** due to extreme memory usage on large filesystems. This means
|
||
|
** we will walk through all the files on the system twice.
|
||
|
** Notes;
|
||
|
** Mod Log: Created by Jason Cobb (2/97)
|
||
|
** Adapted for Compression Cleaner by DSchott (6/98)
|
||
|
*/
|
||
|
BOOL CCompCleaner::WalkForUsedSpace(LPCTSTR lpPath, IEmptyVolumeCacheCallBack *picb, BOOL bCompress, int depth)
|
||
|
{
|
||
|
BOOL bRet = TRUE;
|
||
|
BOOL bFind = TRUE;
|
||
|
WIN32_FIND_DATA wd;
|
||
|
ULARGE_INTEGER dwFileSize;
|
||
|
static DWORD dwCount = 0;
|
||
|
|
||
|
TCHAR szFindPath[MAX_PATH], szAddFile[MAX_PATH];
|
||
|
|
||
|
if (PathCombine(szFindPath, lpPath, TEXT("*.*")))
|
||
|
{
|
||
|
BOOL bFolderFound = FALSE;
|
||
|
|
||
|
bFind = TRUE;
|
||
|
HANDLE hFind = FindFirstFile(szFindPath, &wd);
|
||
|
while (hFind != INVALID_HANDLE_VALUE && bFind)
|
||
|
{
|
||
|
if (!PathCombine(szAddFile, lpPath, wd.cFileName))
|
||
|
{
|
||
|
// Failure here means the file name is too long, just ignore that file
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (wd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||
|
{
|
||
|
dwFileSize.HighPart = 0;
|
||
|
dwFileSize.LowPart = 0;
|
||
|
bFolderFound = TRUE;
|
||
|
}
|
||
|
else if ((IsFileOpen(szAddFile, wd.dwFileAttributes, &wd.ftLastAccessTime) == FALSE) &&
|
||
|
(!(wd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)) &&
|
||
|
(!(wd.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)) &&
|
||
|
(!(wd.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE)) &&
|
||
|
(LastAccessisOK(wd.ftLastAccessTime)) &&
|
||
|
(!IsDontCompressFile(szAddFile)))
|
||
|
{
|
||
|
dwFileSize.HighPart = wd.nFileSizeHigh;
|
||
|
dwFileSize.LowPart = wd.nFileSizeLow;
|
||
|
|
||
|
if (bCompress)
|
||
|
{
|
||
|
if (!CompressFile(picb, szAddFile, dwFileSize))
|
||
|
{
|
||
|
bRet = FALSE;
|
||
|
bFind = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cbSpaceUsed.QuadPart += (dwFileSize.QuadPart * 4 / 10);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// CallBack the cleanup Manager to update the UI
|
||
|
|
||
|
if ((dwCount++ % 10) == 0 && !bCompress)
|
||
|
{
|
||
|
if (picb && picb->ScanProgress(cbSpaceUsed.QuadPart, 0, NULL) == E_ABORT)
|
||
|
{
|
||
|
//
|
||
|
//User aborted
|
||
|
//
|
||
|
bFind = FALSE;
|
||
|
bRet = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bFind = FindNextFile(hFind, &wd);
|
||
|
}
|
||
|
|
||
|
FindClose(hFind);
|
||
|
|
||
|
if (bRet && bFolderFound)
|
||
|
{
|
||
|
//
|
||
|
//Recurse through all of the directories
|
||
|
//
|
||
|
if (PathCombine(szFindPath, lpPath, TEXT("*.*")))
|
||
|
{
|
||
|
bFind = TRUE;
|
||
|
HANDLE hFind = FindFirstFile(szFindPath, &wd);
|
||
|
while (hFind != INVALID_HANDLE_VALUE && bFind)
|
||
|
{
|
||
|
if ((wd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
||
|
(lstrcmp(wd.cFileName, TEXT(".")) != 0) &&
|
||
|
(lstrcmp(wd.cFileName, TEXT("..")) != 0))
|
||
|
{
|
||
|
ULARGE_INTEGER cbSpaceBefore;
|
||
|
|
||
|
cbSpaceBefore.QuadPart = cbSpaceUsed.QuadPart;
|
||
|
|
||
|
PathCombine(szAddFile, lpPath, wd.cFileName);
|
||
|
|
||
|
if (WalkForUsedSpace(szAddFile, picb, bCompress, depth + 1) == FALSE)
|
||
|
{
|
||
|
// User canceled
|
||
|
bFind = FALSE;
|
||
|
bRet = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Tag this directory for compression
|
||
|
// We only want to tag directories that are in the root
|
||
|
// otherwise we'll end up with a very large data structure
|
||
|
if (cbSpaceBefore.QuadPart != cbSpaceUsed.QuadPart &&
|
||
|
depth == 0 && !bCompress)
|
||
|
{
|
||
|
AddDirToList(szAddFile);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bFind = FindNextFile(hFind, &wd);
|
||
|
}
|
||
|
|
||
|
FindClose(hFind);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
// Adds a directory to the linked list of directories.
|
||
|
|
||
|
BOOL CCompCleaner::AddDirToList(LPCTSTR lpFile)
|
||
|
{
|
||
|
BOOL bRet = TRUE;
|
||
|
CLEANFILESTRUCT *pNew = (CLEANFILESTRUCT *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pNew));
|
||
|
|
||
|
if (pNew == NULL)
|
||
|
{
|
||
|
MiDebugMsg((0, TEXT("CCompCleaner::AddDirToList -> ERROR HeapAlloc() failed with error %d"), GetLastError()));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
lstrcpy(pNew->file, lpFile);
|
||
|
|
||
|
if (head)
|
||
|
pNew->pNext = head;
|
||
|
else
|
||
|
pNew->pNext = NULL;
|
||
|
|
||
|
head = pNew;
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
void CCompCleaner::FreeList(CLEANFILESTRUCT *pCleanFile)
|
||
|
{
|
||
|
if (pCleanFile == NULL)
|
||
|
return;
|
||
|
|
||
|
if (pCleanFile->pNext)
|
||
|
FreeList(pCleanFile->pNext);
|
||
|
|
||
|
HeapFree(GetProcessHeap(), 0, pCleanFile);
|
||
|
}
|
||
|
|
||
|
// Compresses the specified file
|
||
|
|
||
|
BOOL CCompCleaner::CompressFile(IEmptyVolumeCacheCallBack *picb, LPCTSTR lpFile, ULARGE_INTEGER filesize)
|
||
|
{
|
||
|
ULARGE_INTEGER ulCompressedSize;
|
||
|
|
||
|
ulCompressedSize.QuadPart = filesize.QuadPart;
|
||
|
|
||
|
// If the file is read only, we need to remove the
|
||
|
// R/O attribute long enough to compress the file.
|
||
|
|
||
|
BOOL bFileWasRO = FALSE;
|
||
|
DWORD dwAttributes = GetFileAttributes(lpFile);
|
||
|
|
||
|
if ((0xFFFFFFFF != dwAttributes) && (dwAttributes & FILE_ATTRIBUTE_READONLY))
|
||
|
{
|
||
|
bFileWasRO = TRUE;
|
||
|
SetFileAttributes(lpFile, FILE_ATTRIBUTE_NORMAL);
|
||
|
}
|
||
|
|
||
|
HANDLE hFile = CreateFile(lpFile, GENERIC_READ | GENERIC_WRITE,
|
||
|
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||
|
if (INVALID_HANDLE_VALUE != hFile)
|
||
|
{
|
||
|
USHORT InBuffer = COMPRESSION_FORMAT_DEFAULT;
|
||
|
DWORD dwBytesReturned = 0;
|
||
|
if (DeviceIoControl(hFile, FSCTL_SET_COMPRESSION, &InBuffer, sizeof(InBuffer),
|
||
|
NULL, 0, &dwBytesReturned, NULL))
|
||
|
{
|
||
|
// Get the compressed file size so we can figure out
|
||
|
// how much space we gained by compressing.
|
||
|
ulCompressedSize.LowPart = GetCompressedFileSize(lpFile, &ulCompressedSize.HighPart);
|
||
|
}
|
||
|
CloseHandle(hFile);
|
||
|
}
|
||
|
|
||
|
// Restore the file attributes if needed
|
||
|
if (bFileWasRO)
|
||
|
SetFileAttributes(lpFile, dwAttributes);
|
||
|
|
||
|
// Adjust the cbSpaceFreed
|
||
|
cbSpaceFreed.QuadPart = cbSpaceFreed.QuadPart + (filesize.QuadPart - ulCompressedSize.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 compressing files
|
||
|
MiDebugMsg((0, TEXT("CCompCleaner::PurgeFiles User abort")));
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|