/*
**------------------------------------------------------------------------------
** Module:  Disk Cleanup Applet
** File:    dmgrinfo.c
**
** Purpose: Defines the CleanupMgrInfo class for the property tab
** Notes:   
** Mod Log: Created by Jason Cobb (2/97)
**
** Copyright (c)1997 Microsoft Corporation, All Rights Reserved
**------------------------------------------------------------------------------
*/

/*
**------------------------------------------------------------------------------
** Project include files
**------------------------------------------------------------------------------
*/
#include "common.h"
#include <limits.h>
#include <emptyvc.h>
#include "dmgrinfo.h"
#include "dmgrdlg.h"
#include "diskutil.h"
#include "resource.h"
#include "msprintf.h"


/*
**------------------------------------------------------------------------------
**  Local variables
**------------------------------------------------------------------------------
*/
HINSTANCE   CleanupMgrInfo::hInstance             = NULL;


/*
**------------------------------------------------------------------------------
** Function prototypes
**------------------------------------------------------------------------------
*/
INT_PTR CALLBACK
ScanAbortDlgProc(
    HWND hDlg,
    UINT Message,
    WPARAM wParam,
    LPARAM lParam
    );

void
ScanAbortThread(
    CleanupMgrInfo *pcmi
    );

INT_PTR CALLBACK
PurgeAbortDlgProc(
    HWND hDlg,
    UINT Message,
    WPARAM wParam,
    LPARAM lParam
    );

void
PurgeAbortThread(
    CleanupMgrInfo *pcmi
    );

/*
**------------------------------------------------------------------------------
** Function definitions
**------------------------------------------------------------------------------
*/


void
CleanupMgrInfo::Register(
    HINSTANCE hInstance
    )
{
    CleanupMgrInfo::hInstance = hInstance;
}

void
CleanupMgrInfo::Unregister(
    void
    )
{
    CleanupMgrInfo::hInstance= NULL;
}

/*
**------------------------------------------------------------------------------
** GetCleanupMgrInfoPointer
**
** Purpose:    
** Mod Log:    Created by Jason Cobb (2/97)
**------------------------------------------------------------------------------
*/
CleanupMgrInfo * GetCleanupMgrInfoPointer(
    HWND hDlg
    )
{
    //   
    //Get the DriveInfo
    //
    CleanupMgrInfo * pcmi = (CleanupMgrInfo *)GetWindowLongPtr(hDlg, DWLP_USER);

    return pcmi;
}



/*
**------------------------------------------------------------------------------
** CleanupMgrInfo method definitions
**------------------------------------------------------------------------------
*/

/*
**------------------------------------------------------------------------------
** CleanupMgrInit::init
**
** Purpose:    sets to default values
** Mod Log:    Created by Jason Cobb (2/97)
**------------------------------------------------------------------------------
*/
void 
CleanupMgrInfo::init(void)
{
    dre             = Drive_INV;
    szVolName[0]    = 0;
    vtVolume        = vtINVALID;
    dwUIFlags       = 0;
    bPurgeFiles     = TRUE;
}


/*
**------------------------------------------------------------------------------
** CleanupMgrInfo::destroy
**
** Purpose:    releases any dynamic memory
** Mod Log:    Created by Jason Cobb (2/97)
**------------------------------------------------------------------------------
*/
void 
CleanupMgrInfo::destroy(void)
{
    //
    //Set values back to defaults
    //
    init();
}

/*
**------------------------------------------------------------------------------
** CleanupMgrInfo::CleanupMgrInfo
**
** Purpose:    Default constructor
** Mod Log:    Created by Jason Cobb (2/97)
**------------------------------------------------------------------------------
*/
CleanupMgrInfo::CleanupMgrInfo (void)
{
    init();
}

/*
**------------------------------------------------------------------------------
** CleanupMgrInfo::CleanupMgrInfo
**
** Purpose:    Constructor
** Mod Log:    Created by Jason Cobb (2/97)
**------------------------------------------------------------------------------
*/
CleanupMgrInfo::CleanupMgrInfo(
    LPTSTR lpDrive,
    DWORD dwFlags,
    ULONG ulProfile
    )
{
    HRESULT hr;

    init();

    hr = CoInitialize(NULL);

    if (SUCCEEDED(hr))
    {
        if (create(lpDrive, dwFlags))
        {
            dwReturnCode = RETURN_SUCCESS;
            dwUIFlags = dwFlags;
            ulSAGEProfile = ulProfile;
            bAbortScan = FALSE;
            bAbortPurge = FALSE;
    
            volumeCacheCallBack = NULL;
            pIEmptyVolumeCacheCallBack = NULL;
            volumeCacheCallBack = new CVolumeCacheCallBack();

            if (volumeCacheCallBack)
            {
                hr = volumeCacheCallBack->QueryInterface(IID_IEmptyVolumeCacheCallBack,
                                                   (LPVOID*)&pIEmptyVolumeCacheCallBack);
            }
            else
            {
                hr = E_OUTOFMEMORY;
            }

            if (hr != NOERROR)
            {
                MiDebugMsg((hr, "CleanupMgrInfo::CleanupMgrInfo failed with error "));
            }

            //
            //Initialize all of the cleanup clients
            //
            if (initializeClients() && !(dwUIFlags & FLAG_TUNEUP) && !(dwUIFlags & FLAG_SAGESET))
            {
                //
                //Have all of the cleanup clients calculate the ammount of disk
                //space that they can free up.
                //
                getSpaceUsedByClients();
            }
        }
    }
}

/*
**------------------------------------------------------------------------------
** CleanupMgrInfo::~CleanupMgrInfo
**
** Purpose:    Destructor
** Mod Log:    Created by Jason Cobb (2/97)
**------------------------------------------------------------------------------
*/
CleanupMgrInfo::~CleanupMgrInfo (void)
{
    if (isValid())
    {
        //
        //Cleanup the Volume Cache Clients
        //
        deactivateClients();    

        if (volumeCacheCallBack != NULL)
        {
            volumeCacheCallBack->Release();
        }
   
        CoUninitialize();

        destroy();   
    }
}

/*
**------------------------------------------------------------------------------
** CleanupMgrInfo::create
**
** Purpose:    Gets Drive info from drive letter
** Mod Log:    Created by Jason Cobb (2/97)
**------------------------------------------------------------------------------
*/
BOOL 
CleanupMgrInfo::create(
    LPTSTR lpDrive,
    DWORD Flags
    )
{
     
    //
    //Note:  Make sure the assigns to zero stay current
    //       otherwise we might get garbage stats if
    //       we fail because of lack of free space
    //
    DWORD cSectorsPerCluster;
    DWORD cBytesPerSector;
    DWORD cBytesPerCluster;
    DWORD cFreeClusters;
    DWORD cUsedClusters;
    DWORD cTotalClusters;
    ULARGE_INTEGER cbFree;
    ULARGE_INTEGER cbUsed;
    ULARGE_INTEGER cbTotal;
#ifdef NEC_98
    drenum drive;
    hardware hw_type;
#endif

    cbFree.QuadPart = 0;
    cbUsed.QuadPart = 0;
    
    //
    //Cleanup up any old stuff
    //
    destroy();

      
    //
    //Check parameters
    //
    if (lpDrive == NULL)
        return FALSE;

      
    //  
    //Is it a valid drive path
    //
    if (!fIsValidDriveString(lpDrive))
        return FALSE;

    //
    //Get drive from path
    //
    if (!GetDriveFromString(lpDrive, dre))
        return FALSE;

    lstrcpy(szRoot, lpDrive);

      
    // 
    // Step 2.  Get general info from drive
    //

    //
    //Get volume name
    //
    if (!GetVolumeInformation (szRoot,                              // Root name
                               szVolName, sizeof(szVolName),        // Volume Name
                               NULL,                                // Volume serial number
                               NULL,                                // Max path length
                               NULL,                                // flags
                               szFileSystem, sizeof(szFileSystem))) // file system name                         
    {
        //Error - failed to get volume name
        goto lblERROR;
    }

    //
    //Get the Driver Icon
    //
    if (Flags & FLAG_SAGESET)
        hDriveIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(ICON_CLEANMGR));
    else
        hDriveIcon = GetDriveIcon(dre, FALSE);

    //  
    //Get Hardware type
    //
    if (!GetHardwareType(dre, hwHardware))
    {
        //Error - failed to get hardware
        goto lblERROR;
    }


