/*++

Microsoft Confidential
Copyright (c) 1992-1997  Microsoft Corporation
All rights reserved

Module Name:

    crashdmp.c

Abstract:

    Implements the "Recovery" group on the Startup/Recovery
    dialog of the System Control Panel Applet

Notes:

    The virtual memory settings and the crash dump (core dump) settings
    are tightly-coupled.  Therefore, crashdmp.c and startup.h have some
    heavy dependencies on virtual.c and virtual.h (and vice versa).

Author:

    Byron Dazey 06-Jun-1992

Revision History:

    15-Oct-1997 scotthal
        Complete overhaul

--*/

#include "sysdm.h"
#include <windowsx.h>

#define KBYTE (1024UI64)
#define MBYTE (1024UI64 * KBYTE)
#define GBYTE (1024UI64 * MBYTE)

//
// CrashDumpEnabled is not a boolean value anymore. It can take on one of the
// following types.
//

#define DUMP_TYPE_NONE              (0)
#define DUMP_TYPE_MINI              (1)
#define DUMP_TYPE_SUMMARY           (2)
#define DUMP_TYPE_FULL              (3)
#define DUMP_TYPE_MAX               (4)

#define REG_LOG_EVENT_VALUE_NAME    TEXT ("LogEvent")
#define REG_SEND_ALERT_VALUE_NAME   TEXT ("SendAlert")
#define REG_OVERWRITE_VALUE_NAME    TEXT ("Overwrite")
#define REG_AUTOREBOOT_VALUE_NAME   TEXT ("AutoReboot")
#define REG_DUMPFILE_VALUE_NAME     TEXT ("DumpFile")
#define REG_MINIDUMP_DIR_VALUE_NAME TEXT ("MinidumpDir")
#define REG_DUMP_TYPE_VALUE_NAME    TEXT ("CrashDumpEnabled")

#define BIG_MEMORY_MAX_BOOT_PF_MB   (2048)
#define CRASH_CONTROL_KEY           TEXT("System\\CurrentControlSet\\Control\\CrashControl")

//
// The crashdump code is hard-coded to generate only summary dumps for
// machines with more than 2 GB of physical memory. Do not change this
// constant unless unless you change the same code in ntos\io\dumpctl.c
//

#define LARGE_MEMORY_THRESHOLD      (2 * GBYTE)

typedef struct _SYSTEM_MEMORY_CONFIGURATION {
    BOOL    BigMemory;
    ULONG   PageSize;
    ULONG64 PhysicalMemorySize;
    ULONG64 BootPartitionPageFileSize;
    TCHAR   BootDrive;
} SYSTEM_MEMORY_CONFIGURATION;

VCREG_RET gvcCrashCtrl =  VCREG_ERROR;
HKEY ghkeyCrashCtrl = NULL;
int  gcrefCrashCtrl = 0;
BOOL gfCoreDumpChanged = FALSE;

TCHAR CrashDumpFile [MAX_PATH] = TEXT("%SystemRoot%\\MEMORY.DMP");
TCHAR MiniDumpDirectory [MAX_PATH] = TEXT("%SystemRoot%\\Minidump");
TCHAR DumpFileText [100];
TCHAR MiniDumpDirText [100];

SYSTEM_MEMORY_CONFIGURATION SystemMemoryConfiguration;

//
// Private function prototypes
//

DWORD
GetDumpSelection(
    HWND hDlg
    );

NTSTATUS
GetMemoryConfiguration(
    OUT SYSTEM_MEMORY_CONFIGURATION * MemoryConfig
    );

VOID
DisableCoreDumpControls(
    HWND hDlg
    );

static
BOOL
CoreDumpInit(
    IN HWND hDlg
    );

static
BOOL
CoreDumpUpdateRegistry(
    IN HWND hDlg,
    IN HKEY hKey
    );

int
CoreDumpHandleOk(
    IN BOOL fInitialized,
    IN HWND hDlg,
    IN WPARAM wParam,
    IN LPARAM lParam
    );


VOID
SwapDumpSelection(
    HWND hDlg
    );
//
// Implementation
//

