///////////////////////////////////////////////////////////////////////////////
/*  File: volprop.cpp

    Description: Provides implementations for quota property pages.


    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
    08/01/97    Removed IDC_CBX_WARN_THRESHOLD from UI.              BrianAu
    11/27/98    Added logging checkboxes back in.                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
#include "pch.h"
#pragma hdrstop

#include "dskquota.h"
#include "volprop.h"
#include "uihelp.h"
#include "registry.h"
#include "guidsp.h"
#include "uiutils.h"

#ifdef POLICY_MMC_SNAPIN
#   include "snapin.h"
#endif

//
// Context help IDs.
//
#pragma data_seg(".text", "CODE")
const static DWORD rgVolumePropPageHelpIDs[] =
{
    IDC_TRAFFIC_LIGHT,          IDH_TRAFFIC_LIGHT,
    IDC_TXT_QUOTA_STATUS,       IDH_TXT_QUOTA_STATUS,
    IDC_TXT_QUOTA_STATUS_LABEL, DWORD(-1),
    IDC_CBX_ENABLE_QUOTA,       IDH_CBX_ENABLE_QUOTA,
    IDC_CBX_DENY_LIMIT,         IDH_CBX_DENY_LIMIT,
    IDC_RBN_DEF_NOLIMIT,        IDH_RBN_DEF_NO_LIMIT,
    IDC_RBN_DEF_LIMIT,          IDH_RBN_DEF_LIMIT,
    IDC_EDIT_DEF_LIMIT,         IDH_EDIT_DEF_LIMIT,
    IDC_EDIT_DEF_THRESHOLD,     IDH_EDIT_DEF_THRESHOLD,
    IDC_CMB_DEF_LIMIT,          IDH_CMB_DEF_LIMIT,
    IDC_CMB_DEF_THRESHOLD,      IDH_CMB_DEF_THRESHOLD,
    IDC_BTN_DETAILS,            IDH_BTN_DETAILS,
    IDC_BTN_EVENTLOG,           IDH_BTN_EVENTLOG,
    IDC_CBX_LOG_OVERWARNING,    IDH_CBX_LOG_OVERWARNING,
    IDC_CBX_LOG_OVERLIMIT,      IDH_CBX_LOG_OVERLIMIT,
    IDC_TXT_DEFAULTS,           IDH_GRP_DEFAULTS,
    IDC_TXT_LOGGING,            DWORD(-1),
    IDC_TXT_WARN_LEVEL,         DWORD(-1),
    0,0
};


#pragma data_seg()

#ifdef POLICY_MMC_SNAPIN
const TCHAR c_szSnapInPrefs[] = TEXT("GPTEditorExtData");
#endif

extern TCHAR c_szWndClassDetailsView[]; // defined in details.cpp

/*
// NOTE:  This code has been disabled.
//          I've left in case we decide to launch the event viewer from
//          the volume prop page again. [brianau - 3/23/98]
//
const TCHAR c_szVerbOpen[]          = TEXT("Open");
const TCHAR c_szManagementConsole[] = TEXT("MMC.EXE");
const TCHAR c_szMMCInitFile[]       = TEXT("%SystemRoot%\\System32\\EVENTVWR.MSC");
*/


#define VPPM_FOCUS_ON_THRESHOLDEDIT  (WM_USER + 1)


///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::VolumePropPage

    Description: Constructor for a volume property page object.
        Initializes the members that hold volume quota data.

    Arguments: None.

    Returns: Nothing.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
VolumePropPage::VolumePropPage(VOID)
    : m_dwQuotaState(0),
      m_dwQuotaLogFlags(0),
      m_idStatusUpdateTimer(0),
      m_dwLastStatusMsgID(0),
      m_cVolumeMaxBytes(NOLIMIT),
      m_pxbDefaultLimit(NULL),
      m_pxbDefaultThreshold(NULL),
      m_llDefaultQuotaThreshold(0),
      m_llDefaultQuotaLimit(0),
      m_idCtlNextFocus(-1)
{

}


///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::~VolumePropPage

    Description: Destructor for a volume property page object.

    Arguments: None.

    Returns: Nothing.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
VolumePropPage::~VolumePropPage(
    VOID
    )
{
    delete m_pxbDefaultLimit;
    delete m_pxbDefaultThreshold;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::DlgProc

    Description: Static method called by windows to process messages for the
        property page dialog.  Since it's static, we have to save the "this"
        pointer in the window's USERDATA.

    Arguments: Standard WndProc-type arguments.

    Returns: Standard WndProc-type return values.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR APIENTRY
VolumePropPage::DlgProc(
    HWND hDlg,
    UINT message,
    WPARAM wParam,
    LPARAM lParam
    )
{
    INT_PTR bResult = FALSE;

    //
    // Retrieve the "this" pointer from the dialog's userdata.
    // It was placed there in OnInitDialog().
    //
    VolumePropPage *pThis = (VolumePropPage *)GetWindowLongPtr(hDlg, DWLP_USER);

    try
    {
        switch(message)
        {
            case WM_INITDIALOG:
            {
                DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_INITDIALOG")));
                PROPSHEETPAGE *pPage = (PROPSHEETPAGE *)lParam;
                pThis = (VolumePropPage *)pPage->lParam;

                DBGASSERT((NULL != pThis));
                //
                // pThis pointer AddRef'd in AddPages().
                // Save it in the window's userdata.
                //
                SetWindowLongPtr(hDlg, DWLP_USER, (INT_PTR)pThis);
                bResult = pThis->OnInitDialog(hDlg, wParam, lParam);
                break;
            }

            case WM_SYSCOLORCHANGE:
                bResult = pThis->m_TrafficLight.ForwardMessage(message, wParam, lParam);
                break;

            case WM_NOTIFY:
                DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_NOTIFY")));
                bResult = pThis->OnNotify(hDlg, wParam, lParam);
                break;

            case WM_COMMAND:
                DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_COMMAND")));
                bResult = pThis->OnCommand(hDlg, wParam, lParam);
                break;

            case WM_HELP:
                DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_HELP")));
                bResult = pThis->OnHelp(hDlg, wParam, lParam);
                break;

            case WM_CONTEXTMENU:
                bResult = pThis->OnContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam));
                break;

            case WM_DESTROY:
                DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_DESTROY")));
                pThis->KillStatusUpdateTimer(hDlg);
                //
                // Nothing to do.
                //
                break;

            case WM_TIMER:
                DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_TIMER")));
                bResult = pThis->OnTimer(hDlg, wParam, lParam);
                break;

            case VPPM_FOCUS_ON_THRESHOLDEDIT:
                //
                // This is sort of a hack because of the way the prop sheet
                // code in comctl32 sets focus after a page has returned
                // PSNRET_INVALID.  It automatically activates the problem
                // page and sets focus to the FIRST control in the tab order.
                // Since the only failure we generate is from the threshold
                // exceeding the limit, I want to return focus to the threshold
                // edit control so that the user can directly change the offending
                // value.  Posting this custom message was the only way I
                // could get this to work. [brianau].
                //
                SetFocus((HWND)lParam);
                SendMessage((HWND)lParam, EM_SETSEL, 0, -1);
                break;

            default:
                break;
        }
    }
    catch(CAllocException& me)
    {
        //
        // Announce any out-of-memory errors associated with running the
        // volume Quota property page.
        //
        DiskQuotaMsgBox(GetDesktopWindow(),
                        IDS_OUTOFMEMORY,
                        IDS_TITLE_DISK_QUOTA,
                        MB_ICONERROR | MB_OK);
    }

    return bResult;
}