#ifdef NEC_98
    drive = Drive_A;
    GetHardwareType (Drive_A, hw_type);

    if (hw_type != hwFixed) 
    {
        drive = Drive_B;
        GetHardwareType (Drive_B, hw_type);

        if (hw_type != hwFixed)
            drive = Drive_C;
    }
#endif


    // 
    //Get disk statistics
    //
    if (!GetDiskFreeSpace (szRoot, 
                           &cSectorsPerCluster, 
                           &cBytesPerSector,
                           &cFreeClusters,
                           &cTotalClusters))
    {
        //Error - couldn't get drive stats
        goto lblERROR;
    }
      
    //  
    //Calculate secondary statistics
    //
    cBytesPerCluster = cBytesPerSector * cSectorsPerCluster;
    if (cTotalClusters >= cFreeClusters)
        cUsedClusters = cTotalClusters - cFreeClusters;
    else
        cUsedClusters = 0L;

    cbFree.QuadPart   = UInt32x32To64(cFreeClusters, cBytesPerCluster);
    cbUsed.QuadPart   = UInt32x32To64(cUsedClusters, cBytesPerCluster);
    cbTotal.QuadPart  = cbFree.QuadPart + cbUsed.QuadPart;

    //
    //Get the current low disk space ratio
    //
    cbLowSpaceThreshold = GetFreeSpaceRatio(dre, cbTotal);

    //
    //Should we also load the agressive cleaners? We only do this if we
    //are below are critical threshold of disk space left.
    //
    if (cbLowSpaceThreshold.QuadPart >= cbFree.QuadPart)
    {
        MiDebugMsg((0, "*****We are in aggressive mode*****"));
        bOutOfDiskSpace = TRUE;
    }
    else
        bOutOfDiskSpace = FALSE;

    // 
    // Step 3.  Save stats
    //

    cbDriveFree          = cbFree;
    cbDriveUsed          = cbUsed;
    cbEstCleanupSpace.QuadPart    = 0;

    //
    //Success
    //
    return TRUE;

lblERROR:
    //  
    //Error
    //
    destroy();
    return FALSE;
}