VCREG_RET
CoreDumpOpenKey(
    )
{
    if (gvcCrashCtrl == VCREG_ERROR) {
        gvcCrashCtrl = OpenRegKey( CRASH_CONTROL_KEY, &ghkeyCrashCtrl );
    }

    if (gvcCrashCtrl != VCREG_ERROR) {
        gcrefCrashCtrl++;
    }

    return gvcCrashCtrl;
}

void
CoreDumpCloseKey(
    )
{
    if (gcrefCrashCtrl > 0) {
        gcrefCrashCtrl--;
        if (gcrefCrashCtrl == 0) {
            CloseRegKey( ghkeyCrashCtrl );
            gvcCrashCtrl = VCREG_ERROR;
        }
    }
}




BOOL
StartAlerterService(
    IN SC_HANDLE hAlerter
    )
{
    BOOL fResult = FALSE;

    fResult = ChangeServiceConfig(
        hAlerter,
        SERVICE_NO_CHANGE,
        SERVICE_AUTO_START,
        SERVICE_NO_CHANGE,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL
    );

    fResult = StartService(hAlerter, 0, NULL);

    return(fResult);

}

BOOL
IsAlerterSvcStarted(
    HWND hDlg
    )
{
    SC_HANDLE schSCManager, schService = NULL;
    LPQUERY_SERVICE_CONFIG lpqscBuf;
    DWORD dwBytesNeeded;
    BOOL fRunning = FALSE;
    SERVICE_STATUS ssSrvcStat;


    /*
     * Open the Service Controller
     */
    schSCManager = OpenSCManager(
         NULL,                   /* local machine           */
         NULL,                   /* ServicesActive database */
         SC_MANAGER_ALL_ACCESS); /* full access rights      */

    if (schSCManager == NULL) {
        goto iassExit;
    }


    /*
     * Try to open the Alerter Service
     */


    /* Open a handle to the service. */

    schService = OpenService(
         schSCManager,           /* SCManager database  */
         TEXT("Alerter"),        /* name of service     */
         SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_START
    );

    if (schService == NULL) {
        goto iassExit;
    }

    /*
     * Query the Alerter service to see if it has been started
     */

    if (!QueryServiceStatus(schService, &ssSrvcStat )) {
        goto iassExit;
    }


    if (ssSrvcStat.dwCurrentState != SERVICE_RUNNING) {
        fRunning = StartAlerterService(schService);
    } else {

        fRunning = TRUE;
    }


iassExit:
    if (!fRunning) {
        MsgBoxParam(hDlg, IDS_SYSDM_NOALERTER, IDS_SYSDM_TITLE, MB_ICONEXCLAMATION );
    }

    if (schService != NULL) {
        CloseServiceHandle(schService);
    }

    if (schSCManager != NULL) {
        CloseServiceHandle(schSCManager);
    }

    return fRunning;
}


BOOL
VerifyDumpPath(
    IN HWND hDlg
    )
{
    
    TCHAR szPath[MAX_PATH];
    TCHAR szExpPath[MAX_PATH];
    LPTSTR psz;
    TCHAR ch;
    UINT uType;

    if( GetDlgItemText(hDlg, IDC_STARTUP_CDMP_FILENAME, szPath,
            ARRAYSIZE(szPath)) == 0) {

        MsgBoxParam(hDlg, IDS_SYSDM_DEBUGGING_FILENAME, IDS_SYSDM_TITLE, MB_ICONSTOP | MB_OK);
        return FALSE;
    }

    /*
     * Expand any environment vars, and then check to make sure it
     * is a fully quallified path
     */
     
    // if it has a '%' in it, then try to expand it
    
    if (ExpandEnvironmentStrings(szPath, szExpPath, ARRAYSIZE(szExpPath)) >= ARRAYSIZE(szExpPath)) {
        MsgBoxParam(hDlg, IDS_SYSDM_DEBUGGING_PATHLONG, IDS_SYSDM_TITLE, MB_ICONSTOP | MB_OK,
                (DWORD)MAX_PATH);
        return FALSE;
    }

    // now cannonicalize it

    GetFullPathName( szExpPath, ARRAYSIZE(szPath), szPath, &psz );

    // check to see that it already was cannonicalized

    if (lstrcmp( szPath, szExpPath ) != 0) {
        MsgBoxParam(hDlg, IDS_SYSDM_DEBUGGING_UNQUALIFIED, IDS_SYSDM_TITLE, MB_ICONSTOP | MB_OK );
        return FALSE;
    }

    /*
     * check the drive (don't allow remote)
     */

    ch = szPath[3];
    szPath[3] = TEXT('\0');
    if (IsPathSep(szPath[0]) || ((uType = GetDriveType(szPath)) !=
            DRIVE_FIXED && uType != DRIVE_REMOVABLE)) {
        MsgBoxParam(hDlg, IDS_SYSDM_DEBUGGING_DRIVE, IDS_SYSDM_TITLE, MB_ICONSTOP | MB_OK );
        return FALSE;
    }
    szPath[3] = ch;

    /*
     * if path is non-exstant, tell user and let him decide what to do
     */

    if (GetFileAttributes(szPath) == 0xFFFFFFFFL && GetLastError() !=
        ERROR_FILE_NOT_FOUND && MsgBoxParam(hDlg, IDS_SYSDM_DEBUGGING_PATH, IDS_SYSDM_TITLE,
            MB_ICONQUESTION | MB_YESNO ) == IDYES) {
        return FALSE;
    }

    return TRUE;
}

    
    