///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::OnInitDialog

    Description: Handler for WM_INITDIALOG.

    Arguments:
        hDlg - Dialog window handle.

        wParam - Handle of control to receive focus if we return FALSE.

        lParam - Pointer to PROPSHEETPAGE structure for the property page.

    Returns:
        TRUE  = Tells windows to assign focus to the control in wParam.

    Exceptions: OutOfMemory.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
    02/10/98    Converted from static to a virtual function to       BrianAu
                support addition of SnapInVolPropPage class.
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
VolumePropPage::OnInitDialog(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    HRESULT hResult = NO_ERROR;
    DWORD dwSectorsPerCluster = 0;
    DWORD dwBytesPerSector    = 0;
    DWORD dwFreeClusters      = 0;
    DWORD dwTotalClusters     = 0;

    //
    // Load the volume's quota information into member variables.
    //
    hResult = RefreshCachedVolumeQuotaInfo();

    //
    // Calculate the volume's size.
    // We'll use this to limit user threshold and quota limit entries.
    //
    if (GetDiskFreeSpace(m_idVolume.ForParsing(),
                         &dwSectorsPerCluster,
                         &dwBytesPerSector,
                         &dwFreeClusters,
                         &dwTotalClusters))
    {
        m_cVolumeMaxBytes = (UINT64)dwSectorsPerCluster *
                            (UINT64)dwBytesPerSector *
                            (UINT64)dwTotalClusters;
    }

    //
    // Create the XBytes objects to manage the relationship between the
    // limit/threshold edit controls and their combo boxes.
    //
    m_pxbDefaultLimit     = new XBytes(hDlg,
                                       IDC_EDIT_DEF_LIMIT,
                                       IDC_CMB_DEF_LIMIT,
                                       m_llDefaultQuotaLimit);
    m_pxbDefaultThreshold = new XBytes(hDlg,
                                       IDC_EDIT_DEF_THRESHOLD,
                                       IDC_CMB_DEF_THRESHOLD,
                                       m_llDefaultQuotaThreshold);

    m_TrafficLight.Initialize(GetDlgItem(hDlg, IDC_TRAFFIC_LIGHT), IDR_AVI_TRAFFIC);

    InitializeControls(hDlg);

    return TRUE;  // Set focus to default control.
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::OnCommand

    Description: Handler for WM_COMMAND.

    Arguments:
        hDlg - Dialog window handle.

        wParam - ID of selected control and notification code.

        lParam - HWND of selected control.

    Returns:
        TRUE  = Message wasn't handled.
        FALSE = Message was handled.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
    08/01/97    Removed IDC_CBX_WARN_THRESHOLD from UI.              BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
VolumePropPage::OnCommand(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    INT_PTR bResult = TRUE;
    DWORD dwCtlId        = LOWORD(wParam);
    HWND hWndCtl         = (HWND)lParam;
    DWORD dwNotifyCode   = HIWORD(wParam);
    BOOL bIsChecked      = FALSE;
    BOOL bEnableApplyBtn = FALSE;

    switch(dwCtlId)
    {
        case IDC_CBX_ENABLE_QUOTA:
        {
            //
            // This is executed when the user checks or unchecks the
            // "Enable quota management checkbox.
            //
            bIsChecked = IsDlgButtonChecked(hDlg, IDC_CBX_ENABLE_QUOTA);
            //
            // Remember: Limit/Threshold Edit and combo boxes are enabled/disabled
            //           through XBytes::SetBytes().
            //
            m_pxbDefaultLimit->SetBytes(m_llDefaultQuotaLimit);
            m_pxbDefaultThreshold->SetBytes(m_llDefaultQuotaThreshold);

            CheckDlgButton(hDlg, IDC_RBN_DEF_NOLIMIT, NOLIMIT == m_pxbDefaultLimit->GetBytes());
            CheckDlgButton(hDlg, IDC_RBN_DEF_LIMIT,   BST_CHECKED != IsDlgButtonChecked(hDlg, IDC_RBN_DEF_NOLIMIT));
            CheckDlgButton(hDlg, IDC_CBX_DENY_LIMIT,  bIsChecked && DISKQUOTA_IS_ENFORCED(m_dwQuotaState));
            CheckDlgButton(hDlg,
                           IDC_CBX_LOG_OVERWARNING,
                           bIsChecked &&
                           DISKQUOTA_IS_LOGGED_USER_THRESHOLD(m_dwQuotaLogFlags));
            CheckDlgButton(hDlg,
                           IDC_CBX_LOG_OVERLIMIT,
                           bIsChecked &&
                           DISKQUOTA_IS_LOGGED_USER_LIMIT(m_dwQuotaLogFlags));

            EnableControls(hDlg);

            bEnableApplyBtn = TRUE;
            bResult = FALSE;
            break;
        }

        case IDC_CBX_DENY_LIMIT:
            bResult = FALSE;
            bEnableApplyBtn = TRUE;
            break;

        case IDC_RBN_DEF_NOLIMIT:
            DBGASSERT((IsDlgButtonChecked(hDlg, IDC_CBX_ENABLE_QUOTA)));

            if (m_pxbDefaultLimit->IsEnabled())
            {
                m_pxbDefaultThreshold->SetBytes(NOLIMIT);
                m_pxbDefaultLimit->SetBytes(NOLIMIT);
                bEnableApplyBtn = TRUE;
            }
            bResult = FALSE;
            break;

        case IDC_RBN_DEF_LIMIT:
            //
            // If the original threshold was -1 (no limit), set to 0.
            // Otherwise, set to the original value.
            //
            DBGASSERT((IsDlgButtonChecked(hDlg, IDC_CBX_ENABLE_QUOTA)));
            if (!m_pxbDefaultLimit->IsEnabled())
            {
                m_pxbDefaultLimit->SetBytes(NOLIMIT == m_llDefaultQuotaLimit ?
                                            0 : m_llDefaultQuotaLimit);
                m_pxbDefaultThreshold->SetBytes(NOLIMIT == m_llDefaultQuotaThreshold ?
                                                0 : m_llDefaultQuotaThreshold);


                EnableControls(hDlg);
                bEnableApplyBtn = TRUE;
            }
            bResult = FALSE;
            break;

        case IDC_EDIT_DEF_LIMIT:
        case IDC_EDIT_DEF_THRESHOLD:
            switch(dwNotifyCode)
            {
                case EN_UPDATE:
                    DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc; WM_COMMAND, EN_CHANGE")));
                    bResult = OnEditNotifyUpdate(hDlg, wParam, lParam);
                    bEnableApplyBtn = TRUE;
                    break;

                case EN_KILLFOCUS:
                    DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc; WM_COMMAND, EN_KILLFOCUS")));
                    bResult = OnEditNotifyKillFocus(hDlg, wParam, lParam);
                    break;

                case EN_SETFOCUS:
                    DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc; WM_COMMAND, EN_SETFOCUS")));
                    bResult = OnEditNotifySetFocus(hDlg, wParam, lParam);
                    break;

                default:
                    break;
            }
            break;

        case IDC_CMB_DEF_LIMIT:
        case IDC_CMB_DEF_THRESHOLD:
            switch(dwNotifyCode)
            {
                case CBN_SELCHANGE:
                    DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_COMMAND, CBN_CHANGE")));
                    bResult = OnComboNotifySelChange(hDlg, wParam, lParam);
                    bEnableApplyBtn = TRUE;
                    break;

                default:
                    break;
            }
            break;

        case IDC_BTN_DETAILS:
            bResult = OnButtonDetails(hDlg, wParam, lParam);
            break;

        case IDC_CBX_LOG_OVERLIMIT:
        case IDC_CBX_LOG_OVERWARNING:
            bEnableApplyBtn = TRUE;
            break;

/*
//
// NOTE: This code disabled until we decide to launch the event viewer
//         from the volume prop page.  Probably won't happen because we
//         can't define a quota-specific error type for NT events.
//         If we can't filter an event viewer list on quota-only events,
//         there's not much use in getting to the event viewer from here.
//         [brianau - 3/23/98]
//
        case IDC_BTN_EVENTLOG:
            bResult = OnButtonEventLog(hDlg, wParam, lParam);
            break;
*/

        default:
            break;
    }

    if (bEnableApplyBtn)
        PropSheet_Changed(GetParent(hDlg), hDlg);

    return bResult;
}


///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::OnNotify

    Description: Handler for WM_NOTIFY.

    Arguments:
        hDlg - Dialog window handle.

        wParam - ID of selected control and notification code.

        lParam - HWND of selected control.

    Returns:
        TRUE  = Message wasn't handled.
        FALSE = Message was handled.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
VolumePropPage::OnNotify(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    DBGTRACE((DM_VPROP, DL_MID, TEXT("VolumePropPage::OnNotify")));
    INT_PTR bResult = TRUE;

    switch(((NMHDR *)lParam)->code)
    {
        case PSN_SETACTIVE:
            DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_NOTIFY, PSN_SETACTIVE")));
            bResult = OnSheetNotifySetActive(hDlg, wParam, lParam);
            break;

        case PSN_APPLY:
            DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_NOTIFY, PSN_APPLY")));
            bResult = OnSheetNotifyApply(hDlg, wParam, lParam);
            break;

        case PSN_KILLACTIVE:
            DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_NOTIFY, PSN_KILLACTIVE")));
            bResult = OnSheetNotifyKillActive(hDlg, wParam, lParam);
            break;

        case PSN_RESET:
            DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_NOTIFY, PSN_RESET")));
            bResult = OnSheetNotifyReset(hDlg, wParam, lParam);
            break;

        default:
            break;
    }
    return bResult;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::OnSheetNotifySetActive

    Description: Handler for WM_NOTIFY - PSN_SETACTIVE.

    Arguments:
        hDlg - Dialog window handle.

        wParam - ID of control.

        lParam - Address of NMHDR structure.

    Returns:
        FALSE = Accept activation.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
VolumePropPage::OnSheetNotifySetActive(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    DBGTRACE((DM_VPROP, DL_HIGH, TEXT("VolumePropPage::OnSheetNotifySetActive")));

    //
    // Update the status text and set the status update timer.
    //
    UpdateStatusIndicators(hDlg);
    SetStatusUpdateTimer(hDlg);

    if (IDC_EDIT_DEF_THRESHOLD == m_idCtlNextFocus)
    {
        //
        // Focus is being set as a result of an invalid entry
        // in the warning level field.  Force input focus to the
        // field and select the entire contents.  User can then just
        // enter a new value.
        //
        PostMessage(hDlg,
                    VPPM_FOCUS_ON_THRESHOLDEDIT,
                    0,
                    (LPARAM)GetDlgItem(hDlg, IDC_EDIT_DEF_THRESHOLD));

        m_idCtlNextFocus = -1;
    }

    return 0;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::OnSheetNotifyApply

    Description: Handler for WM_NOTIFY - PSN_APPLY.

    Arguments:
        hDlg - Dialog window handle.

        wParam - ID of control.

        lParam - Address of NMHDR structure.

    Returns:
        TRUE = PSN return value set using SetWindowLong.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
VolumePropPage::OnSheetNotifyApply(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    DBGTRACE((DM_VPROP, DL_HIGH, TEXT("VolumePropPage::OnSheetNotifyApply")));
    HRESULT hResult  = NO_ERROR;
    LONG dwPSNReturn = PSNRET_NOERROR;
    INT idMsg        = -1;

    if (!IsDlgButtonChecked(hDlg, IDC_CBX_ENABLE_QUOTA) &&
        !DISKQUOTA_IS_DISABLED(m_dwQuotaState))
    {
        idMsg = IDS_DISABLE_QUOTA_WARNING;
    }
    else if (IsDlgButtonChecked(hDlg, IDC_CBX_ENABLE_QUOTA) &&
        DISKQUOTA_IS_DISABLED(m_dwQuotaState))
    {
        idMsg = IDS_ENABLE_QUOTA_WARNING;
    }

    if (-1 != idMsg)
    {
        //
        // User wants to disable or enable quotas.
        // Warn about what this means and let them know that
        // re-activation of quotas requires a quota file rebuild.
        //
        if (IDCANCEL == DiskQuotaMsgBox(hDlg,
                                        idMsg,
                                        IDS_TITLE_DISK_QUOTA,
                                        MB_ICONWARNING | MB_OKCANCEL))
        {
            //
            // User decided to not continue the action.
            // Restore the checkbox to it's previous setting and abort the
            // settings change.
            // Sending the message to our DlgProc resets the dependent controls
            // to their proper states.
            //
            CheckDlgButton(hDlg,
                           IDC_CBX_ENABLE_QUOTA,
                           !IsDlgButtonChecked(hDlg, IDC_CBX_ENABLE_QUOTA));

            SendMessage(hDlg,
                        WM_COMMAND,
                        (WPARAM)MAKELONG((WORD)IDC_CBX_ENABLE_QUOTA, (WORD)0),
                        (LPARAM)GetDlgItem(hDlg, IDC_CBX_ENABLE_QUOTA));

            dwPSNReturn = PSNRET_INVALID;
        }
    }

    if (PSNRET_NOERROR == dwPSNReturn)
    {
        //
        // We need to do this because if you activate the apply button
        // with Alt-A we receive PSN_APPLY before EN_KILLFOCUS.
        //
        m_pxbDefaultThreshold->OnEditKillFocus((LPARAM)GetDlgItem(hDlg, IDC_EDIT_DEF_THRESHOLD));
        m_pxbDefaultLimit->OnEditKillFocus((LPARAM)GetDlgItem(hDlg, IDC_EDIT_DEF_LIMIT));

        //
        // Ensure warning threshold is not above limit.
        //
        INT64 iThreshold = m_pxbDefaultThreshold->GetBytes();
        INT64 iLimit     = m_pxbDefaultLimit->GetBytes();

        if (iThreshold > iLimit)
        {
            TCHAR szLimit[40], szThreshold[40];
            XBytes::FormatByteCountForDisplay(iLimit, szLimit, ARRAYSIZE(szLimit));
            XBytes::FormatByteCountForDisplay(iThreshold, szThreshold, ARRAYSIZE(szThreshold));

            CString s(g_hInstDll, IDS_FMT_ERR_WARNOVERLIMIT, szThreshold, szLimit, szLimit);
            switch(DiskQuotaMsgBox(hDlg, s, IDS_TITLE_DISK_QUOTA, MB_ICONWARNING | MB_YESNO))
            {
                case IDYES:
                    m_pxbDefaultThreshold->SetBytes(iLimit);
                    break;

                case IDNO:
                    m_idCtlNextFocus = IDC_EDIT_DEF_THRESHOLD;
                    dwPSNReturn = PSNRET_INVALID;
                    break;
            }
        }
    }

    if (PSNRET_NOERROR == dwPSNReturn)
    {
        hResult = ApplySettings(hDlg);
        if (FAILED(hResult))
        {
             DiskQuotaMsgBox(hDlg,
                             IDS_APPLY_SETTINGS_ERROR,
                             IDS_TITLE_DISK_QUOTA,
                             MB_ICONERROR | MB_OK);
            dwPSNReturn = PSNRET_INVALID;
            InitializeControls(hDlg);
        }
    }

    SetWindowLongPtr(hDlg, DWLP_MSGRESULT, dwPSNReturn);

    return TRUE;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::OnSheetNotifyKillActive

    Description: Handler for WM_NOTIFY - PSN_KILLACTIVE.

    Arguments:
        hDlg - Dialog window handle.

        wParam - ID of control.

        lParam - Address of NMHDR structure.

    Returns:
        TRUE  = Invalid data entered.  Don't kill page.
        FALSE = All data is valid.  Ok to kill page.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
VolumePropPage::OnSheetNotifyKillActive(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    DBGTRACE((DM_VPROP, DL_HIGH, TEXT("VolumePropPage::OnSheetNotifyKillActive")));
    BOOL bAllDataIsValid = TRUE;

    if (bAllDataIsValid)
    {
        KillStatusUpdateTimer(hDlg);
    }

    SetWindowLongPtr(hDlg, DWLP_MSGRESULT, !bAllDataIsValid);

    //
    // Must release quota controller whenever the sheet is deactivated.
    // Without this we were holding open a handle to the volume.  This prevented
    // the disk check utility ("Tools" page) from accessing the volume.
    // Whenever we need an IDiskQuotaControl ptr we call GetQuotaController which
    // will create a new controller if necessary.
    //
    if (NULL != m_pQuotaControl)
    {
        m_pQuotaControl->Release();
        m_pQuotaControl = NULL;
    }

    return TRUE;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::OnSheetNotifyReset

    Description: Handler for WM_NOTIFY - PSN_RESET.

    Arguments:
        hDlg - Dialog window handle.

        wParam - ID of control.

        lParam - Address of NMHDR structure.

    Returns:
        No return value.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
VolumePropPage::OnSheetNotifyReset(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    DBGTRACE((DM_VPROP, DL_HIGH, TEXT("VolumePropPage::OnSheetNotifyReset")));
    HRESULT hResult = NO_ERROR;

    //
    // Nothing to do right now.
    //

    return FALSE;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::OnHelp

    Description: Handler for WM_HELP.  Displays context sensitive help.

    Arguments:
        lParam - Pointer to a HELPINFO structure.

    Returns: TRUE;

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/17/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
VolumePropPage::OnHelp(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, STR_DSKQUOUI_HELPFILE,
                HELP_WM_HELP, (DWORD_PTR)(LPTSTR) rgVolumePropPageHelpIDs);
    return TRUE;
}


INT_PTR
VolumePropPage::OnContextMenu(
    HWND hwndItem,
    int xPos,
    int yPos
    )
{
    int idCtl = GetDlgCtrlID(hwndItem);
    WinHelp(hwndItem,
            UseWindowsHelp(idCtl) ? NULL : STR_DSKQUOUI_HELPFILE,
            HELP_CONTEXTMENU,
            (DWORD_PTR)((LPTSTR)rgVolumePropPageHelpIDs));

    return FALSE;
}


///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::OnTimer

    Description: Handler for WM_TIMER.  Updates the quota status text and
        traffic light.

    Arguments:
        wParam - Timer ID.

    Returns: FALSE (0);

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/17/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
VolumePropPage::OnTimer(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    if (wParam == m_idStatusUpdateTimer)
    {
        UpdateStatusIndicators(hDlg);
    }

    return FALSE;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::OnEditNotifyUpdate

    Description: Handler for WM_COMMAND, EN_UPDATE.
        Called whenever a character is entered in an edit control.

    Arguments:

    Returns: FALSE;

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/17/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
VolumePropPage::OnEditNotifyUpdate(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    XBytes *rgpxb[2] = { m_pxbDefaultLimit, m_pxbDefaultThreshold };
    const int iLIMIT     = 0;
    const int iTHRESHOLD = 1;
    int iCurrent         = iLIMIT;

    if (IDC_EDIT_DEF_THRESHOLD == LOWORD(wParam))
        iCurrent = iTHRESHOLD;

    if (NULL != rgpxb[iCurrent])
        rgpxb[iCurrent]->OnEditNotifyUpdate(lParam);

    return FALSE;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::OnEditNotifyKillFocus

    Description: Handler for WM_COMMAND, EN_KILLFOCUS.
        Called whenever focus leaves an edit control.
        Validates the value in the edit control and adjusts it if necessary.

    Arguments:

    Returns: FALSE;

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/17/96    Initial creation.                                    BrianAu
    11/12/98    Added code to call XBytes::OnEditKillFocus.          BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
VolumePropPage::OnEditNotifyKillFocus(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    XBytes *rgpxb[2] = { m_pxbDefaultLimit, m_pxbDefaultThreshold };
    const int iLIMIT     = 0;
    const int iTHRESHOLD = 1;
    int iCurrent         = iLIMIT;

    if (IDC_EDIT_DEF_THRESHOLD == LOWORD(wParam))
        iCurrent = iTHRESHOLD;

    if (NULL != rgpxb[iCurrent])
        rgpxb[iCurrent]->OnEditKillFocus(lParam);

    return FALSE;
}


INT_PTR
VolumePropPage::OnEditNotifySetFocus(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    //
    // Nothing to do.
    // FEATURE:  Delete this method?
    //
    return FALSE;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::OnComboNotifySelChange

    Description: Handler for WM_COMMAND, CBN_SELCHANGE.
        Called whenever the user selects the combo box.

    Arguments: Std DlgProc args.

    Returns: FALSE;

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/17/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
VolumePropPage::OnComboNotifySelChange(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    XBytes *rgpxb[2] = { m_pxbDefaultLimit, m_pxbDefaultThreshold };
    const int iLIMIT     = 0;
    const int iTHRESHOLD = 1;
    int iCurrent         = iLIMIT;

    if (IDC_CMB_DEF_THRESHOLD == LOWORD(wParam))
        iCurrent = iTHRESHOLD;

    if (NULL != rgpxb[iCurrent])
        rgpxb[iCurrent]->OnComboNotifySelChange(lParam);

    return FALSE;
}


///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::OnButtonDetails

    Description: Called when the user selects the "Details" button.
        If a details view is already active for this prop page, it is brought
        to the foreground.  If no details view is already active, a new one
        is created.

    Arguments: Standard DlgProc arguments.

    Returns:

    Exceptions: OutOfMemory.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
VolumePropPage::OnButtonDetails(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    if (!ActivateExistingDetailsView())
    {
        //
        // This property page doesn't have an active details view.
        // Create one.  Note:  If something fails in the details view
        // creation, it isn't displayed.  The DetailsView code is
        // responsible for reporting any errors to the user.
        //
        // NOTE:  The VolumePropPage object never calls "delete"
        //        on the pDetailsView pointer.  The details view
        //        object must live on it's own (modeless) after it is created.
        //        If the VolumePropPage object (this object) is still alive
        //        when the details view object is destroyed, it will receive a
        //        WM_DETAILS_VIEW_DESTROYED message from the view object.  That's
        //        why we pass the hDlg in this constructor.  When this message
        //        is received, we set m_pDetailsView to NULL so that OnButtonDetails
        //        will know to create a new view object.
        //
        DetailsView *pDetailsView = new DetailsView;

        if (!pDetailsView->Initialize(m_idVolume))
        {
            //
            // Something failed.  Either out of memory or the view's thread
            // couldn't start.  Either way, the view won't run.
            // Need to call delete to clean up any partially-completed initialization.
            //
            delete pDetailsView;
        }
    }
    return FALSE;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::OnButtonEventLog

    Description: Called when the user selects the "Event Log" button.
        Invokes the NT Event Viewer application.

    Arguments: Standard DlgProc arguments.

    Returns:

    Exceptions: OutOfMemory.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    10/14/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
#ifdef __NEVER__
//
// NOTE:  This code has been disabled.  I just left it in case we ever
//          decide to launch the event viewer from the volume prop page again.
//          [brianan - 3/23/98]
//
/*
INT_PTR
VolumePropPage::OnButtonEventLog(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    //
    // Expand the %SystemRoot% env var in the path to the MMC
    // initialization file (Event viewer).
    //
    CString strMMCInitFile(c_szMMCInitFile);
    strMMCInitFile.ExpandEnvironmentStrings();

    HINSTANCE SEhInstance = ShellExecute(hDlg,
                                c_szVerbOpen,
                                c_szManagementConsole,
                                strMMCInitFile,
                                NULL,
                                SW_SHOWNORMAL);

    if ((DWORD)SEhInstance <= 32)
    {
        //
        // Something failed.
        //
        INT iMsg = 0;
        switch((DWORD)SEhInstance)
        {
            case 0:
                throw CAllocException();
                break;

            case ERROR_FILE_NOT_FOUND:
                iMsg = IDS_ERROR_FILE_NOT_FOUND;
                break;

            case ERROR_BAD_FORMAT:
                iMsg = IDS_ERROR_FILE_CORRUPT;
                break;

            case SE_ERR_DDEBUSY:
            case SE_ERR_DDEFAIL:
            case SE_ERR_DDETIMEOUT:
                iMsg = IDS_ERROR_DDE_EXECUTE;
                break;

            default:
                //
                // The rest of the ShellExecute errors shouldn't happen
                // in this situation.
                //
                break;
        }

        if (0 != iMsg)
        {
            //
            // Display error message.
            // Note that this assumes error string resources have a %1 embedded
            // for displaying the filename.  If they don't, the following
            // logic will need to be changed.
            //
            CString strMsg(g_hInstDll, iMsg, c_szManagementConsole);
            DiskQuotaMsgBox(GetDesktopWindow(),
                            strMsg,
                            IDS_TITLE_DISK_QUOTA,
                            MB_ICONERROR | MB_OK);
        }
    }
    return 0;
}
*/
#endif // __NEVER__

///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::ActivateExistingDetailsView

    Description: Called by OnButtonDetails to see if there's already a details
                 view active for this volume.  If there is, open it.

    Arguments: None.

    Returns:
        TRUE  = Existing details view was found and promoted to the foreground.
        FALSE = Either no existing view was found or an existing one could
                not be promoted to the foreground.

    Exceptions: OutOfMemory.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    02/25/97    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
BOOL
VolumePropPage::ActivateExistingDetailsView(
    VOID
    ) const
{
    BOOL bResult = FALSE;
    CString strVolDisplayName;
    DetailsView::CreateVolumeDisplayName(m_idVolume, &strVolDisplayName);

    CString strDetailsViewTitle(g_hInstDll, IDS_TITLE_MAINWINDOW, (LPCTSTR)strVolDisplayName);

    HWND hwndDetailsView = FindWindow(c_szWndClassDetailsView,
                                      strDetailsViewTitle);

    if (NULL != hwndDetailsView)
    {
        //
        // Restore the details view and bring it to the front.
        //
        ShowWindow(hwndDetailsView, SW_RESTORE);
        bResult = SetForegroundWindow(hwndDetailsView);
    }

    return bResult;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::ApplySettings

    Description: Applies the current settings to the volume if they have
        changed from the original settings.

    Arguments:
        hDlg - Dialog window handle.

    Returns:
        NO_ERROR            - Success.
        E_INVALIDARG        - One of the settings was invalid.
        ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device.
        E_FAIL              - Any other error.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
VolumePropPage::ApplySettings(
    HWND hDlg
    )
{
    HRESULT hResult         = NO_ERROR;
    DWORD dwStateSetting    = 0;
    DWORD dwLogFlagSettings = m_dwQuotaLogFlags;
    BOOL  bTranslated       = FALSE;
    LONGLONG llThreshold;
    LONGLONG llLimit;
    IDiskQuotaControl *pqc;

    hResult = GetQuotaController(&pqc);
    if (SUCCEEDED(hResult))
    {
        //
        // Set quota state if changed.
        //
        QuotaStateFromControls(hDlg, &dwStateSetting);
        if (dwStateSetting != (m_dwQuotaState & DISKQUOTA_STATE_MASK))
        {
            hResult = pqc->SetQuotaState(dwStateSetting);
            if (FAILED(hResult))
                goto apply_failed;

            m_dwQuotaState = dwStateSetting;
        }

        //
        // Set quota log flags if changed.
        //
        LogFlagsFromControls(hDlg, &dwLogFlagSettings);
        if (dwLogFlagSettings != m_dwQuotaLogFlags)
        {
            hResult = pqc->SetQuotaLogFlags(dwLogFlagSettings);
            if (FAILED(hResult))
                goto apply_failed;

            m_dwQuotaLogFlags = dwLogFlagSettings;
        }

        //
        // Get current default quota threshold and limit values.
        //
        if (IsDlgButtonChecked(hDlg, IDC_RBN_DEF_NOLIMIT))
        {
            llThreshold = NOLIMIT;
            llLimit     = NOLIMIT;
        }
        else
        {
            llThreshold = m_pxbDefaultThreshold->GetBytes();
            llLimit     = m_pxbDefaultLimit->GetBytes();
        }

        //
        // Set default quota threshold if changed.
        //
        if (llThreshold != m_llDefaultQuotaThreshold)
        {
            hResult = pqc->SetDefaultQuotaThreshold(llThreshold);
            if (FAILED(hResult))
                goto apply_failed;

            m_llDefaultQuotaThreshold = llThreshold;
        }

        //
        // Set default quota limit if changed.
        //
        if (llLimit != m_llDefaultQuotaLimit)
        {
            hResult = pqc->SetDefaultQuotaLimit(llLimit);
            if (FAILED(hResult))
                goto apply_failed;

            m_llDefaultQuotaLimit = llLimit;
        }

apply_failed:

        pqc->Release();
    }
    return hResult;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::RefreshCachedVolumeInfo

    Description: Reads the volume's quota information and stores it in
        member variables.

    Arguments: None.

    Returns:
        NO_ERROR            - Success.
        ERROR_ACCESS_DENIED (hr) - No READ access to quota device.
        E_FAIL              - Any other error.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
VolumePropPage::RefreshCachedVolumeQuotaInfo(
    VOID
    )
{
    HRESULT hResult = NO_ERROR;

    IDiskQuotaControl *pqc;

    hResult = GetQuotaController(&pqc);
    if (SUCCEEDED(hResult))
    {
        //
        // Read quota state.
        //
        hResult = pqc->GetQuotaState(&m_dwQuotaState);
        if (FAILED(hResult))
            goto refresh_vol_info_failed;

        //
        // Read quota log flags.
        //
        hResult = pqc->GetQuotaLogFlags(&m_dwQuotaLogFlags);
        if (FAILED(hResult))
            goto refresh_vol_info_failed;

        //
        // Read default quota threshold.
        //
        hResult = pqc->GetDefaultQuotaThreshold(&m_llDefaultQuotaThreshold);
        if (FAILED(hResult))
            goto refresh_vol_info_failed;

        //
        // Read default quota limit.
        //
        hResult = pqc->GetDefaultQuotaLimit(&m_llDefaultQuotaLimit);

refresh_vol_info_failed:

        pqc->Release();
    }

    return hResult;
}


//
// Determine if a given disk quota policy value is set.
//
bool
VolumePropPage::SetByPolicy(
    LPCTSTR pszPolicyValue
    )
{
    DWORD dwData;
    DWORD dwType;
    DWORD cbData = sizeof(dwData);

    return (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE,
                                        REGSTR_KEY_POLICYDATA,
                                        pszPolicyValue,
                                        &dwType,
                                        &dwData,
                                        &cbData));
}                                         


HRESULT
VolumePropPage::EnableControls(
    HWND hwndDlg
    )
{    
    BOOL bQuotaEnabled = (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_CBX_ENABLE_QUOTA));
    BOOL bEnable;

    //
    // "Enable quota management" checkbox.
    //
    //  Policy   Quota Enabled     Ctl Enabled
    //    0            0                1
    //    0            1                1
    //    1            0                0
    //    1            1                0
    //
    EnableWindow(GetDlgItem(hwndDlg, IDC_CBX_ENABLE_QUOTA), 
                 !SetByPolicy(REGSTR_VAL_POLICY_ENABLE));
    //
    // "Deny disk space..." checkbox.
    //
    //  Policy   Quota Enabled     Ctl Enabled
    //    0            0                0
    //    0            1                1
    //    1            0                0
    //    1            1                0
    //
    EnableWindow(GetDlgItem(hwndDlg, IDC_CBX_DENY_LIMIT), 
                 bQuotaEnabled && !SetByPolicy(REGSTR_VAL_POLICY_ENFORCE));
    //                 
    // Log event checkboxes
    //
    //  Policy   Quota Enabled     Ctl Enabled
    //    0            0                0
    //    0            1                1
    //    1            0                0
    //    1            1                0
    //
    EnableWindow(GetDlgItem(hwndDlg, IDC_CBX_LOG_OVERLIMIT),
                 bQuotaEnabled && !SetByPolicy(REGSTR_VAL_POLICY_LOGLIMIT));

    EnableWindow(GetDlgItem(hwndDlg, IDC_CBX_LOG_OVERWARNING),
                 bQuotaEnabled && !SetByPolicy(REGSTR_VAL_POLICY_LOGTHRESHOLD));

    //
    // "Do not limit disk usage" radio button
    // "Limit disk space to" radio button
    //
    //  Policy    Quota Enabled     No Limit    Ctl Enabled
    //    0            0               0            0
    //    0            0               1            0
    //    0            1               0            0
    //    0            1               1            1
    //    1            0               0            0
    //    1            0               1            0
    //    1            1               0            0
    //    1            1               1            0
    //
    bEnable = bQuotaEnabled && !SetByPolicy(REGSTR_VAL_POLICY_LIMIT);
              
    EnableWindow(GetDlgItem(hwndDlg, IDC_RBN_DEF_NOLIMIT),    bEnable);
    EnableWindow(GetDlgItem(hwndDlg, IDC_RBN_DEF_LIMIT),      bEnable);
    //
    //  "Limit disk space" edit and combo controls.
    //
    //  Policy    Quota Enabled     No Limit    Ctl Enabled
    //    0            0               0            0
    //    0            0               1            0
    //    0            1               0            1
    //    0            1               1            0
    //    1            0               0            0
    //    1            0               1            0
    //    1            1               0            0
    //    1            1               1            0
    //
    bEnable = bQuotaEnabled && 
              !SetByPolicy(REGSTR_VAL_POLICY_LIMIT) &&
              NOLIMIT != m_pxbDefaultLimit->GetBytes();

    EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_DEF_LIMIT),     bEnable);
    EnableWindow(GetDlgItem(hwndDlg, IDC_CMB_DEF_LIMIT),      bEnable);

    bEnable = bQuotaEnabled && 
              !SetByPolicy(REGSTR_VAL_POLICY_THRESHOLD) &&
              NOLIMIT != m_pxbDefaultThreshold->GetBytes();

    EnableWindow(GetDlgItem(hwndDlg, IDC_TXT_WARN_LEVEL),     bEnable);
    EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_DEF_THRESHOLD), bEnable);
    EnableWindow(GetDlgItem(hwndDlg, IDC_CMB_DEF_THRESHOLD),  bEnable);
    //
    // Miscellaneous text controls.
    //
    //  Quota Enabled     Ctl Enabled
    //        0                0
    //        1                1
    //
    EnableWindow(GetDlgItem(hwndDlg, IDC_TXT_DEFAULTS), bQuotaEnabled);
    EnableWindow(GetDlgItem(hwndDlg, IDC_TXT_LOGGING),  bQuotaEnabled);

    return NOERROR;
}


///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::InitializeControls

    Description: Initializes the page controls based on the volume's
        quota settings.

    Arguments:
        hDlg - Dialog window handle.

    Returns:
        NO_ERROR - Always returns NO_ERROR.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
    08/01/97    Removed IDC_CBX_WARN_THRESHOLD from UI.              BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
VolumePropPage::InitializeControls(
    HWND hDlg
    )
{
    BOOL bQuotaEnabled  = !(DISKQUOTA_IS_DISABLED(m_dwQuotaState));
    BOOL bUnlimited     = (NOLIMIT == m_llDefaultQuotaLimit);

    CheckDlgButton(hDlg,
                   IDC_CBX_ENABLE_QUOTA,
                   bQuotaEnabled);

    CheckDlgButton(hDlg,
                   IDC_CBX_DENY_LIMIT,
                   DISKQUOTA_IS_ENFORCED(m_dwQuotaState));

    CheckDlgButton(hDlg,
                   IDC_CBX_LOG_OVERWARNING,
                   !DISKQUOTA_IS_DISABLED(m_dwQuotaState) &&
                   DISKQUOTA_IS_LOGGED_USER_THRESHOLD(m_dwQuotaLogFlags));

    CheckDlgButton(hDlg,
                   IDC_CBX_LOG_OVERLIMIT,
                   !DISKQUOTA_IS_DISABLED(m_dwQuotaState) &&
                   DISKQUOTA_IS_LOGGED_USER_LIMIT(m_dwQuotaLogFlags));

    CheckDlgButton(hDlg, IDC_RBN_DEF_NOLIMIT,  bUnlimited);
    CheckDlgButton(hDlg, IDC_RBN_DEF_LIMIT,   !bUnlimited);

    EnableControls(hDlg);

    return NO_ERROR;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::UpdateStatusIndicators
    Description: Updates the "Status" text message at the top of the property
        page according to the actual quota system state.  Also updates the
        traffic light AVI clip.

    Arguments:
        hDlg - Dialog handle.

    Returns:
        Always returns NO_ERROR.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/18/96    Initial creation.                                    BrianAu
    08/28/96    Added stoplight icon.                                BrianAu
    09/10/96    Converted stoplight from an icon to an AVI clip.     BrianAu
                Call it a traffic light now.
    07/14/97    Removed distinct "enforce" and "tracking" messages   BrianAu
                and replaced with a single "active" message.
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
VolumePropPage::UpdateStatusIndicators(
    HWND hDlg
    )
{
    HRESULT hResult = NO_ERROR;
    DWORD dwMsgID   = IDS_STATUS_UNKNOWN;
    IDiskQuotaControl *pqc;

    hResult = GetQuotaController(&pqc);
    if (SUCCEEDED(hResult))
    {
        //
        // Update cached state information.
        //
        hResult = pqc->GetQuotaState(&m_dwQuotaState);
        pqc->Release();
        pqc = NULL;

    }
    if (SUCCEEDED(hResult))
    {
        //
        // Figure out what message to display.
        // "Rebuilding" overrides any other state.
        //
        if (DISKQUOTA_FILE_REBUILDING(m_dwQuotaState))
        {
            dwMsgID = IDS_STATUS_REBUILDING;
        }
        else switch(m_dwQuotaState & DISKQUOTA_STATE_MASK)
        {
            case DISKQUOTA_STATE_DISABLED:
                dwMsgID = IDS_STATUS_DISABLED;
                break;
            case DISKQUOTA_STATE_TRACK:
            case DISKQUOTA_STATE_ENFORCE:
                dwMsgID = IDS_STATUS_ACTIVE;
                break;
            default:
                break;
        }
    }

    if (dwMsgID != m_dwLastStatusMsgID)
    {
        //
        // Format the status text and configure the traffic light.
        //
        // Traffic light states:
        // RED             = Quotas disabled.
        // GREEN           = Quotas enabled.
        // Flashing YELLOW = Quota file is rebuilding.
        //
        INT iTrafficLightState = TrafficLight::GREEN;

        if (DISKQUOTA_FILE_REBUILDING(m_dwQuotaState))
            iTrafficLightState = TrafficLight::FLASHING_YELLOW;
        else if (DISKQUOTA_IS_DISABLED(m_dwQuotaState))
            iTrafficLightState = TrafficLight::RED;

        m_TrafficLight.Show(iTrafficLightState);

        CString strStatus(g_hInstDll, dwMsgID);
        SetWindowText(GetDlgItem(hDlg, IDC_TXT_QUOTA_STATUS), strStatus);

        m_dwLastStatusMsgID = dwMsgID;
        //
        // Re-initialize the controls based on the new state.
        //
        InitializeControls(hDlg);
    }

    return NO_ERROR;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::QuotaStateFromControls

    Description: Determines the quota state from the states of the individual
        controls on the page.

    Arguments:
        hDlg - Dialog's window handle.

        pdwState - Address of DWORD variable to receive state bits.

    Returns:
        Always returns NO_ERROR.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/19/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
VolumePropPage::QuotaStateFromControls(
    HWND hDlg,
    LPDWORD pdwState
    ) const
{
    DBGASSERT((NULL != pdwState));

    //
    // Set quota state if changed.
    //
    if (IsDlgButtonChecked(hDlg, IDC_CBX_ENABLE_QUOTA))
    {
        if (IsDlgButtonChecked(hDlg, IDC_CBX_DENY_LIMIT))
        {
            *pdwState = DISKQUOTA_STATE_ENFORCE;
        }
        else
            *pdwState = DISKQUOTA_STATE_TRACK;
    }
    else
        *pdwState = DISKQUOTA_STATE_DISABLED;

    return NO_ERROR;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::LogFlagsFromControls

    Description: Determines the log flags state from the states of the
        individual controls on the page.

    Arguments:
        hDlg - Dialog's window handle.

        pdwLogFlags - Address of DWORD variable to receive flag bits.

    Returns:
        Always returns NO_ERROR.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/19/96    Initial creation.                                    BrianAu
    08/01/97    Removed IDC_CBX_WARN_THRESHOLD from UI.              BrianAu
    11/20/98    Added "log over limit" and "log over warning"        BrianAu
                controls.
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
VolumePropPage::LogFlagsFromControls(
    HWND hDlg,
    LPDWORD pdwLogFlags
    ) const
{
    DBGASSERT((NULL != pdwLogFlags));
    DISKQUOTA_SET_LOG_USER_LIMIT(*pdwLogFlags,
                                 IsDlgButtonChecked(hDlg, IDC_CBX_LOG_OVERLIMIT));

    DISKQUOTA_SET_LOG_USER_THRESHOLD(*pdwLogFlags,
                                     IsDlgButtonChecked(hDlg, IDC_CBX_LOG_OVERWARNING));

    return NO_ERROR;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::TrafficLight::Initialize

    Description: Initializes the traffic light by opening the AVI clip.

    Arguments:
        hwndAnimateCtl - Handle to the animation control in the dialog.

        idAviClipRes - Resource ID of the AVI clip resource.

    Returns: Nothing.  If the thing doesn't load, it just won't play.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    09/10/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
VOID
VolumePropPage::TrafficLight::Initialize(
    HWND hwndAnimateCtl,
    INT idAviClipRes
    )
{
    DBGASSERT((NULL != hwndAnimateCtl));

    m_hwndAnimateCtl = hwndAnimateCtl;
    m_idAviClipRes   = idAviClipRes;

    Animate_Open(m_hwndAnimateCtl, MAKEINTRESOURCE(idAviClipRes));
//
// See note in TrafficLight::Show below.
//
//    Animate_SetFrameTime(m_hwndAnimateCtl, GetCaretBlinkTime());
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: VolumePropPage::TrafficLight::Show

    Description: Shows the traffic light in one of it's states.

    Arguments:
        eShow - One of the following enumerated constant values:

            OFF, YELLOW, RED, GREEN, FLASHING_YELLOW.

        NOTE:   THIS IS VERY IMPORTANT!!!

                The definitions of these constants MUST match as follows
                with the frame numbers in the AVI clip TRAFFIC.AVI.  If
                you change either, it won't work.

                Frame  Constant         Value
                ------ ---------------- ------
                  0     OFF               0
                  1     YELLOW            1
                  2     RED               2
                  3     GREEN             3
                N/A     FLASHING_YELLOW   4

                Flashing yellow is created by playing frames 0 and 1
                repeatedly.

    Returns: Nothing.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    09/10/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
VOID
VolumePropPage::TrafficLight::Show(
    INT eShow
    )
{
    switch(eShow)
    {
        case OFF:
        case YELLOW:
        case RED:
        case GREEN:
            Animate_Seek(m_hwndAnimateCtl, eShow);
            break;

        case FLASHING_YELLOW:
            Animate_Seek(m_hwndAnimateCtl, YELLOW);
//
// NOTE:
//
// The common control guys didn't want me to add the ACM_SETFRAMETIME
// message so we can't vary the rate of the animation.  Since we can't
// have a fixed-rate blinking control, I'm just fixing the traffic light
// at yellow rather than flashing.  If we can ever add the frame time
// modification message to the animation control, we can activate
// this functionality.  A flashing light isn't worth the trouble of
// a unique implementation.  I really wanted this.  It looks cool.
//
// FEATURE:  If we have time.  Make this work without the animation control.
//          Note that I tried just setting the icon.  But since the volume
//          status checking is done on the same thread that processes the
//          STM_SETICON messgae, flashing of the icon is erratic.
//
//            Animate_Play(m_hwndAnimateCtl, YELLOW, OFF, (UINT)-1);
            break;

        default:
            break;
    }
}


INT_PTR
VolumePropPage::TrafficLight::ForwardMessage(
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    return SendMessage(m_hwndAnimateCtl, uMsg, wParam, lParam);
}

#ifdef POLICY_MMC_SNAPIN

SnapInVolPropPage::~SnapInVolPropPage(
    void
    )
{
    if (NULL != m_pPolicy)
        m_pPolicy->Release();
}


BOOL
SnapInVolPropPage::OnInitDialog(
    HWND hDlg,
    UINT wParam,
    LONG lParam
    )
{
    DBGTRACE((DM_VPROP, DL_HIGH, TEXT("SnapInVolPropPage::OnInitDialog")));
    HRESULT hr = NO_ERROR;
    bool bRemovableMedia = false;

    //
    // Policy object should have already been created and initialized
    // by the creator of the property sheet.  See CSnapInCompData::PropPageThreadProc.
    //
    DBGASSERT((NULL != m_pPolicy));

    //
    // Retrieve and restore the previous dialog state from when the
    // user last used the snapin.  We use the GPTInfo structure for
    // convenience since we're storing the same information that is
    // used for setting policy.
    //
    DISKQUOTAPOLICYINFO dqpi;
    RegKey keyPref(HKEY_CURRENT_USER, REGSTR_KEY_DISKQUOTA);
    if (SUCCEEDED(keyPref.Open(KEY_READ)) &&
        SUCCEEDED(keyPref.GetValue(c_szSnapInPrefs, (LPBYTE)&dqpi, sizeof(dqpi))) &&
        sizeof(dqpi) == dqpi.cb)
    {
        m_llDefaultQuotaLimit     = dqpi.llDefaultQuotaLimit;
        m_llDefaultQuotaThreshold = dqpi.llDefaultQuotaThreshold;
        m_dwQuotaState            = dqpi.dwQuotaState;
        m_dwQuotaLogFlags         = dqpi.dwQuotaLogFlags;
        bRemovableMedia           = dqpi.bRemovableMedia;
    }
    else
        DBGERROR((TEXT("Error loading policy dialog information")));

    //
    // Create the XBytes objects to manage the relationship between the
    // limit/threshold edit controls and their combo boxes.
    //
    m_pxbDefaultLimit     = new XBytes(hDlg,
                                       IDC_EDIT_DEF_LIMIT,
                                       IDC_CMB_DEF_LIMIT,
                                       m_llDefaultQuotaLimit);

    m_pxbDefaultThreshold = new XBytes(hDlg,
                                       IDC_EDIT_DEF_THRESHOLD,
                                       IDC_CMB_DEF_THRESHOLD,
                                       m_llDefaultQuotaThreshold);

    InitializeControls(hDlg);

    return TRUE;  // Set focus to default control.
}


BOOL
SnapInVolPropPage::OnSheetNotifySetActive(
    HWND hDlg,
    UINT wParam,
    LONG lParam
    )
{
    DBGTRACE((DM_VPROP, DL_HIGH, TEXT("SnapInVolPropPage::OnSheetNotifySetActive")));
    return FALSE;
}


BOOL
SnapInVolPropPage::OnSheetNotifyApply(
    HWND hDlg,
    UINT wParam,
    LONG lParam
    )
{
    DBGTRACE((DM_VPROP, DL_HIGH, TEXT("SnapInVolPropPage::OnSheetNotifyApply")));
    LONG dwPSNReturn = PSNRET_NOERROR;
    HRESULT hr = NOERROR;

    DWORD dwState, dwLogFlags;
    LONGLONG llThreshold, llLimit;

    //
    // Policy object should have already been created and initialized
    // by the creator of the property sheet.  See CSnapInCompData::PropPageThreadProc.
    //
    DBGASSERT((NULL != m_pPolicy));

    //
    // Query the state of the dialog to get the quota settings.
    //
    QuotaStateFromControls(hDlg, &dwState);
    LogFlagsFromControls(hDlg, &dwLogFlags);
    if (IsDlgButtonChecked(hDlg, IDC_RBN_DEF_NOLIMIT))
    {
        llThreshold = NOLIMIT;
        llLimit     = NOLIMIT;
    }
    else
    {
        llThreshold = m_pxbDefaultThreshold->GetBytes();
        llLimit     = m_pxbDefaultLimit->GetBytes();
    }

    DISKQUOTAPOLICYINFO dqpi;
    dqpi.cb                      = sizeof(dqpi);
    dqpi.llDefaultQuotaThreshold = llThreshold;
    dqpi.llDefaultQuotaLimit     = llLimit;
    dqpi.dwQuotaState            = dwState;
    dqpi.dwQuotaLogFlags         = dwLogFlags;
    dqpi.bRemovableMedia         = (BST_CHECKED == IsDlgButtonChecked(hDlg, IDC_RBN_POLICY_REMOVABLE));

    //
    // Save the policy information to the registry.
    //
    hr = m_pPolicy->Save(&dqpi);
    if (FAILED(hr))
    {
        DBGERROR((TEXT("Error 0x%08X saving policy information"), hr));
        //
        // Show some UI?
        //
    }

    //
    // Save current dialog state so that we can restore it next time the
    // user opens the snapin.  This way, if the user typically sets
    // quotas to the same values, the UI will appear much more friendly.
    //
    RegKey keyPref(HKEY_CURRENT_USER, REGSTR_KEY_DISKQUOTA);
    if (FAILED(keyPref.Open(KEY_WRITE)) ||
        FAILED(keyPref.SetValue(c_szSnapInPrefs, (LPBYTE)&dqpi, sizeof(dqpi))))
    {
        DBGERROR((TEXT("Error saving policy dialog information")));
    }

    SetWindowLongPtr(hDlg, DWLP_MSGRESULT, dwPSNReturn);

    return TRUE;
}


BOOL
SnapInVolPropPage::OnSheetNotifyKillActive(
    HWND hDlg,
    UINT wParam,
    LONG lParam
    )
{
    DBGTRACE((DM_VPROP, DL_HIGH, TEXT("SnapInVolPropPage::OnSheetNotifyKillActive")));
    SetWindowLongPtr(hDlg, DWLP_MSGRESULT, FALSE);
    return TRUE;
}



BOOL
SnapInVolPropPage::OnSheetNotifyReset(
    HWND hDlg,
    UINT wParam,
    LONG lParam
    )
{
    DBGTRACE((DM_VPROP, DL_HIGH, TEXT("SnapInVolPropPage::OnSheetNotifyReset")));
    return FALSE;
}


HRESULT
SnapInVolPropPage::CreateDiskQuotaPolicyObject(
    IDiskQuotaPolicy **ppOut
    )
{
    DBGTRACE((DM_VPROP, DL_HIGH, TEXT("SnapInVolPropPage::CreateDiskQuotaPolicyObject")));
    HRESULT hr = NOERROR;
    try
    {
        CDiskQuotaPolicy *pPolicy = new CDiskQuotaPolicy;
        hr = pPolicy->QueryInterface(IID_IDiskQuotaPolicy,
                                     reinterpret_cast<void **>(&m_pPolicy));
        if (SUCCEEDED(hr))
        {
            m_pPolicy->AddRef();
            *ppOut = m_pPolicy;
        }
    }
    catch(CAllocException& e)
    {
        hr = E_OUTOFMEMORY;
    }
    return hr;
}

#endif // POLICY_MMC_SNAPIN