/*
**------------------------------------------------------------------------------
** CleanupMgrInfo::initializeClients
**
** Purpose:    Initializes all of the Volume Cache Clients
** Mod Log:    Created by Jason Cobb (2/97)
**------------------------------------------------------------------------------
*/
BOOL 
CleanupMgrInfo::initializeClients(void)
{
    HKEY    hKeyVolCache = NULL;
    DWORD   iSubKey;
    DWORD   dwClient;
    TCHAR   szVolCacheClient[MAX_PATH];
    TCHAR   szGUID[MAX_PATH];
    DWORD   dwGUIDSize;
    DWORD   dwType;
    DWORD   dwState, cb, cw;
    TCHAR   szProfile[64];
    BOOL    bRet = TRUE;
    BOOL    bCleanup;

    iNumVolumeCacheClients = 0;
    pClientInfo = NULL;
    
    MiDebugMsg((0, "CleanupMgrInfo::initializeClients entered"));

    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_VOLUMECACHE, 0, KEY_READ, &hKeyVolCache) == ERROR_SUCCESS)
    {
        //
        //Enumerate through all of the clients to see how large we need to make the pClientInfo array
        //

        iSubKey = 0;
        while(RegEnumKey(hKeyVolCache, iSubKey, szVolCacheClient, ARRAYSIZE(szVolCacheClient)) != ERROR_NO_MORE_ITEMS)
        {
            iSubKey++;        
        }
        
        if ((pClientInfo = (PCLIENTINFO)LocalAlloc(LPTR, (iSubKey * sizeof(CLIENTINFO)))) == NULL)
        {
#ifdef DEBUG
            MessageBox(NULL, TEXT("FATAL ERROR LocalAlloc() failed!"), TEXT("CLEANMGR DEBUG"), MB_OK);
#endif
            RegCloseKey(hKeyVolCache);
            return FALSE;
        }
        
        //
        //Fill in the pClientInfo data structure and initialize all of the volume cache clients
        //   
        iSubKey = 0;
        dwClient = 0;
        while(RegEnumKey(hKeyVolCache, iSubKey, szVolCacheClient, ARRAYSIZE(szVolCacheClient)) != ERROR_NO_MORE_ITEMS)
        {
            // default is we failed, so cleanup the current item....
            bCleanup = TRUE;
            
            if (RegOpenKeyEx(hKeyVolCache, szVolCacheClient, 0, MAXIMUM_ALLOWED, &(pClientInfo[dwClient].hClientKey)) == ERROR_SUCCESS)
            {
                lstrcpy(pClientInfo[dwClient].szRegKeyName, szVolCacheClient);
            
                dwGUIDSize = sizeof(szGUID);
                dwType = REG_SZ;
                if (RegQueryValueEx(pClientInfo[dwClient].hClientKey, NULL, NULL, &dwType, (LPBYTE)szGUID, &dwGUIDSize) == ERROR_SUCCESS)
                {
                    HRESULT hr;
                    WCHAR   wcsFmtID[39];

#ifdef UNICODE
                    StrCpyN( wcsFmtID, szGUID, ARRAYSIZE( wcsFmtID ));
#else
                    //Convert to Unicode.
                    MultiByteToWideChar(CP_ACP, 0, szGUID, -1, wcsFmtID, ARRAYSIZE( wcsFmtID )) ;
#endif

                    //Convert to GUID.
                    hr = CLSIDFromString((LPOLESTR)wcsFmtID, &(pClientInfo[dwClient].clsid));

                    if (FAILED(hr))
                    {
                        MiDebugMsg((hr, "CLSIDFromString(%s,) returned error ", szGUID));
                    }

                    //
                    //Create an instance of the COM object for this cleanup client
                    //
                    pClientInfo[dwClient].pVolumeCache = NULL;
                    hr = CoCreateInstance(pClientInfo[dwClient].clsid,
                                                    NULL,
                                                    CLSCTX_INPROC_SERVER,
                                                    IID_IEmptyVolumeCache,
                                                    (void **) &(pClientInfo[dwClient].pVolumeCache));

                    if (SUCCEEDED(hr))
                    {
                        WCHAR   wcsRoot[MAX_PATH];

                        MiDebugMsg((hr, "CleanupMgrInfo::initializeClients Created IID_IEmptyVolumeCache"));
                        //
                        //Set the flags to pass to the cleanup client
                        //
                        pClientInfo[dwClient].dwInitializeFlags = 0;
                        if (dwUIFlags & FLAG_SAGESET)
                            pClientInfo[dwClient].dwInitializeFlags |= EVCF_SETTINGSMODE;
                        if (bOutOfDiskSpace)
                            pClientInfo[dwClient].dwInitializeFlags |= EVCF_OUTOFDISKSPACE;

#ifdef UNICODE
                        StrCpyN( wcsRoot, szRoot, ARRAYSIZE( wcsRoot ));
#else
                        //
                        //Convert szRoot to UNICODE
                        //
                        MultiByteToWideChar(CP_ACP, 0, szRoot, -1, wcsRoot, ARRAYSIZE( wcsRoot ));
#endif

                        // Try to use version two of the interface if it is supported
                        IEmptyVolumeCache2 * pEVC2;
                        hr = pClientInfo[dwClient].pVolumeCache->QueryInterface( IID_IEmptyVolumeCache2, (void**)&pEVC2 );
                        if (SUCCEEDED(hr))
                        {
                            // version 2 exists so that we can have a mutli-local enabled data driven cleaner.  It
                            // allows the added Advanced Button to be set to a localized value.  It tells the
                            // object being called which key it is being called for so that one object can support
                            // multiple filters.
                            WCHAR   wcsFilterName[MAX_PATH];
                            MiDebugMsg((hr, "CleanupMgrInfo::initializeClients found V2 interface"));
#ifdef UNICODE
                            StrCpyN( wcsFilterName, szVolCacheClient, ARRAYSIZE( wcsFilterName ));
#else
                            MultiByteToWideChar(CP_ACP, 0, szVolCacheClient, -1, wcsFilterName, ARRAYSIZE( wcsFilterName )) ;
#endif

                            hr = pEVC2->InitializeEx(pClientInfo[dwClient].hClientKey,
                                                    (LPCWSTR)wcsRoot,
                                                    (LPCWSTR)wcsFilterName,
                                                    &((LPWSTR)pClientInfo[dwClient].wcsDisplayName),
                                                    &((LPWSTR)pClientInfo[dwClient].wcsDescription),
                                                    &((LPWSTR)pClientInfo[dwClient].wcsAdvancedButtonText),
                                                    &(pClientInfo[dwClient].dwInitializeFlags));
                            pEVC2->Release();
                        }
                        else
                        {
                            MiDebugMsg((hr, "CleanupMgrInfo::initializeClients using V1 interface"));
                            //
                            //Initialize the cleanup client
                            //
                            if ((pClientInfo[dwClient].wcsDescription = (LPWSTR)CoTaskMemAlloc(DESCRIPTION_LENGTH*sizeof(WCHAR))) == NULL)
                                return FALSE;

                            // We seem to have shipped this thing with a giant leak.  The object is supposted to set
                            // pClientInfo[dwClient].wcsDescription to NULL if the registry value should be used instead
                            // of the buffer.  However we just allocated a buffer for pClientInfo[dwClient].wcsDescription
                            // in the code above (this is the dumbass part).  All the filters then set this pointer to
                            // NULL and it's bye-bye buffer.  I can't simply not allocate this memory because some cleaners
                            // might rely on being able to use this memory and we shipped it that way.
                            LPWSTR wszDumbassLeakProtection = pClientInfo[dwClient].wcsDescription;
                            hr = pClientInfo[dwClient].pVolumeCache->Initialize(pClientInfo[dwClient].hClientKey,
                                                                               (LPCWSTR)wcsRoot,
                                                                               &((LPWSTR)pClientInfo[dwClient].wcsDisplayName),
                                                                               &((LPWSTR)pClientInfo[dwClient].wcsDescription),
                                                                               &(pClientInfo[dwClient].dwInitializeFlags));                                                                
                            if ( wszDumbassLeakProtection != pClientInfo[dwClient].wcsDescription )
                            {
                                // REVIEW: Use try...except around CoTaskMemFree in case some smart cleaner
                                // realized our mistake and deleted the memory for us?
                                MiDebugMsg((hr, "CleanupMgrInfo::initializeClients prevent mem leak hack"));
                                CoTaskMemFree( wszDumbassLeakProtection );
                            }

                            if ( S_OK == hr )
                            {
                                // To make it easier to make a cleaner we have a default implementation of IEmptyVolumeCache
                                // that works entirerly using registry data.  The problem is that display strings are strored
                                // in the registry.  This is invalid for NT because NT must be multi-local localizable and
                                // the only way to do that is to load all display strings from a resource.  As a hack, you
                                // can now implement IPropertyBag using an object with it's guid stored under the propertybag
                                // value in the registry.  We will cocreate this object and query for IPropertyBag.  If this
                                // works then we will attempt to read the localized strings from the property bag before we
                                // fall back on checking the registry.
                                TCHAR   szPropBagGUID[MAX_PATH];
                                HRESULT hrFoo;
                                IPropertyBag * ppb = NULL;
                                VARIANT var;

                                VariantInit( &var );
                                dwGUIDSize = sizeof(szPropBagGUID);
                                dwType = REG_SZ;
                                if (RegQueryValueEx(pClientInfo[dwClient].hClientKey, TEXT("PropertyBag"), NULL, &dwType, (LPBYTE)szPropBagGUID, &dwGUIDSize) == ERROR_SUCCESS)
                                {
                                    WCHAR   wcsFmtID[39];
                                    CLSID   clsid;

                                    MiDebugMsg((hr, "CleanupMgrInfo::initializeClients found PropBag key"));

#ifdef UNICODE
                                    StrCpyN( wcsFmtID, szPropBagGUID, ARRAYSIZE( wcsFmtID ));
#else
                                    MultiByteToWideChar(CP_ACP, 0, szPropBagGUID, -1, wcsFmtID, ARRAYSIZE( wcsFmtID )) ;
#endif

                                    //Convert to GUID.
                                    CLSIDFromString((LPOLESTR)wcsFmtID, &clsid);

                                    //
                                    //Create an instance of the COM object for this cleanup client
                                    //
                                    hrFoo = CoCreateInstance(clsid,
                                                          NULL,
                                                          CLSCTX_INPROC_SERVER,
                                                          IID_IPropertyBag,
                                                          (void **) &ppb);

                                    if ( FAILED(hrFoo) )
                                    {
                                        MiDebugMsg((hrFoo, "CleanupMgrInfo::initializeClients failed to create PropBag"));
                                    }
                                }

                                //
                                //If the client did not return the DisplayName via the Initialize
                                //Interface then we need to get it from the registry.
                                //
                                if ((pClientInfo[dwClient].wcsDisplayName) == NULL)
                                {
                                    LPTSTR  lpszDisplayName;

                                    if ( ppb )
                                    {
                                        WCHAR wszSrc[MAX_PATH];
                                        MiDebugMsg((hr, "CleanupMgrInfo::initializeClients checking PropBag for display"));

                                        SHTCharToUnicode(REGSTR_VAL_DISPLAY, wszSrc, MAX_PATH);

                                        // do propertybag stuff
                                        var.vt = VT_BSTR;
                                        var.bstrVal = NULL;
                                        hrFoo = ppb->Read( wszSrc, &var, NULL );
                                        if (SUCCEEDED(hrFoo))
                                        {
                                            if ( var.vt == VT_BSTR )
                                            {
                                                DWORD dwSize = (lstrlenW(var.bstrVal)+1)*sizeof(WCHAR);
                                                pClientInfo[dwClient].wcsDisplayName = (LPWSTR)CoTaskMemAlloc(dwSize);
                                                StrCpyW( pClientInfo[dwClient].wcsDisplayName, var.bstrVal );
                                            }
                                            VariantClear( &var );
                                        }
                                    }

                                    if ((pClientInfo[dwClient].wcsDisplayName) == NULL)
                                    {
                                        //
                                        //First check if their is a "display" value for the client's 
                                        //name that is displayed in the list box.  If not then use
                                        //the key name itself.
                                        //
                                        cb = 0;
                                        dwType = REG_SZ;
                                        RegQueryValueEx(pClientInfo[dwClient].hClientKey, REGSTR_VAL_DISPLAY, NULL, &dwType, (LPBYTE)NULL, &cb);
                                        if ((lpszDisplayName = (LPTSTR)LocalAlloc(LPTR, max(cb, (ULONG)(lstrlen(szVolCacheClient) + 1))* sizeof (TCHAR ))) != NULL)
                                        {
                                            if (RegQueryValueEx(pClientInfo[dwClient].hClientKey, REGSTR_VAL_DISPLAY, NULL, &dwType, (LPBYTE)lpszDisplayName, &cb) != ERROR_SUCCESS)
                                            {
                                                //
                                                //Count not find "display" value so use the key name instead
                                                //
                                                StrCpy(lpszDisplayName, szVolCacheClient);
                                            }

    #ifdef UNICODE
                                            cw = (lstrlen( lpszDisplayName ) + 1) * sizeof( WCHAR);
    #else
                                            //
                                            //Convert this value to UNICODE
                                            //
                                            cw = (MultiByteToWideChar(CP_ACP, 0, lpszDisplayName, -1, NULL, 0) * sizeof(WCHAR));
    #endif
                                            if ((pClientInfo[dwClient].wcsDisplayName = (LPWSTR)CoTaskMemAlloc(cw)) != NULL)
                                            {
    #ifdef UNICODE
                                                StrCpy( pClientInfo[dwClient].wcsDisplayName, lpszDisplayName );
    #else
                                                MultiByteToWideChar(CP_ACP, 0, lpszDisplayName, -1, (pClientInfo[dwClient].wcsDisplayName), cw);
    #endif
                                            }
                                            LocalFree(lpszDisplayName);
                                        }
                                    }
                                }

                                //
                                //If the client did not return the Description via the Initialize
                                //Interface then we need to get it from the registry.
                                //
                                if ((pClientInfo[dwClient].wcsDescription) == NULL)
                                {
                                    LPTSTR  lpszDescription;


                                    if ( ppb )
                                    {
                                        WCHAR wszSrc[MAX_PATH];
                                        MiDebugMsg((hr, "CleanupMgrInfo::initializeClients checking PropBag for description"));

                                        SHTCharToUnicode(REGSTR_VAL_DESCRIPTION, wszSrc, MAX_PATH);

                                        // do propertybag stuff
                                        var.vt = VT_BSTR;
                                        var.bstrVal = NULL;
                                        hrFoo = ppb->Read( wszSrc, &var, NULL );
                                        if (SUCCEEDED(hrFoo))
                                        {
                                            if ( var.vt == VT_BSTR )
                                            {
                                                DWORD dwSize = (lstrlenW(var.bstrVal)+1)*sizeof(WCHAR);
                                                pClientInfo[dwClient].wcsDescription = (LPWSTR)CoTaskMemAlloc(dwSize);
                                                StrCpyW( pClientInfo[dwClient].wcsDescription, var.bstrVal );
                                            }
                                            VariantClear( &var );
                                        }
                                    }

                                    if ((pClientInfo[dwClient].wcsDescription) == NULL)
                                    {
                                        //
                                        //Check if their is a "description" value for the client 
                                        //
                                        cb = 0;
                                        dwType = REG_SZ;
                                        RegQueryValueEx(pClientInfo[dwClient].hClientKey, REGSTR_VAL_DESCRIPTION, NULL, &dwType, (LPBYTE)NULL, &cb);
                                        if ((lpszDescription = (LPTSTR)LocalAlloc(LPTR, (cb + 1 ) * sizeof( TCHAR ))) != NULL)
                                        {
                                            if (RegQueryValueEx(pClientInfo[dwClient].hClientKey, REGSTR_VAL_DESCRIPTION, NULL, &dwType, (LPBYTE)lpszDescription, &cb) == ERROR_SUCCESS)
                                            {
#ifdef UNICODE
                                                cw = ( lstrlen( lpszDescription ) + 1 ) * sizeof( WCHAR );
#else
                                                //
                                                //Convert this value to UNICODE
                                                //
                                                cw = (MultiByteToWideChar(CP_ACP, 0, lpszDescription, -1, NULL, 0) * sizeof(WCHAR));
#endif
                                                if ((pClientInfo[dwClient].wcsDescription = (LPWSTR)CoTaskMemAlloc(cw)) != NULL)
                                                {
#ifdef UNICODE                                          
                                                    StrCpy( pClientInfo[dwClient].wcsDescription, lpszDescription );
#else
                                                    MultiByteToWideChar(CP_ACP, 0, lpszDescription, -1, (pClientInfo[dwClient].wcsDescription), cw);
#endif
                                                }
                                            }

                                            LocalFree(lpszDescription);
                                        }
                                    }
                                }

                                //
                                //Set the Advanced Button text
                                //
                                pClientInfo[dwClient].wcsAdvancedButtonText = NULL;

                                if (pClientInfo[dwClient].dwInitializeFlags & EVCF_HASSETTINGS)
                                {
                                    if ( ppb )
                                    {
                                        WCHAR wszSrc[MAX_PATH];
                                        MiDebugMsg((hr, "CleanupMgrInfo::initializeClients checking PropBag for button text"));

                                        SHTCharToUnicode(REGSTR_VAL_ADVANCEDBUTTONTEXT, wszSrc, MAX_PATH);

                                        // do propertybag stuff
                                        var.vt = VT_BSTR;
                                        var.bstrVal = NULL;
                                        hrFoo = ppb->Read( wszSrc, &var, NULL );
                                        if (SUCCEEDED(hrFoo))
                                        {
                                            if ( var.vt == VT_BSTR )
                                            {
                                                DWORD dwSize = (lstrlenW(var.bstrVal)+1)*sizeof(WCHAR);
                                                pClientInfo[dwClient].wcsAdvancedButtonText = (LPWSTR)CoTaskMemAlloc(dwSize);
                                                StrCpyW( pClientInfo[dwClient].wcsAdvancedButtonText, var.bstrVal );
                                            }
                                            VariantClear( &var );
                                        }
                                    }
                                    if ( pClientInfo[dwClient].wcsAdvancedButtonText == NULL )
                                    {
                                        LPTSTR  lpszAdvancedButtonText;
                                        TCHAR   szDetails[BUTTONTEXT_LENGTH];

                                        LoadString(g_hInstance, IDS_DETAILS, szDetails, ARRAYSIZE(szDetails));

                                        cb = 0;
                                        dwType = REG_SZ;
                                        RegQueryValueEx(pClientInfo[dwClient].hClientKey, REGSTR_VAL_ADVANCEDBUTTONTEXT, NULL, &dwType, (LPBYTE)NULL, &cb);
                                        if ((lpszAdvancedButtonText = (LPTSTR)LocalAlloc(LPTR, max(cb, (UINT) (lstrlen(szDetails) + 1)*sizeof(TCHAR)))) != NULL)
                                        {
                                            if (RegQueryValueEx(pClientInfo[dwClient].hClientKey, REGSTR_VAL_ADVANCEDBUTTONTEXT, NULL, &dwType, (LPBYTE)lpszAdvancedButtonText, &cb) != ERROR_SUCCESS)
                                            StrCpy(lpszAdvancedButtonText, szDetails);

#ifdef UNICODE
                                            cw = (lstrlen( lpszAdvancedButtonText ) + 1) * sizeof( WCHAR );
#else
                                            //
                                            //Convert this value to UNICODE
                                            //
                                            cw = (MultiByteToWideChar(CP_ACP, 0, lpszAdvancedButtonText, -1, NULL, 0) * sizeof(WCHAR));
#endif
                                            if ((pClientInfo[dwClient].wcsAdvancedButtonText = (LPWSTR)CoTaskMemAlloc(cw)) != NULL)
                                            {
#ifdef UNICODE
                                                StrCpy( pClientInfo[dwClient].wcsAdvancedButtonText, lpszAdvancedButtonText );
#else
                                                MultiByteToWideChar(CP_ACP, 0, lpszAdvancedButtonText, -1, (pClientInfo[dwClient].wcsAdvancedButtonText), cw);
#endif
                                            }

                                            LocalFree(lpszAdvancedButtonText);
                                        }
                                    }
                                }

                                if (ppb)
                                {
                                    ppb->Release();
                                }
                            }
                        }

                        // Now we're back to stuff that both version 1 and version 2 require
                        if (SUCCEEDED(hr))
                        {
                            if (S_OK == hr)
                            {
                                //
                                //Default to showing this client in the UI
                                //
                                pClientInfo[dwClient].bShow = TRUE;
                            
                                //
                                //Get the "priority" from the registry
                                //
                                cb = sizeof(pClientInfo[dwClient].dwPriority);
                                dwType = REG_DWORD;
                                if (RegQueryValueEx(pClientInfo[dwClient].hClientKey, REGSTR_VAL_PRIORITY, NULL, &dwType, (LPBYTE)&(pClientInfo[dwClient].dwPriority), &cb) != ERROR_SUCCESS)
                                    pClientInfo[dwClient].dwPriority = DEFAULT_PRIORITY;
                                
                                //
                                //Flags
                                //
                                if (dwUIFlags & FLAG_SAGERUN || dwUIFlags & FLAG_SAGESET)
                                    wsprintf(szProfile, TEXT("%s%04d"), SZ_STATE, ulSAGEProfile);
                                else
                                    lstrcpy(szProfile, SZ_STATE);
                                    
                                dwState = 0;
                                cb = sizeof(dwState);
                                dwType = REG_DWORD;
                                
                                // If we were called with the low disk flag, select every cleaner by default
                                if (dwUIFlags & FLAG_LOWDISK)
                                {
                                    pClientInfo[iSubKey].bSelected = TRUE;
                                }

                                // Otherwise, check the registry
                                else if (RegQueryValueEx(pClientInfo[dwClient].hClientKey, szProfile, NULL,
                                    &dwType, (LPBYTE)&dwState, &cb) == ERROR_SUCCESS)
                                {
                                    if (dwUIFlags & FLAG_SAGERUN || dwUIFlags & FLAG_SAGESET)
                                    {
                                        pClientInfo[dwClient].bSelected = (dwState & STATE_SAGE_SELECTED);
                                    }
                                    else
                                    {
                                        pClientInfo[dwClient].bSelected = (dwState & STATE_SELECTED);
                                    }
                                }
                                else
                                {
                                    //
                                    //No registry settings for this profile so use the cleanup clients
                                    //default settings.
                                    //
                                    if (dwUIFlags & FLAG_SAGERUN || dwUIFlags & FLAG_SAGESET)
                                    {
                                        pClientInfo[dwClient].bSelected = (pClientInfo[dwClient].dwInitializeFlags & EVCF_ENABLEBYDEFAULT_AUTO) ? TRUE : FALSE;
                                    }
                                    else
                                    {
                                        pClientInfo[dwClient].bSelected = (pClientInfo[dwClient].dwInitializeFlags & EVCF_ENABLEBYDEFAULT) ? TRUE : FALSE;
                                    }
                                }
                                
                                //
                                //Get the icon of the cleanup client
                                //

                                // first test to see if it is overridden...
                                TCHAR szIconPath[MAX_PATH];
                                cb = sizeof( szIconPath );
                                BOOL fOverridden = FALSE;
                                
                                if ( RegQueryValueEx(pClientInfo[dwClient].hClientKey, TEXT("IconPath"), NULL,
                                    &dwType, (LPBYTE)szIconPath, &cb) == ERROR_SUCCESS )
                                {
                                    fOverridden = TRUE;
                                }
                                else
                                {
                                    lstrcpy( szIconPath, szGUID );
                                }
                                
                                pClientInfo[dwClient].hIcon = GetClientIcon(szIconPath, fOverridden);

                                bCleanup = FALSE;
                            }
                            else
                            {
                                //
                                //This should be S_FALSE.  This means that the client has nothing to 
                                //cleanup now so we don't even need to show it in the list.
                                //Therefor we will just call its Release() function and close it's
                                //registry key.
                                //

                                // drop through and let it cleanup below...
                            }
                        }
                        else
                        {
                            MiDebugMsg((hr, "Client %d Initialize() retuned error ", dwClient));
                        }                                                                      
                    }
                    else
                    {
                        MiDebugMsg((hr, "Client %d %s returned error ", dwClient, szGUID));
                    }
                }
#ifdef DEBUG
                else
                {
                    MessageBox(NULL, szVolCacheClient, TEXT("ERROR Opening GUID key"), MB_OK);
                }                
#endif
            }
#ifdef DEBUG
            else
            {
                MessageBox(NULL, szVolCacheClient, TEXT("ERROR Opening the client key"), MB_OK);
            }
#endif

            if ( bCleanup )
            {
                deactivateSingleClient(&(pClientInfo[dwClient]));
                ZeroMemory( &(pClientInfo[dwClient]), sizeof( CLIENTINFO ));
            }
            else
            {
                dwClient ++;
            }
            iSubKey++;        
        }
        iNumVolumeCacheClients = dwClient;
    }