BOOL
CoreDumpValidFile(
    HWND hDlg
    )
{
    switch (GetDumpSelection (hDlg)) {

        case DUMP_TYPE_NONE:
            return TRUE;

        case DUMP_TYPE_MINI:
            return VerifyDumpPath (hDlg);
            

        case DUMP_TYPE_SUMMARY:
        case DUMP_TYPE_FULL:
            return VerifyDumpPath (hDlg);
            
        default:
            ASSERT (FALSE);
            return TRUE;
    }

    return FALSE;
}


int
APIENTRY
CoreDumpDlgProc(
    HWND hDlg,
    UINT message,
    WPARAM wParam,
    LPARAM lParam
    )
{
    static BOOL fInitialized = FALSE;

    switch (message)
    {
    case WM_INITDIALOG:
        g_fStartupInitializing = TRUE;
        fInitialized = CoreDumpInit(hDlg);
        g_fStartupInitializing = FALSE;
        return RET_CONTINUE;
        break;

    case WM_COMMAND:

        switch (LOWORD(wParam)) {
            case IDOK:
                return(CoreDumpHandleOk(fInitialized, hDlg, wParam, lParam));
                break;

            case IDCANCEL:
                if (fInitialized) {
                    VirtualCloseKey();
                    CoreDumpCloseKey();
                }
                // Let the Startup/Recovery dlg proc also handle IDOK
                return(RET_NO_CHANGE);
                break;

            case IDC_STARTUP_CDMP_TYPE: {
                SwapDumpSelection (hDlg);
            }
            // Fall through

            case IDC_STARTUP_CDMP_FILENAME:
            case IDC_STARTUP_CDMP_LOG:
            case IDC_STARTUP_CDMP_SEND:
            case IDC_STARTUP_CDMP_OVERWRITE:
            case IDC_STARTUP_CDMP_AUTOREBOOT:
                if (!g_fStartupInitializing) {
                    gfCoreDumpChanged = TRUE;
                }
                break;
            default: {
                // indicat not handled
                return RET_CONTINUE;
            }
        }
        break; // WM_COMMAND

    case WM_DESTROY:
        return RET_CONTINUE;
        break;

    default:
        return RET_CONTINUE;
    }

    return RET_BREAK;
}