#ifdef DEBUG
    else
    {
        MessageBox(NULL, TEXT("ERROR Opening up Volume Cache key"), TEXT("CLEANMGR DEBUG"), MB_OK);
    }
#endif

    if( hKeyVolCache )
    {
        RegCloseKey(hKeyVolCache);
    }
    return bRet;
}

/*
**------------------------------------------------------------------------------
** CleanupMgrInfo::deactivateClients
**
** Purpose:    Initializes all of the Volume Cache Clients
** Mod Log:    Created by Jason Cobb (2/97)
**------------------------------------------------------------------------------
*/
void 
CleanupMgrInfo::deactivateClients(void)
{
    int     i;
    
    for (i=0; i<iNumVolumeCacheClients; i++)
    {
        deactivateSingleClient(&(pClientInfo[i]));
    }

    //
    //Free the pClientInfo array
    //
    if (pClientInfo)
    {
        MiDebugMsg((0, "LocalFree() on ClientInfo structure"));
        LocalFree( pClientInfo);
    }
}


/*
**------------------------------------------------------------------------------
** CleanupMgrInfo::deactivateSingleClient
**
** Purpose:    Deactivate's the given client and closes its registry key
** Mod Log:    Created by Jason Cobb (2/97)
**------------------------------------------------------------------------------
*/
void 
CleanupMgrInfo::deactivateSingleClient(PCLIENTINFO pSingleClientInfo)
{
    DWORD   dwDeactivateFlags = 0;
    TCHAR   szProfile[64];
    
    if (pSingleClientInfo->pVolumeCache != NULL)
    {
        //
        //Call the clients Deactivate function
        //
        pSingleClientInfo->pVolumeCache->Deactivate(&dwDeactivateFlags);

        //
        //Release the client
        //
        pSingleClientInfo->pVolumeCache->Release();
        pSingleClientInfo->pVolumeCache = NULL;
    }
            
    if (pSingleClientInfo->hClientKey != 0)
    {
        DWORD   dwState, cb, dwType, dwSelectedFlag;

        if (dwUIFlags & FLAG_SAVE_STATE)
        {
            //
            //Save the state flags
            //
            if (dwUIFlags & FLAG_SAGESET)
            {
                dwSelectedFlag = STATE_SAGE_SELECTED;
                wsprintf(szProfile, TEXT("%s%04d"), SZ_STATE, ulSAGEProfile);
            }
            else
            {
                dwSelectedFlag = STATE_SELECTED;
                lstrcpy(szProfile, SZ_STATE);
            }

            dwState = 0;
            cb = sizeof(dwState);
            dwType = REG_DWORD;
            RegQueryValueEx(pSingleClientInfo->hClientKey, szProfile, NULL,
                &dwType, (LPBYTE)&dwState, &cb);

            if (pSingleClientInfo->bSelected)
                dwState |= dwSelectedFlag;
            else
                dwState &= ~dwSelectedFlag;

            RegSetValueEx(pSingleClientInfo->hClientKey, szProfile, 0, REG_DWORD,
                (LPBYTE)&dwState, sizeof(dwState));
        }
    
        //
        //Close all of the registry keys
        //
        RegCloseKey(pSingleClientInfo->hClientKey);

        //
        //Should we remove this entry from the registry?
        //
        if (dwDeactivateFlags & EVCF_REMOVEFROMLIST && pSingleClientInfo->bSelected)
        {
            HKEY    hKeyVolCache;
            
            if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_VOLUMECACHE, 0, KEY_ALL_ACCESS, &hKeyVolCache) == ERROR_SUCCESS)
            {
                SHDeleteKey(hKeyVolCache, pSingleClientInfo->szRegKeyName);
                RegCloseKey(hKeyVolCache);
            }
        }            
            
    }

    //
    //Free the DisplayName and Description memory
    //
    if (pSingleClientInfo->wcsDisplayName)
        CoTaskMemFree(pSingleClientInfo->wcsDisplayName);
        
    if (pSingleClientInfo->wcsDescription)
        CoTaskMemFree(pSingleClientInfo->wcsDescription);
}

/*
**------------------------------------------------------------------------------
** CleanupMgrInfo::getSpaceUsedByClients
**
** Purpose:    Calls the IEmptyVolumeCache->GetSpaceUsed interface for each client
**             to determine the total amount of cache space.  This function is
**             called on a secondary thread because it can take quite a long time.
** Mod Log:    Created by Jason Cobb (2/97)
**------------------------------------------------------------------------------
*/
BOOL
CleanupMgrInfo::getSpaceUsedByClients(void)
{
    int         i;
    HRESULT     hr;
    BOOL        bRet = TRUE;
    TCHAR       szDisplayName[256];
        
    cbEstCleanupSpace.QuadPart = 0;
    bAbortScan = FALSE;

    hAbortScanEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

    // Someone should make sure the window created in the ScanAbortThread thread is visible before
    // the hAbortScanEvent event is signaled
    hAbortScanThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ScanAbortThread,
        (LPVOID)this, 0, &dwAbortScanThreadID);

    //
    //Wait until the Abort Scan window is created
    //
    WaitForSingleObject(hAbortScanEvent, INFINITE);

    CloseHandle(hAbortScanEvent);

    if (volumeCacheCallBack != NULL)
    {
        volumeCacheCallBack->SetCleanupMgrInfo((PVOID)this);
    }

    for (i=0; i<iNumVolumeCacheClients; i++)
    {
        //
        //Update the progress UI
        //
        szDisplayName[0] = '\0';
        
#ifdef UNICODE
        StrCpyN( szDisplayName, pClientInfo[i].wcsDisplayName, ARRAYSIZE( szDisplayName ));
#else
        WideCharToMultiByte(CP_ACP, 0, pClientInfo[i].wcsDisplayName, -1, szDisplayName, ARRAYSIZE(szDisplayName), NULL, NULL);
#endif
        
        PostMessage(hAbortScanWnd, WMAPP_UPDATEPROGRESS, (WPARAM)i, (LPARAM)szDisplayName);

        //
        //Query the client for the ammount of cache disk space that it could
        //possible free.
        //
        if (pClientInfo[i].pVolumeCache != NULL && volumeCacheCallBack != NULL)
        {
            volumeCacheCallBack->SetCurrentClient((PVOID)&(pClientInfo[i]));
            hr = pClientInfo[i].pVolumeCache->GetSpaceUsed(&(pClientInfo[i].dwUsedSpace.QuadPart), 
                                                                    pIEmptyVolumeCacheCallBack);      
            
            if (FAILED(hr))
            {
                dwReturnCode = RETURN_CLEANER_FAILED;
                MiDebugMsg((hr, "Client %d GetSpaceUsed failed with error ", i));
            }
            
            MiDebugMsg((0, "Client %d has %d disk space it can free", i,
                pClientInfo[i].dwUsedSpace.QuadPart));
        }

        //
        //See if this cleaner wants to be hidden if it has no space to free
        //
        if ((pClientInfo[i].dwUsedSpace.QuadPart == 0) &&
            (pClientInfo[i].dwInitializeFlags & EVCF_DONTSHOWIFZERO))
        {
            MiDebugMsg((0, "Not showing client %d because it has no space to free", i));
            pClientInfo[i].bShow = FALSE;
        }

        cbEstCleanupSpace.QuadPart += pClientInfo[i].dwUsedSpace.QuadPart;

        //
        //Did the user abort?
        //
        if (bAbortScan == TRUE)
        {
            dwReturnCode = RETURN_USER_CANCELED_SCAN;
            bRet = FALSE;
            break;
        }
    }

    // the dismissal of the progress dialog is now delayed until the propsheet comes up..

    return bRet;
}