int
CoreDumpHandleOk(
    IN BOOL fInitialized,
    IN HWND hDlg,
    IN WPARAM wParam,
    IN LPARAM lParam
    )
{
    BOOL fRegChg;
    NTSTATUS Status;
    DWORD Ret;
    int iRet = RET_NO_CHANGE;
    SYSTEM_MEMORY_CONFIGURATION MemoryConfig = {0};

    if (fInitialized && gfCoreDumpChanged)
    {
        // Validate crashdump file name.
        if (!CoreDumpValidFile(hDlg))
        {
            SetFocus(GetDlgItem(hDlg, IDC_STARTUP_CDMP_FILENAME));
            SetWindowLongPtr (hDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
            iRet = RET_ERROR;
            return iRet;
        }


        Status = GetMemoryConfiguration(&MemoryConfig);

        if (NT_SUCCESS (Status) &&
            MemoryConfig.BootPartitionPageFileSize <
            CoreDumpGetRequiredFileSize (hDlg))
        {
            // Warn that the dump file may be truncated.
            Ret = MsgBoxParam (hDlg,
                               IDS_SYSDM_DEBUGGING_MINIMUM,
                               IDS_SYSDM_TITLE,
                               MB_ICONEXCLAMATION | MB_YESNO,
                               (MemoryConfig.BootDrive ? MemoryConfig.BootDrive : TEXT('?')),
                               (DWORD) (CoreDumpGetRequiredFileSize (hDlg) / MBYTE)
                               );

            if (Ret == IDNO)
            {
                return RET_ERROR;
            }
        }

        // If the Alert button is checked, make sure the alerter service
        // is started.
        if (IsDlgButtonChecked(hDlg, IDC_STARTUP_CDMP_SEND))
        {
            IsAlerterSvcStarted(hDlg);
        }

        fRegChg = CoreDumpUpdateRegistry (hDlg, ghkeyCrashCtrl);

        // Clean up registry stuff
        CoreDumpCloseKey();
        VirtualCloseKey();

        if (fRegChg)
        {
            // Notify the kernel to reread the crashdump parameters from the registry.
            Status = NtSetSystemInformation(SystemCrashDumpStateInformation, NULL, 0);
            if (NT_SUCCESS(Status))
            {
                iRet = RET_CHANGE_NO_REBOOT;
            }
            else
            {
                iRet = RET_RECOVER_CHANGE;
            }
        }
    } else {
        iRet = RET_NO_CHANGE;
    }

    return(iRet);
}

void
CoreDumpInitErrorExit(
    HWND hDlg,
    HKEY hk
    )
{
    MsgBoxParam(hDlg, IDS_SYSDM_NOOPEN_RECOVER_GROUP, IDS_SYSDM_TITLE, MB_ICONEXCLAMATION);
    if( hk == ghkeyMemMgt )
        VirtualCloseKey();

    DisableCoreDumpControls(hDlg);

    HourGlass(FALSE);
    return;
}


DWORD
GetDumpSelection(
    HWND hDlg
    )
{
    HWND hControl;

    hControl = GetDlgItem (hDlg, IDC_STARTUP_CDMP_TYPE);
    return ComboBox_GetCurSel ( hControl );
}

VOID
DisableCoreDumpControls(
    HWND hDlg
    )
{
    EnableWindow( GetDlgItem (hDlg, IDC_STARTUP_CDMP_GRP), FALSE);
    EnableWindow( GetDlgItem (hDlg, IDC_STARTUP_CDMP_TXT1), FALSE);
    EnableWindow( GetDlgItem (hDlg, IDC_STARTUP_CDMP_LOG ), FALSE);
    EnableWindow( GetDlgItem (hDlg, IDC_STARTUP_CDMP_SEND), FALSE);
    EnableWindow( GetDlgItem (hDlg, IDC_STARTUP_CDMP_TYPE), FALSE);
    EnableWindow( GetDlgItem (hDlg, IDC_STARTUP_CDMP_FILENAME), FALSE);
    EnableWindow( GetDlgItem (hDlg, IDC_STARTUP_CDMP_OVERWRITE), FALSE);
    EnableWindow( GetDlgItem (hDlg, IDC_STARTUP_CDMP_AUTOREBOOT), FALSE);
}

VOID
SwapDumpSelection(
    HWND hDlg
    )
{
    //
    // If there is no dump type, disable some controls. If this is a minidump
    // disable overwrite and change "File Name:" to "Mini Dump Directory:"
    //

    switch (GetDumpSelection (hDlg)) {

        case DUMP_TYPE_NONE:
            EnableWindow (GetDlgItem (hDlg, IDC_STARTUP_CDMP_OVERWRITE), FALSE);
            EnableWindow (GetDlgItem (hDlg, IDC_STARTUP_CDMP_FILENAME), FALSE);
            EnableWindow (GetDlgItem (hDlg, IDC_STARTUP_CDMP_FILE_LABEL), FALSE);
            SetWindowText (GetDlgItem (hDlg, IDC_STARTUP_CDMP_FILENAME),
                           CrashDumpFile
                           );
            Static_SetText (GetDlgItem (hDlg, IDC_STARTUP_CDMP_FILE_LABEL),
                            DumpFileText
                            );
            break;

        case DUMP_TYPE_MINI:
            EnableWindow (GetDlgItem (hDlg, IDC_STARTUP_CDMP_OVERWRITE), FALSE);
            EnableWindow (GetDlgItem (hDlg, IDC_STARTUP_CDMP_FILENAME), TRUE);
            EnableWindow (GetDlgItem (hDlg, IDC_STARTUP_CDMP_FILE_LABEL), TRUE);
            SetWindowText (GetDlgItem (hDlg, IDC_STARTUP_CDMP_FILENAME),
                           MiniDumpDirectory
                           );
            Static_SetText (GetDlgItem (hDlg, IDC_STARTUP_CDMP_FILE_LABEL),
                            MiniDumpDirText
                            );
            break;

        default:
            EnableWindow (GetDlgItem (hDlg, IDC_STARTUP_CDMP_OVERWRITE), TRUE);
            EnableWindow (GetDlgItem (hDlg, IDC_STARTUP_CDMP_FILENAME), TRUE);
            EnableWindow (GetDlgItem (hDlg, IDC_STARTUP_CDMP_FILE_LABEL), TRUE);
            SetWindowText (GetDlgItem (hDlg, IDC_STARTUP_CDMP_FILENAME),
                           CrashDumpFile
                           );
            Static_SetText (GetDlgItem (hDlg, IDC_STARTUP_CDMP_FILE_LABEL),
                             DumpFileText
                             );
    }
}

BOOL
GetSystemDrive(
    OUT TCHAR * Drive
    )
{
    TCHAR WindowsDir [ MAX_PATH ];

    if (!GetWindowsDirectory (WindowsDir, ARRAYSIZE (WindowsDir))) {
        return FALSE;
    }

    if (!isalpha (*WindowsDir)) {
        return FALSE;
    }

    *Drive = *WindowsDir;

    return TRUE;
}

NTSTATUS
GetMemoryConfiguration(
    OUT SYSTEM_MEMORY_CONFIGURATION * MemoryConfig
    )
{
    BOOL Succ;
    TCHAR SystemDrive;
    NTSTATUS Status;
    SYSTEM_BASIC_INFORMATION BasicInfo;
    ULONGLONG iMaxPageFileSize;

    Status = NtQuerySystemInformation(
                        SystemBasicInformation,
                        &BasicInfo,
                        sizeof (BasicInfo),
                        NULL
                        );

    if (NT_SUCCESS (Status)) {
        Status;
    }

    MemoryConfig->PhysicalMemorySize =
            (ULONG64) BasicInfo.NumberOfPhysicalPages *
            (ULONG64) BasicInfo.PageSize;

    MemoryConfig->PageSize = BasicInfo.PageSize;

    //
    // Get the Boot-partition pagefile size.
    //

    Succ = GetSystemDrive (&SystemDrive);

    if (!Succ) {
        return FALSE;
    }

    MemoryConfig->BootDrive = (WCHAR) toupper (SystemDrive);

    SystemDrive = tolower (SystemDrive) - 'a';

    //
    // A big memory machine is one that has more memory than we can write to
    // at crashdump time.
    //

    iMaxPageFileSize = GetMaxPagefileSizeInMB(SystemDrive);
    iMaxPageFileSize *= (1024 * 1024); // MaxPageFileSize stored in megabytes
    if ((ULONGLONG)MemoryConfig->PhysicalMemorySize >= iMaxPageFileSize) { 
        MemoryConfig->BigMemory = TRUE;
    } else {
        MemoryConfig->BigMemory = FALSE;
    }

    //
    // NOTE: apf is a global exposed by virtual.c
    //

    Succ = VirtualGetPageFiles ( apf );

    if (!Succ) {
        return FALSE;
    }

    //
    // This is the file size in terms of megabytes.
    //

    MemoryConfig->BootPartitionPageFileSize = apf [ SystemDrive ].nMinFileSize;

    //
    // Convert to bytes.
    //

    MemoryConfig->BootPartitionPageFileSize *= MBYTE;

    VirtualFreePageFiles ( apf );

    return STATUS_SUCCESS;

}

BOOL
CheckInitFromRegistry(
    IN HWND hDlg,
    IN DWORD ControlId,
    IN HKEY RegKey,
    IN LPTSTR ValueName,
    IN BOOL Default
    )
{
    BOOL Succ;
    DWORD Type;
    DWORD Data;
    BOOL DataSize;
    BOOL Value;

    DataSize = sizeof (Data);

    Succ = RegQueryValueEx (
                     RegKey,
                     ValueName,
                     NULL,
                     &Type,
                     (LPBYTE) &Data,
                     &DataSize
                     );

    if (Succ != ERROR_SUCCESS || Type != REG_DWORD) {
        Value = Default;
    } else {
        Value = Data ? TRUE : FALSE;
    }

    return CheckDlgButton (hDlg, ControlId, Value);
}


BOOL
ComboAddStringFromResource(
    IN HWND hDlg,
    IN DWORD ControlId,
    IN HINSTANCE ModuleHandle,
    IN DWORD ResourceId,
    IN DWORD ItemData
    )
{
    DWORD Res;
    DWORD Item;
    HWND hControl;
    DWORD Result;
    WCHAR Buffer[512];

    Res = LoadString(ModuleHandle, ResourceId, Buffer, ARRAYSIZE(Buffer));
    if (Res == 0)
    {
        return FALSE;
    }

    hControl = GetDlgItem(hDlg, ControlId);
    Item = ComboBox_InsertString(hControl, -1, Buffer);
    ComboBox_SetItemData(hControl, Item, ItemData);

    return TRUE;
}


BOOL
StoreCheckboxToReg(
    IN HWND hDlg,
    IN DWORD ControlId,
    IN HKEY hKey,
    IN LPCTSTR RegValueName
    )
{
    DWORD Checked;

    Checked = IsDlgButtonChecked (hDlg, ControlId);

    RegSetValueEx(
            hKey,
            RegValueName,
            0,
            REG_DWORD,
            (LPBYTE) &Checked,
            sizeof (Checked)
            );

    return TRUE;
}


BOOL
StoreStringToReg(
    IN HWND hDlg,
    IN DWORD ControlId,
    IN HKEY hKey,
    IN LPCTSTR RegValueName
    )
{
    TCHAR Buffer [ MAX_PATH ];

    GetDlgItemText (hDlg, ControlId, Buffer, ARRAYSIZE(Buffer));

    //
    // Check the buffer for valid file-name??
    //

    RegSetValueEx (
            hKey,
            RegValueName,
            0,
            REG_EXPAND_SZ,
            (LPBYTE) Buffer,
            (wcslen (Buffer) + 1) * sizeof (TCHAR)
            );

    return TRUE;
}

static DWORD SelectionToType [] = { 0, 3, 2, 1 };

DWORD
GetDumpTypeFromRegistry(
    HKEY Key
    )
{
    DWORD DataSize;
    DWORD Type;
    DWORD DumpType;

    DataSize = sizeof (DWORD);
    RegQueryValueEx (
                    Key,
                    REG_DUMP_TYPE_VALUE_NAME,
                    NULL,
                    &Type,
                    (LPBYTE) &DumpType,
                    &DataSize
                    );

    if (DumpType > 3) {
        DumpType = DUMP_TYPE_MINI;
    } else {
        DumpType = SelectionToType [ DumpType ];
    }

    return DumpType;
}


BOOL CoreDumpInit(HWND hDlg)
{
    BOOL Succ;
    NTSTATUS Status;
    DWORD DataSize;
    DWORD DumpType;
    DWORD Type;
    VCREG_RET vcVirt;
    VCREG_RET vcCore;
    SYSTEM_MEMORY_CONFIGURATION MemoryConfig;

    HourGlass (TRUE);

    // Do no put anything before the initialization of the globals, here.
    vcVirt = VirtualOpenKey();

    if( vcVirt == VCREG_ERROR )
    {
        CoreDumpInitErrorExit(hDlg, NULL);
        return FALSE;
    }

    vcCore = CoreDumpOpenKey();
    if (vcCore == VCREG_ERROR)
    {
        CoreDumpInitErrorExit(hDlg, ghkeyMemMgt);
        return FALSE;
    }
    else if (vcCore == VCREG_READONLY || vcVirt == VCREG_READONLY)
    {
        DisableCoreDumpControls (hDlg);
    }
    else
    {
        Status = GetMemoryConfiguration (&SystemMemoryConfiguration);
        if (!NT_SUCCESS (Status))
        {
            return FALSE;
        }
    }

    Status = GetMemoryConfiguration (&MemoryConfig);
    if (!NT_SUCCESS (Status))
    {
        return FALSE;
    }

    Succ = LoadString (hInstance, IDS_CRASHDUMP_DUMP_FILE, DumpFileText, ARRAYSIZE(DumpFileText));
    Succ = LoadString (hInstance, IDS_CRASHDUMP_MINI_DIR, MiniDumpDirText, ARRAYSIZE(MiniDumpDirText));

    // Special Case: Server Product does not want ability to disable logging
    // of crashdumps.
    if (IsWorkstationProduct ())
    {
        CheckInitFromRegistry(
                    hDlg,
                    IDC_STARTUP_CDMP_LOG,
                    ghkeyCrashCtrl,
                    REG_LOG_EVENT_VALUE_NAME,
                    TRUE
                    );
    }
    else
    {
        CheckDlgButton (hDlg, IDC_STARTUP_CDMP_LOG, TRUE);
        EnableWindow ( GetDlgItem (hDlg, IDC_STARTUP_CDMP_LOG), FALSE);
    }

    CheckInitFromRegistry(hDlg, IDC_STARTUP_CDMP_SEND, ghkeyCrashCtrl,REG_SEND_ALERT_VALUE_NAME, TRUE);
    CheckInitFromRegistry(hDlg, IDC_STARTUP_CDMP_OVERWRITE, ghkeyCrashCtrl, REG_OVERWRITE_VALUE_NAME, TRUE);
    CheckInitFromRegistry(hDlg, IDC_STARTUP_CDMP_AUTOREBOOT, ghkeyCrashCtrl, REG_AUTOREBOOT_VALUE_NAME, TRUE);
    ComboAddStringFromResource(hDlg, IDC_STARTUP_CDMP_TYPE, hInstance,                  // Global hInstance
                    IDS_CRASHDUMP_NONE, 0);

#ifdef _WIN64
    ComboAddStringFromResource(hDlg, IDC_STARTUP_CDMP_TYPE, hInstance, IDS_CRASHDUMP_MINI_WIN64, 0);
#else
    ComboAddStringFromResource(hDlg, IDC_STARTUP_CDMP_TYPE, hInstance, IDS_CRASHDUMP_MINI, 0);
#endif
    ComboAddStringFromResource(hDlg, IDC_STARTUP_CDMP_TYPE, hInstance, IDS_CRASHDUMP_SUMMARY, 0 );

    // Special case: Server Products do not allow full memory dumps.
    DumpType = GetDumpTypeFromRegistry(ghkeyCrashCtrl);
    if (MemoryConfig.PhysicalMemorySize < LARGE_MEMORY_THRESHOLD)
    {        
        ComboAddStringFromResource(hDlg, IDC_STARTUP_CDMP_TYPE, hInstance, IDS_CRASHDUMP_FULL, 0);
    }
    else
    {
        if (DumpType == DUMP_TYPE_FULL)
        {
            DumpType = DUMP_TYPE_SUMMARY;
        }
    }

    ComboBox_SetCurSel(GetDlgItem (hDlg, IDC_STARTUP_CDMP_TYPE), DumpType);

    DataSize = sizeof (CrashDumpFile);
    RegQueryValueEx (
                    ghkeyCrashCtrl,
                    REG_DUMPFILE_VALUE_NAME,
                    NULL,
                    &Type,
                    (LPBYTE) CrashDumpFile,
                    &DataSize
                    );

    DataSize = sizeof (MiniDumpDirectory);
    RegQueryValueEx (
                    ghkeyCrashCtrl,
                    REG_MINIDUMP_DIR_VALUE_NAME,
                    NULL,
                    &Type,
                    (LPBYTE) MiniDumpDirectory,
                    &DataSize
                    );

    // Update the selection fields of the dialog.
    SwapDumpSelection (hDlg);
    HourGlass(FALSE);

    return TRUE;
}



BOOL
CoreDumpUpdateRegistry(
    HWND hDlg,
    HKEY hKey
    )
{
    DWORD Selection;

    StoreCheckboxToReg(
                hDlg,
                IDC_STARTUP_CDMP_LOG,
                hKey,
                REG_LOG_EVENT_VALUE_NAME
                );

    StoreCheckboxToReg(
                hDlg,
                IDC_STARTUP_CDMP_SEND,
                hKey,
                REG_SEND_ALERT_VALUE_NAME
                );

    StoreCheckboxToReg(
                hDlg,
                IDC_STARTUP_CDMP_OVERWRITE,
                hKey,
                REG_OVERWRITE_VALUE_NAME
                );

    StoreCheckboxToReg(
                hDlg,
                IDC_STARTUP_CDMP_AUTOREBOOT,
                hKey,
                REG_AUTOREBOOT_VALUE_NAME
                );

    Selection = GetDumpSelection (hDlg);

    if (Selection == DUMP_TYPE_MINI) {

        StoreStringToReg (
                    hDlg,
                    IDC_STARTUP_CDMP_FILENAME,
                    hKey,
                    REG_MINIDUMP_DIR_VALUE_NAME
                    );

    } else {

        StoreStringToReg(
                    hDlg,
                    IDC_STARTUP_CDMP_FILENAME,
                    hKey,
                    REG_DUMPFILE_VALUE_NAME
                    );
    }


    if (Selection > 3) {
        Selection = 3;
    }

    Selection = SelectionToType [ Selection ];
    RegSetValueEx (
            hKey,
            REG_DUMP_TYPE_VALUE_NAME,
            0,
            REG_DWORD,
            (LPBYTE) &Selection,
            sizeof (Selection)
            );

    return TRUE;
}


ULONG64
EstimateSummaryDumpSize(
    ULONG64 PhysicalMemorySize
    )
{
    ULONG64 Size;

    //
    // Very rough guesses at the size of the summary dump.
    //

    if (PhysicalMemorySize < 128 * MBYTE) {

        Size = 50 * MBYTE;

    } else if (PhysicalMemorySize < 4 * GBYTE) {

        Size = 200 * MBYTE;

    } else if (PhysicalMemorySize < 8 * GBYTE) {

        Size = 400 * MBYTE;

    } else {

        Size = 800 * MBYTE;
    }

    return Size;
}


ULONG64
CoreDumpGetRequiredFileSize(
    IN HWND hDlg OPTIONAL
    )
{
    ULONG64 Size;
    DWORD DumpType;
    NTSTATUS Status;
    SYSTEM_MEMORY_CONFIGURATION MemoryConfig;


    //
    // If we were passed a hDlg, get the selection from the dlg. Otherwise,
    // get the selection from the registry.
    //

    if (hDlg != NULL) {

        //
        // Get selection from dlg.
        //

        DumpType = GetDumpSelection ( hDlg );

    } else {

        HKEY hKey;
        DWORD Err;

        //
        // Get selection from registry.
        //

        Err = OpenRegKey (CRASH_CONTROL_KEY,
                          &hKey
                          );

        if (Err == VCREG_ERROR) {
            return DUMP_TYPE_MINI;
        }

        ASSERT ( hKey );
        DumpType = GetDumpTypeFromRegistry ( hKey );
        CloseRegKey ( hKey );
    }

    switch (DumpType) {

        case DUMP_TYPE_NONE:
            Size = 0;
            break;

        case DUMP_TYPE_MINI:
            Size = 64 * KBYTE;
            break;

        case DUMP_TYPE_SUMMARY:

            Status = GetMemoryConfiguration (&MemoryConfig);

            if (NT_SUCCESS (Status)) {
                Size = EstimateSummaryDumpSize (MemoryConfig.PhysicalMemorySize);
            } else {
                //
                // A (large) shot in the dark.
                //
                Size = 800 * MBYTE;
            }
            break;

        case DUMP_TYPE_FULL:

            Status = GetMemoryConfiguration (&MemoryConfig);

            if (NT_SUCCESS (Status)) {
                Size = MemoryConfig.PhysicalMemorySize;
            } else {
                Size = 0;
            }

        break;

        default:
            ASSERT (FALSE);
    }

    return Size;
}