/*
**------------------------------------------------------------------------------
** CleanupMgrInfo::calculateSpaceToPurge
**
** Purpose:    Calculates the amount of space that is going to be purged
**             by adding up all of the selected clients.  It also calculates
**             the progress bar divisor number.  This is needed because a
**             progress bar has a MAX of 0xFFFF.
**
** Mod Log:    Created by Jason Cobb (6/97)
**------------------------------------------------------------------------------
*/
void
CleanupMgrInfo::calculateSpaceToPurge(void)
{
    int i;
    
    cbSpaceToPurge.QuadPart = 0;

    for (i=0; i<iNumVolumeCacheClients; i++)
    {
        //
        //If this client is not selected or we are not showing it then don't purge it
        //
        if (pClientInfo[i].bShow == FALSE || pClientInfo[i].bSelected == FALSE)
            continue;
    
        cbSpaceToPurge.QuadPart += pClientInfo[i].dwUsedSpace.QuadPart;
    }

    cbProgressDivider.QuadPart = (cbSpaceToPurge.QuadPart / PROGRESS_DIVISOR) + 1;
}

/*
**------------------------------------------------------------------------------
** CleanupMgrInfo::purgeClients
**
** Purpose:    Calls the IEmptyVolumeCache->Purge interface for each client
**             to have the client cleaner object start removeing their files
** Mod Log:    Created by Jason Cobb (2/97)
**------------------------------------------------------------------------------
*/
BOOL
CleanupMgrInfo::purgeClients(void)
{
    int         i;
    HRESULT     hr;
    BOOL        bRet = TRUE;
    TCHAR       szDisplayName[256];
        
    cbTotalPurgedSoFar.QuadPart = 0;
    bAbortPurge = FALSE;

    //
    //Calculate the amount of space that will be purged.
    //
    calculateSpaceToPurge();
    MiDebugMsg((0, "Total number of bytes to delete is %d", cbSpaceToPurge.LowPart));

    hAbortPurgeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

    // Someone should (but it hasn't broken in 3 years), make sure the window created in PurgeAbortThread is visible before
    // the hAbortPurgeEvent is signaled
    hAbortPurgeThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PurgeAbortThread,
        (LPVOID)this, 0, &dwAbortPurgeThreadID);

    //
    //Wait until the Abort Purge window is created
    //
    WaitForSingleObject(hAbortPurgeEvent, INFINITE);

    CloseHandle(hAbortPurgeEvent);

    if (volumeCacheCallBack != NULL)
    {
        volumeCacheCallBack->SetCleanupMgrInfo((PVOID)this);
    }

    for (i=0; i<iNumVolumeCacheClients; i++)
    {
        //
        //If this client is not selected or we are not showing it then don't purge it
        //
        if (pClientInfo[i].bShow == FALSE || pClientInfo[i].bSelected == FALSE)
            continue;
    
#ifdef UNICODE
        StrCpyN( szDisplayName, pClientInfo[i].wcsDisplayName, ARRAYSIZE( szDisplayName ));
#else
        //
        //Convert UNICODE display name to ANSI and then add it to the list
        //
        WideCharToMultiByte(CP_ACP, 0, pClientInfo[i].wcsDisplayName, -1, szDisplayName, sizeof(szDisplayName), NULL, NULL);
#endif

        PostMessage(hAbortPurgeWnd, WMAPP_UPDATESTATUS, 0, (LPARAM)szDisplayName);

        cbCurrentClientPurgedSoFar.QuadPart = 0;

        //
        //Query the client for the ammount of cache disk space that it could
        //possible free.
        //
        if (pClientInfo[i].pVolumeCache != NULL && volumeCacheCallBack != NULL)
        {
            volumeCacheCallBack->SetCurrentClient((PVOID)&(pClientInfo[i]));

            hr = pClientInfo[i].pVolumeCache->Purge(pClientInfo[i].dwUsedSpace.QuadPart, pIEmptyVolumeCacheCallBack);
            
            if (FAILED(hr))
            {
                dwReturnCode = RETURN_CLEANER_FAILED;
                MiDebugMsg((hr, "Client %d Purge failed with error ", i));
            }
        }

        cbTotalPurgedSoFar.QuadPart += pClientInfo[i].dwUsedSpace.QuadPart;
        cbCurrentClientPurgedSoFar.QuadPart = 0;

        //
        //Update the progress bar
        //
        PostMessage(hAbortPurgeWnd, WMAPP_UPDATEPROGRESS, 0, 0);

        //
        //Did the user abort?
        //
        if (bAbortPurge == TRUE)
        {
            dwReturnCode = RETURN_USER_CANCELED_PURGE;
            bRet = FALSE;
            break;
        }

        Sleep(1000);
    }

    if (!bAbortPurge)
    {
        bAbortPurge = TRUE;

        //
        //Wait for Purge thread to finish
        //  
        WaitForSingleObject(hAbortPurgeThread, INFINITE);

        bAbortPurge = FALSE;
    }

    return bRet;
}

/*
**------------------------------------------------------------------------------
** GetClientIcon
**
** Purpose:    Gets the Icon for this client.  
**             The icon will be inferred using the standard OLE mechanism
**             under HKCR\CLSID\{clsid}\DefaultIcon (with the default value
**             for this being the <Module Path>, <icon index>).
**             If no icon is specified the standard windows icon will be used.
** Mod Log:    Created by Jason Cobb (2/97)
**------------------------------------------------------------------------------
*/
HICON
CleanupMgrInfo::GetClientIcon(
    LPTSTR  lpGUID,
    BOOL    fIconPath
    )
{
    HKEY    hk;
    HICON   hIconLarge, hIconSmall;
    HICON   hIcon = NULL;
    TCHAR   szIconKey[MAX_PATH];
    TCHAR   szDefaultIcon[MAX_PATH];
    DWORD   dwType, cbBytes;
    TCHAR   szIconExeName[MAX_PATH];
    int     i, iIconIndex;

    if ( fIconPath )
    {
        StrCpy( szDefaultIcon, lpGUID );
    }
    if ( !fIconPath )
    {
        wsprintf(szIconKey, SZ_DEFAULTICONPATH, lpGUID);
        if (RegOpenKey(HKEY_CLASSES_ROOT, szIconKey, &hk) == ERROR_SUCCESS)
        {
            dwType = REG_SZ;
            cbBytes = sizeof(szDefaultIcon);
            if (RegQueryValueEx(hk, NULL, NULL, &dwType, (LPBYTE)szDefaultIcon, &cbBytes) == ERROR_SUCCESS)
            {
                fIconPath = TRUE;
            }
            RegCloseKey(hk);
        }
    }

    if (fIconPath)
    {
        //
        //Parse out the exe where the icon lives
        //
        for(i=0; i<lstrlen(szDefaultIcon); i++)
        {
            if (szDefaultIcon[i] == ',')
                break;

            szIconExeName[i] = szDefaultIcon[i];
        }

        szIconExeName[i] = '\0';

        //
        //Parse out the icon index
        //
        i++;
        iIconIndex = StrToInt(&(szDefaultIcon[i]));

        if (ExtractIconEx(szIconExeName, iIconIndex, (HICON FAR *)&hIconLarge, (HICON FAR *)&hIconSmall, 1))
        {
            if (hIconSmall)
                hIcon = hIconSmall;
            else
                hIcon = hIconLarge;
        }
    }
    
    if (hIcon == NULL)
    {
        if ((hIcon = LoadIcon(CleanupMgrInfo::hInstance, MAKEINTRESOURCE(ICON_GENERIC))) == NULL)
        {
            MiDebugMsg((0, "LoadIcon failed with error %d", GetLastError()));
        }   
    }
    
    return hIcon;
}

INT_PTR CALLBACK
ScanAbortDlgProc(
    HWND hDlg,
    UINT Message,
    WPARAM wParam,
    LPARAM lParam
    )
{
    CleanupMgrInfo *pcmi;

    switch(Message)
    {
        case WM_INITDIALOG:
            SetWindowLongPtr (hDlg, DWLP_USER, 0L);

            //   
            //Get the CleanupMgrInfo
            //
            pcmi = (CleanupMgrInfo *)lParam;    
            if (pcmi == NULL)
            {   
                //Error - passed in invalid CleanupMgrInfo info
                return FALSE;
            }       

            //
            //Save pointer to CleanupMgrInfo object
            //
            SetWindowLongPtr(hDlg, DWLP_USER, lParam);

            TCHAR * psz;
            psz = SHFormatMessage( MSG_SCAN_ABORT_TEXT, pcmi->szVolName, pcmi->szRoot[0] );
            SetDlgItemText (hDlg, IDC_ABORT_TEXT, psz);
            LocalFree(psz);

            //
            //Set the limits on the progress bar
            //
            SendDlgItemMessage(hDlg, IDC_ABORT_SCAN_PROGRESS, PBM_SETRANGE,
                0, MAKELPARAM(0, pcmi->iNumVolumeCacheClients));
            break;

        case WMAPP_UPDATEPROGRESS:
            if (lParam != NULL)
                SetDlgItemText(hDlg, IDC_SCAN_STATUS_TEXT, (LPTSTR)lParam);
            else
                SetDlgItemText(hDlg, IDC_SCAN_STATUS_TEXT, TEXT(""));
                
            SendDlgItemMessage(hDlg, IDC_ABORT_SCAN_PROGRESS, PBM_SETPOS,
                (WPARAM)wParam, 0);
            break;

        case WM_CLOSE:
        case WM_COMMAND:
            pcmi = (CleanupMgrInfo *)GetWindowLongPtr (hDlg, DWLP_USER);
            if (pcmi != NULL)
                pcmi->bAbortScan = TRUE;
            break;

        default:
            return FALSE;
    }

    return TRUE;
}

BOOL
PASCAL
MessagePump(
    HWND hDialogWnd
    )
{
    MSG Msg;
    BOOL fGotMessage;

    if ((fGotMessage = PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))) 
    {
        if (!IsDialogMessage(hDialogWnd, &Msg)) 
        {
            TranslateMessage(&Msg);
            DispatchMessage(&Msg);
        }
    }

    return fGotMessage;
}


void
ScanAbortThread(
    CleanupMgrInfo *pcmi
    )
{
    if ((pcmi->hAbortScanWnd = CreateDialogParam(g_hInstance, MAKEINTRESOURCE(IDD_SCAN_ABORT),
        NULL, ScanAbortDlgProc, (LPARAM)pcmi)) == NULL)
    {
        return;
    }   

    // Show the window (except when the /SETUP flag is specified)
    if (!(pcmi->dwUIFlags & FLAG_SETUP))
        ShowWindow(pcmi->hAbortScanWnd, SW_SHOW);

    // Trigger the event so we can continue with the scan.
    // If this is triggered from WM_INITDIALOG it can move too quickly
    // and will end up sending a message to NULL instead of the scan abort window
    // because the hwnd doesn't get set until CreateDialogParam returns.
    SetEvent(pcmi->hAbortScanEvent);
    
    //
    //Keep spinning till the Scan is stopped
    //
    while (!(pcmi->bAbortScan))
    {
        MessagePump(pcmi->hAbortScanWnd);
    }

    //
    //Destroy the Abort Scan dialog
    //
    if (pcmi->hAbortScanWnd != NULL)
    {
        DestroyWindow(pcmi->hAbortScanWnd);
        pcmi->hAbortScanWnd = NULL;
    }
}

INT_PTR CALLBACK
PurgeAbortDlgProc(
    HWND hDlg,
    UINT Message,
    WPARAM wParam,
    LPARAM lParam
    )
{
    CleanupMgrInfo  *pcmi;
    DWORD           dwCurrent;

    switch(Message)
    {
        case WM_INITDIALOG:
            SetWindowLongPtr (hDlg, DWLP_USER, 0L);

            //   
            //Get the CleanupMgrInfo
            //
            pcmi = (CleanupMgrInfo *)lParam;    
            if (pcmi == NULL)
            {   
                //Error - passed in invalid CleanupMgrInfo info
                return FALSE;
            }       

            //
            //Save pointer to CleanupMgrInfo object
            //
            SetWindowLongPtr(hDlg, DWLP_USER, lParam);

            TCHAR * psz;
            psz = SHFormatMessage( MSG_PURGE_ABORT_TEXT, pcmi->szVolName, pcmi->szRoot[0]);
            SetDlgItemText (hDlg, IDC_PURGE_TEXT, psz);
            LocalFree(psz);

            //
            //Set the limits on the progress bar
            //
            if (pcmi->cbProgressDivider.QuadPart != 0)
                dwCurrent = (DWORD)(pcmi->cbSpaceToPurge.QuadPart / pcmi->cbProgressDivider.QuadPart);
            else
                dwCurrent = (DWORD)(pcmi->cbSpaceToPurge.QuadPart);

            SendDlgItemMessage(hDlg, IDC_ABORT_PURGE_PROGRESS, PBM_SETRANGE,
                0, MAKELPARAM(0, dwCurrent));

            break;

        case WMAPP_UPDATESTATUS:
            if (lParam != NULL)
                SetDlgItemText(hDlg, IDC_PURGE_STATUS_TEXT, (LPTSTR)lParam);
            else
                SetDlgItemText(hDlg, IDC_PURGE_STATUS_TEXT, TEXT(""));
            break;

        case WMAPP_UPDATEPROGRESS:
            pcmi = (CleanupMgrInfo *)GetWindowLongPtr (hDlg, DWLP_USER);
            if (pcmi != NULL)
            {
                if (pcmi->cbProgressDivider.QuadPart != 0)
                    dwCurrent = (DWORD)((pcmi->cbTotalPurgedSoFar.QuadPart +
                        pcmi->cbCurrentClientPurgedSoFar.QuadPart) /
                        pcmi->cbProgressDivider.QuadPart);
                else
                    dwCurrent = (DWORD)(pcmi->cbTotalPurgedSoFar.QuadPart +
                        pcmi->cbCurrentClientPurgedSoFar.QuadPart);

                SendDlgItemMessage(hDlg, IDC_ABORT_PURGE_PROGRESS, PBM_SETPOS,
                    (WPARAM)dwCurrent, 0);
            }
            break;

        case WM_CLOSE:
        case WM_COMMAND:
            pcmi = (CleanupMgrInfo *)GetWindowLongPtr (hDlg, DWLP_USER);
            if (pcmi != NULL)
                pcmi->bAbortPurge = TRUE;
            break;

        default:
            return FALSE;
    }

    return TRUE;
}

void
PurgeAbortThread(
    CleanupMgrInfo *pcmi
    )
{
    if ((pcmi->hAbortPurgeWnd = CreateDialogParam(g_hInstance, MAKEINTRESOURCE(IDD_PURGE_ABORT),
        NULL, PurgeAbortDlgProc, (LPARAM)pcmi)) == NULL)
    {
        return;
    }   

    // Show the window (except when the /SETUP flag is specified)
    if (!(pcmi->dwUIFlags & FLAG_SETUP))
        ShowWindow(pcmi->hAbortPurgeWnd, SW_SHOW);

    // Make sure the HWND has been set before setting off the event
    PulseEvent(pcmi->hAbortPurgeEvent);

    //
    //Keep spinning till the Purge is stopped
    //
    while (!(pcmi->bAbortPurge))
    {
        MessagePump(pcmi->hAbortPurgeWnd);
    }

    //
    //Destroy the Abort Purge dialog
    //
    if (pcmi->hAbortPurgeWnd != NULL)
    {
        DestroyWindow(pcmi->hAbortPurgeWnd);
        pcmi->hAbortPurgeWnd = NULL;
    }
}
/*
**------------------------------------------------------------------------------
** End of File
**------------------------------------------------------------------------------
*/
