/*++

Copyright (c) 1995-2000 Microsoft Corporation

Module Name:

    devres.c

Abstract:

    Routines for displaying resource dialogs.

Author:

    Paula Tomlinson (paulat) 7-Feb-1996

Revision History:

    Jamie Hunter (jamiehun) 19-Mar-1998

--*/

#include "precomp.h"
#pragma hdrstop

//
// Private Prototypes
//


//
// Global Data
//

static INTERFACE_TYPE ResourcePickerReadOnlyInterfaces[] = {
    //
    // List of interface-types that we don't want user to edit properties of
    //

    PCIBus,

    //
    // End of list
    //
    InterfaceTypeUndefined
};

static const BOOL ResTypeEditable[ResType_MAX+1] = {
    //
    // lists resource types that are shown and are editable
    FALSE,  // ResType_None
    TRUE,   // ResType_Mem
    TRUE,   // ResType_IO
    TRUE,   // ResType_DMA
    TRUE,   // ResType_IRQ
    FALSE,  // ResType_DoNotUse
    FALSE   // ResType_BusNumber
};

#if (ResType_MAX+1) != 7
#error Fix SetupAPI devres.c, ResType_MAX has changed
#endif

//
// HELP ID's
//
static const DWORD DevResHelpIDs[]=
{
    IDC_DEVRES_ICON,            IDH_NOHELP,     // "Low (%d)" (Static)
    IDC_DEVRES_DEVDESC,         IDH_NOHELP,
    IDC_DEVRES_SETTINGSTATE,    IDH_DEVMGR_RESOURCES_SETTINGS,
    IDC_DEVRES_SETTINGSLIST,    IDH_DEVMGR_RESOURCES_SETTINGS,
    IDC_DEVRES_LCTEXT,          IDH_DEVMGR_RESOURCES_BASEDON,
    IDC_DEVRES_LOGCONFIGLIST,   IDH_DEVMGR_RESOURCES_BASEDON,
    IDC_DEVRES_CHANGE,          IDH_DEVMGR_RESOURCES_CHANGE,
    IDC_DEVRES_USESYSSETTINGS,  IDH_DEVMGR_RESOURCES_AUTO,
    IDC_DEVRES_CONFLICTDEVTEXT, IDH_DEVMGR_RESOURCES_CONFLICTS,
    IDC_DEVRES_CONFLICTINFOLIST,    IDH_DEVMGR_RESOURCES_CONFLICTS,
    IDC_DEVRES_MFPARENT,        IDH_DEVMGR_RESOURCES_PARENT,
    IDC_DEVRES_MFPARENT_DESC,   IDH_DEVMGR_RESOURCES_PARENT,
    IDC_DEVRES_MAKEFORCED,      IDH_DEVMGR_RESOURCES_SETMANUALLY,
    0, 0
};

//
// HACKHACK (jamiehun)
// after we've changed UI from a MakeForced, we post this message to get back control of keyboard
//

#define WM_USER_FOCUS           (WM_USER+101)


//
// API to obtain a resource-picker page
//

HPROPSHEETPAGE
GetResourceSelectionPage(
    IN HDEVINFO DeviceInfoSet,
    IN PSP_DEVINFO_DATA DeviceInfoData
    )
{
    LPDMPROP_DATA     pdmData;
    PROPSHEETPAGE     PropPage;

    //
    // private data
    // anything we "do" here must be "undone" in pResourcePickerPropPageCallback
    //
    pdmData = (LPDMPROP_DATA)MyMalloc(sizeof(DMPROP_DATA));
    if (pdmData == NULL) {
        return NULL;
    }
    ZeroMemory(pdmData,sizeof(DMPROP_DATA));

    pdmData->hDevInfo      = DeviceInfoSet;
    pdmData->lpdi          = DeviceInfoData;

    //
    // validate expectations
    //
    MYASSERT(pdmData->hDevInfo != NULL);
    MYASSERT(pdmData->lpdi != NULL);
    MYASSERT(pdmData->lpdi->DevInst != 0);

    ZeroMemory(&PropPage,sizeof(PropPage));

    //
    // create the Resources Property Page
    //
    PropPage.dwSize        = sizeof(PROPSHEETPAGE);
    PropPage.dwFlags       = PSP_DEFAULT | PSP_USECALLBACK;
    PropPage.hInstance     = MyDllModuleHandle;
    PropPage.pszTemplate   = MAKEINTRESOURCE(IDD_DEF_DEVRESOURCE_PROP);
    PropPage.pszIcon       = NULL;
    PropPage.pszTitle      = NULL;
    PropPage.pfnDlgProc    = pResourcePickerDlgProc;
    PropPage.lParam        = (LPARAM)pdmData;
    PropPage.pfnCallback   = pResourcePickerPropPageCallback;
#ifdef _UNICODE
    PropPage.dwFlags      |= PSP_USEFUSIONCONTEXT;
    PropPage.hActCtx       = NULL;
#endif

    return CreatePropertySheetPage(&PropPage);

} // GetResourceSelectionPage


//
// CreatePropertySheetPage - callback function
//
UINT CALLBACK pResourcePickerPropPageCallback(
    HWND hwnd,
    UINT uMsg,
    LPPROPSHEETPAGE ppsp
)
/*++

Routine Description:

    Callback to handle cleanup of the property sheet

Arguments:

   Standard PropSheetPageProc arguments.

Return Value:

   Standard PropSheetPageProc return.

--*/
{
    switch (uMsg) {
        //case PSPCB_ADDREF:
        //    break;

        case PSPCB_CREATE:
            break;

        case PSPCB_RELEASE:
            //
            // release the memory we've previously allocated, outside of the actual dialog
            //
            if (ppsp->lParam != 0) {
                LPDMPROP_DATA pdmData = (LPDMPROP_DATA)(ppsp->lParam);

                MyFree(pdmData);
            }
            break;
    }

    return TRUE;

}

//
// Main dialog proceedure
//


INT_PTR
CALLBACK
pResourcePickerDlgProc(
   HWND   hDlg,
   UINT   message,
   WPARAM wParam,
   LPARAM lParam
   )

/*++

Routine Description:

    This routine provides the dialog box procedure for the main resource
    picker property page. MEMPHIS COMPATIBLE.

Arguments:

   Standard dialog box procedure arguments.

Return Value:

   Standard dialog box procedure return.

--*/

{
    LPDMPROP_DATA   lpdmpd = NULL;

    if (message == WM_INITDIALOG) {
        lpdmpd = (LPDMPROP_DATA)((LPPROPSHEETPAGE)lParam)->lParam;
        SetWindowLongPtr(hDlg, DWLP_USER, (LPARAM)lpdmpd);
    } else {
        lpdmpd = (LPDMPROP_DATA)GetWindowLongPtr(hDlg, DWLP_USER);
    }

    switch (message) {

        //
        // initialize
        //
        case WM_INITDIALOG: {

            HICON           hIcon = NULL;
            int             iIcon = 0, iIndex = 0;
            ULONG           ulSize;
            PDEVICE_INFO_SET pDeviceInfoSet;
            HMACHINE        hMachine;

            lpdmpd->himlResourceImages = NULL;
            lpdmpd->CurrentLC = 0;
            lpdmpd->CurrentLCType = 0;
            lpdmpd->MatchingLC = 0;
            lpdmpd->MatchingLCType = 0;
            lpdmpd->SelectedLC = 0;
            lpdmpd->SelectedLCType = 0;
            lpdmpd->hDlg = hDlg;
            lpdmpd->dwFlags = 0;

            hMachine = pGetMachine(lpdmpd);

            lpdmpd->dwFlags |= DMPROP_FLAG_CHANGESSAVED; // Nothing to save yet

            //
            // NOTE: On Windows95, since lc info is in memory, they first
            // call CM_Setup_DevNode with CM_SETUP_WRITE_LOG_CONFS flag so
            // that in-memory lc data is flushed to the registry at this
            // point.
            //

            //
            // Init the Resource's image list.
            //
            lpdmpd->himlResourceImages = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
                                                  GetSystemMetrics(SM_CYSMICON),
                                                  ILC_MASK, // | ILC_SHARED,
                                                  1,
                                                  1);
            //
            // add icons to image list
            //
            for (iIcon = IDI_RESOURCEFIRST;iIcon < IDI_RESOURCELAST;++iIcon) {
                //
                // resource icon
                //
                hIcon = LoadIcon(MyDllModuleHandle, MAKEINTRESOURCE(iIcon));
                iIndex = ImageList_AddIcon(lpdmpd->himlResourceImages, hIcon);
            }

            for (iIcon = IDI_RESOURCEOVERLAYFIRST;iIcon <= IDI_RESOURCEOVERLAYLAST;++iIcon) {
                //
                // overlay icon
                //
                hIcon = LoadIcon(MyDllModuleHandle, MAKEINTRESOURCE(iIcon));
                iIndex = ImageList_AddIcon(lpdmpd->himlResourceImages, hIcon);

                //
                // Tag this icon as an overlay icon (the first index is an
                // index into the image list (specifies the icon), the
                // second index is just an index to assign to each mask
                // (starting with 1).
                //
                ImageList_SetOverlayImage(lpdmpd->himlResourceImages,
                                          iIndex,
                                          iIcon-IDI_RESOURCEOVERLAYFIRST+1);
            }

            if(pInitDevResourceDlg(lpdmpd)) {
                lpdmpd->dwFlags &= ~DMPROP_FLAG_CHANGESSAVED; // need to save (prob because there was no config)
            }

            if (!(lpdmpd->dwFlags & DMPROP_FLAG_NO_RESOURCES)) {
                pShowConflicts(lpdmpd);
            }
            if (GuiSetupInProgress) {
                //
                // occasionally legacy devices cause resource-picker popup during setup
                // we do this here instead of create prop sheet, since I don't trust
                // people to cleanup on fail. At least here is less risky
                // clean this up in WM_DESTROY
                //
                lpdmpd->hDialogEvent = CreateEvent(NULL,TRUE,FALSE,SETUP_HAS_OPEN_DIALOG_EVENT);
                if (lpdmpd->hDialogEvent) {
                   SetEvent(lpdmpd->hDialogEvent);
                }
            } else {
                lpdmpd->hDialogEvent = NULL;
            }
            break;
        }


        //
        // cleanup
        //
        case WM_DESTROY: {

            HICON    hIcon;
            LOG_CONF LogConf;
            LONG     nItems, n;
            HWND     hList =  GetDlgItem(hDlg, IDC_DEVRES_SETTINGSLIST);
            int    Count, i;

            //
            // Clean up the ICON resource usage
            //
            if ((hIcon = (HICON)LOWORD(SendDlgItemMessage(hDlg,
                         IDC_DEVRES_ICON, STM_GETICON, 0, 0L)))) {
                DestroyIcon(hIcon);
            }

            //
            // free the LC handles that were saved in the combobox data
            //
            nItems = (LONG)SendDlgItemMessage(hDlg, IDC_DEVRES_LOGCONFIGLIST,
                                            CB_GETCOUNT, 0, 0L);

            for (n = 0; n < nItems ; n++) {
                LogConf = (LOG_CONF)SendDlgItemMessage(hDlg,
                                        IDC_DEVRES_LOGCONFIGLIST,
                                        CB_GETITEMDATA, n, 0L);
                CM_Free_Log_Conf_Handle(LogConf);
            }

            if (lpdmpd->CurrentLC != 0) {
                CM_Free_Log_Conf_Handle(lpdmpd->CurrentLC);
            }

            ListView_DeleteAllItems(hList); // this will destroy all data

            if (lpdmpd->himlResourceImages) {
                ImageList_Destroy(lpdmpd->himlResourceImages);
            }

            if (lpdmpd->hDialogEvent) {
                //
                // we were holding up setup, now let setup proceed
                //
                ResetEvent(lpdmpd->hDialogEvent);
                CloseHandle(lpdmpd->hDialogEvent);
                lpdmpd->hDialogEvent = NULL;
            }
            // MyFree(lpdmpd); - do this in pResourcePickerPropPageCallback instead
            break;
        }

        case WM_COMMAND:
            //
            // old-style controls
            //

            switch(LOWORD(wParam)) {
                case IDC_DEVRES_USESYSSETTINGS: {
                    //
                    // consider resource settings to have changed
                    //
                    lpdmpd->dwFlags &= ~DMPROP_FLAG_CHANGESSAVED;
                    PropSheet_Changed(GetParent(hDlg), hDlg);

                    if (IsDlgButtonChecked(hDlg, (int)wParam)) {
                        //
                        // Revert back to allocated display, if any
                        //
                        lpdmpd->dwFlags |= DMPROP_FLAG_USESYSSETTINGS;
                        pSelectLogConf(lpdmpd,(LOG_CONF)0,ALLOC_LOG_CONF,TRUE);
                    } else {
                        //
                        // Allow editing
                        //
                        lpdmpd->dwFlags &= ~DMPROP_FLAG_USESYSSETTINGS;
                    }
                    pShowUpdateEdit(lpdmpd);           // update controls

                    break;
                }

                case IDC_DEVRES_LOGCONFIGLIST: {
                    //
                    // drop-down list action
                    //
                    switch (HIWORD(wParam)) {
                        case CBN_SELENDOK: {
                            ULONG    ulIndex = 0;
                            int      iItem;
                            LOG_CONF SelLC;
                            HWND     hwndLC = GetDlgItem(hDlg, IDC_DEVRES_LOGCONFIGLIST);

                            //
                            // If there is not a Log Config selected, then bail
                            //
                            iItem = (int)SendMessage(hwndLC, CB_GETCURSEL, 0, 0);
                            if(iItem != CB_ERR) {
                                SelLC = (LOG_CONF)SendMessage(hwndLC,CB_GETITEMDATA, (WPARAM)iItem,(LPARAM)0);
                            } else {
                                SelLC = (LOG_CONF)0;
                            }
                            if(SelLC != lpdmpd->SelectedLC) {
                                pSelectLogConf(lpdmpd,SelLC,lpdmpd->ConfigListLCType,FALSE);
                            }
                            //
                            // I prob don't need this here, but I'm playing safe!
                            //
                            lpdmpd->dwFlags &= ~DMPROP_FLAG_CHANGESSAVED;
                            break;
                        }
                    }
                    break;
                }

                case IDC_DEVRES_CHANGE: {
                    //
                    // change selected setting
                    //
                    pChangeCurrentResSetting(lpdmpd);
                    break;
                }

                case IDC_DEVRES_MAKEFORCED: {
                    //
                    // possibly allow editing (after we've shown message)
                    // when we get here, always show a configuration
                    //

                    if(lpdmpd->dwFlags & DMPROP_FLAG_FORCEDONLY) {
                        lpdmpd->dwFlags &= ~DMPROP_FLAG_CHANGESSAVED; // need to save
                    }
                    pShowViewAllEdit(lpdmpd);
                    //
                    // select in the first available config to edit
                    //
                    pSelectLogConf(lpdmpd,(LOG_CONF)0,ALLOC_LOG_CONF,TRUE);

                    //
                    // ensure we have reasonable focus for accessability
                    //
                    PostMessage(hDlg,WM_USER_FOCUS,IDC_DEVRES_SETTINGSLIST,0);
                    break;
                }

                default:
                    break;
            }
            break;

        case WM_USER_FOCUS:
            //
            // change focus to DlgItem wParam
            //
            SetFocus(GetDlgItem(hDlg,(int)wParam));
            return TRUE;

        case WM_NOTIFY: {
            //
            // new controls & property codes
            //
            NMHDR * pHdr = (NMHDR*)lParam;

            switch (pHdr->code) {

                case PSN_SETACTIVE: {

                    HICON           hIcon = NULL, hOldIcon = NULL;
                    TCHAR           szString[MAX_PATH];
                    ULONG           ulSize = 0;
                    HMACHINE        hMachine;

                    hMachine = pGetMachine(lpdmpd);


                    //
                    // Set the ICON and device description
                    //
                    if (SetupDiLoadClassIcon(&lpdmpd->lpdi->ClassGuid, &hIcon, NULL)) {

                        if ((hOldIcon = (HICON)LOWORD(SendDlgItemMessage(hDlg, IDC_DEVRES_ICON,
                                                                         STM_SETICON,
                                                                         (WPARAM)hIcon, 0L)))) {
                            DestroyIcon(hOldIcon);
                        }
                    }

                    //
                    // First try to get the device's friendly name, then fall back to its description,
                    // and finally, use the "Unknown Device" description.
                    //
                    ulSize = MAX_PATH * sizeof(TCHAR);
                    if (CM_Get_DevInst_Registry_Property_Ex(lpdmpd->lpdi->DevInst,
                                                         CM_DRP_FRIENDLYNAME,
                                                         NULL, (LPBYTE)szString,
                                                         &ulSize, 0,hMachine) != CR_SUCCESS) {

                        ulSize = MAX_PATH * sizeof(TCHAR);
                        if (CM_Get_DevInst_Registry_Property_Ex(lpdmpd->lpdi->DevInst,
                                                             CM_DRP_DEVICEDESC,
                                                             NULL, (LPBYTE)szString,
                                                             &ulSize, 0,hMachine) != CR_SUCCESS) {

                            LoadString(MyDllModuleHandle, IDS_DEVNAME_UNK, szString, MAX_PATH);
                        }
                    }
                    SetDlgItemText(hDlg, IDC_DEVRES_DEVDESC, szString);
                    break;
                }

                case PSN_APPLY:  {
                    BOOL      bRet = FALSE;
                    //
                    // If there were Changes and they haven't been saved,
                    // then save them.
                    // consider some special cases as "haven't been saved"
                    //
                    if((lpdmpd->CurrentLC == 0) && (lpdmpd->dwFlags&DMPROP_FLAG_FIXEDCONFIG)) {
                        lpdmpd->dwFlags &= ~DMPROP_FLAG_CHANGESSAVED;
                    }

                    switch(pOkToSave(lpdmpd)) {
                        case IDNO:
                            //
                            // proceed without saving
                            //
                            bRet = TRUE;
                            break;
                        case IDCANCEL:
                            //
                            // don't proceed
                            //
                            bRet = FALSE;
                            break;
                        case IDYES:
                            //
                            // proceed and save
                            //
                            bRet = pSaveDevResSettings(lpdmpd);
                            #if 0
                            if (bRet) {
                                if ((lpdmpd->lpdi)->Flags &  DI_NEEDREBOOT) {
                                    PropSheet_RebootSystem(GetParent(hDlg));
                                } else if ((lpdmpd->lpdi)->Flags &  DI_NEEDRESTART) {
                                    PropSheet_RestartWindows(GetParent(hDlg));
                                }
                            #endif
                            if (bRet) {
                                //
                                // This page doesn't support roll-back, if we saved
                                // something then we're committed, disable the cancel
                                // botton.
                                //
                                PropSheet_CancelToClose(GetParent(hDlg));
                            }
                            break;
                        default:
                            MYASSERT(FALSE /* pOkToSave returned invalid value */);
                            bRet = FALSE;
                            break;
                    }

                    SetWindowLongPtr(hDlg, DWLP_MSGRESULT, bRet ? PSNRET_NOERROR : PSNRET_INVALID_NOCHANGEPAGE);
                    return TRUE;
                }

                case LVN_DELETEALLITEMS:
                    if (pHdr->idFrom == IDC_DEVRES_SETTINGSLIST) {
                        return FALSE;   // we want LVN_DELETEITEM messages
                    }
                    break;

                case LVN_DELETEITEM: {
                    LPNMLISTVIEW pListView = (LPNMLISTVIEW)pHdr;
                    if (pHdr->idFrom == IDC_DEVRES_SETTINGSLIST) {
                        PITEMDATA   pItemData = (PITEMDATA)(LPVOID)(pListView->lParam);
                        //
                        // when an item is deleted, destroy associated data
                        //
                        if (pItemData->MatchingResDes) {
                            CM_Free_Res_Des_Handle(pItemData->MatchingResDes);
                        }
                        MyFree(pItemData);
                    }
                    break;
                }
                    EnableWindow(GetDlgItem(hDlg, IDC_DEVRES_CHANGE), FALSE);
                    break;

                case LVN_ITEMCHANGED:
                    //
                    // If the item change is comming from the resource
                    // list, and there is a logconfig to be edited:
                    //
                    if (pHdr->idFrom == IDC_DEVRES_SETTINGSLIST) {
                        //
                        // see if we should enable resource change
                        //
                        pCheckEnableResourceChange(lpdmpd);
                    }
                    break;

                case NM_DBLCLK:
                    //
                    // If the double click is from the SETTINGS list
                    // AND the DEVRES_CHANGE button is enabled, then
                    // allow the change.
                    //
                    if (pHdr->idFrom == IDC_DEVRES_SETTINGSLIST) {
                        //
                        // this routine should check that we can change settings
                        //
                        pChangeCurrentResSetting(lpdmpd);
                    }
                    break;
            }
            break;
        }

        case WM_SYSCOLORCHANGE: {

            HWND hChildWnd = GetWindow(hDlg, GW_CHILD);

            while (hChildWnd != NULL) {
                SendMessage(hChildWnd, WM_SYSCOLORCHANGE, wParam, lParam);
                hChildWnd = GetWindow(hChildWnd, GW_HWNDNEXT);
            }
            break;
        }

        case WM_HELP:      // F1
            WinHelp(((LPHELPINFO)lParam)->hItemHandle, DEVRES_HELP, HELP_WM_HELP, (ULONG_PTR)DevResHelpIDs);
            break;

        case WM_CONTEXTMENU:      // right mouse click
            WinHelp((HWND)wParam, DEVRES_HELP, HELP_CONTEXTMENU, (ULONG_PTR)DevResHelpIDs);
            break;
   }

   return FALSE;

}

//
// Helper functions
//

HMACHINE
pGetMachine(
    LPDMPROP_DATA   lpdmpd
    )
/*++

Routine Description:

    Retrieve Machine Handle

Arguments:

    lpdmpd - Property Data

Return Value:

    handle

--*/
{
    HMACHINE hMachine;
    PDEVICE_INFO_SET pDeviceInfoSet;

    if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdmpd->hDevInfo))) {
        return NULL;
    }
    hMachine = pDeviceInfoSet->hMachine;
    UnlockDeviceInfoSet(pDeviceInfoSet);
    return hMachine;
}

BOOL
pInitDevResourceDlg(
    LPDMPROP_DATA   lpdmpd
    )
/*++

Routine Description:

    This routine intializes the main resource picker property page.
    MEMPHIS COMPATIBLE.

Arguments:


Return Value:

    TRUE if "not saved"

--*/

{
    HWND            hDlg = lpdmpd->hDlg;
    CONFIGRET       Status = CR_SUCCESS;
    BOOL            bHasCurrent = FALSE;
    BOOL            bShowCurrent = FALSE;
    BOOL            bHasForced = FALSE;
    BOOL            bNoForcedConfig = FALSE;
    BOOL            bNeedsForcedConfig = FALSE;
    BOOL            bHasConfigList = FALSE;
    LV_COLUMN       LvCol;
    HWND            hWndList = NULL;
    TCHAR           szString[MAX_PATH], szTemp[MAX_PATH], szConfigType[MAX_PATH],
                    szConfig[MAX_PATH];
    ULONG           ulIndex = 0, ulSize = 0, DevStatus = 0, DevProblem = 0;
    DWORD           BusType = (DWORD)(-1);
    LOG_CONF        LogConf;
    DWORD           dwPriority = 0;
    WORD            wItem;
    ULONG           ConfigFlags;
    HMACHINE        hMachine = NULL;
    PDEVICE_INFO_SET pDeviceInfoSet;
    int             iIndex;
    BOOL            bHasPrivs = FALSE;
    //
    // Set initial control states
    //
    pHideAllControls(lpdmpd);

    //
    // determine priv token
    // security checks are visual only
    // real security checks are done in umpnpmgr
    //

    bHasPrivs = pSetupDoesUserHavePrivilege(SE_LOAD_DRIVER_NAME);

    hMachine = pGetMachine(lpdmpd);

    //
    // retrieves current configuration, if any
    //
    bHasCurrent = pGetCurrentConfig(lpdmpd);

    //
    // We sometimes get called to show this page even if the device
    // doesn't consume any resources. Check for that case and if so, just
    // display an informational message and disable everything else.
    //

    if (!pDevRequiresResources(lpdmpd->lpdi->DevInst,hMachine)) {

        //
        // This device has no resources
        //
        pShowViewNoResources(lpdmpd);
        lpdmpd->dwFlags |= DMPROP_FLAG_NO_RESOURCES;
        goto Final;
    }

    //
    // Initialize the ListView control
    //
    hWndList = GetDlgItem(hDlg, IDC_DEVRES_SETTINGSLIST);
    LvCol.mask = LVCF_TEXT;

    if (LoadString(MyDllModuleHandle, IDS_RESOURCETYPE, szString, MAX_PATH)) {
        LvCol.pszText = (LPTSTR)szString;
        ListView_InsertColumn(hWndList, 0, (LV_COLUMN FAR *)&LvCol);
    }

    if (LoadString(MyDllModuleHandle, IDS_RESOURCESETTING, szString, MAX_PATH)) {
        LvCol.pszText = (LPTSTR)szString;
        ListView_InsertColumn(hWndList, 1, (LV_COLUMN FAR *)&LvCol);
    }

    ListView_SetImageList(hWndList,lpdmpd->himlResourceImages, LVSIL_SMALL);
    //
    // Get DevStatus & DevProblem here, we may use this info further down
    //
    if (CM_Get_DevNode_Status_Ex(&DevStatus, &DevProblem, lpdmpd->lpdi->DevInst,
                              0,hMachine) != CR_SUCCESS) {
        //
        // we should never get here, show this as a problem
        //
        lpdmpd->dwFlags |= DMPROP_FLAG_HASPROBLEM;
    } else if (DevStatus & DN_HAS_PROBLEM) {
        //
        // cache problem flag away
        //
        lpdmpd->dwFlags |= DMPROP_FLAG_HASPROBLEM;
    } else if (DevStatus & DN_PRIVATE_PROBLEM) {
        //
        // driver indicates problem
        // for now, do same as above
        //
        lpdmpd->dwFlags |= DMPROP_FLAG_HASPROBLEM;
    }

    if (bIsMultiFunctionChild(lpdmpd->lpdi,hMachine)) {
        //
        // If this is a MultiFunction Child, disable all change controls, put up
        // special text, and show the alloc config
        //
        pShowViewMFReadOnly(lpdmpd,FALSE);
        goto Final;
    }

    //
    // begin with read-only view, assuming settings are system
    //
    lpdmpd->dwFlags |= DMPROP_FLAG_USESYSSETTINGS;

    if (CM_Get_First_Log_Conf_Ex(NULL,
                                    lpdmpd->lpdi->DevInst,
                                    FORCED_LOG_CONF,
                                    hMachine) == CR_SUCCESS) {
        //
        // the user currently has a forced config
        //
        lpdmpd->dwFlags &= ~DMPROP_FLAG_USESYSSETTINGS;
        bHasForced = TRUE;
    }

    bShowCurrent = pShowViewReadOnly(lpdmpd,bHasPrivs);
    if (!bHasPrivs || hMachine) {
        //
        // if we don't have enough priv's
        // or we're displaying resources of a remote machine
        // bottle out here
        // we'll either be displaying current resources
        // or displaying a problem
        //
        goto Final;
    }
    if(!bHasForced) {
        //
        // Check bus we're using
        // to see if it's one of the read-only displays
        //
        ulSize = sizeof(BusType);
        if (CM_Get_DevInst_Registry_Property_Ex(lpdmpd->lpdi->DevInst,
                                             CM_DRP_LEGACYBUSTYPE,
                                             NULL, (LPBYTE)&BusType,
                                             &ulSize, 0,hMachine) != CR_SUCCESS) {
            BusType = (DWORD)InterfaceTypeUndefined;
        }

        if (BusType != (DWORD)InterfaceTypeUndefined) {
            int InterfaceItem;

            for(InterfaceItem = 0; ResourcePickerReadOnlyInterfaces[InterfaceItem] != InterfaceTypeUndefined; InterfaceItem++) {
                if (BusType == (DWORD)ResourcePickerReadOnlyInterfaces[InterfaceItem]) {
                    //
                    // Bus is one that we do not allow forced configs
                    // we can skip all the funky code below
                    //
                    // this is a good thing for 64-bit PCI
                    //
                    goto Final;
                }
            }
        }
    }

    //
    // Retrieve alternate configurations for this device
    //
    if (bHasCurrent) {
        //
        // Current config (if any) is indicated with zero handle
        //
        LoadString(MyDllModuleHandle, IDS_CURRENTCONFIG, szString, MAX_PATH);

        iIndex = (int)SendDlgItemMessage(hDlg, IDC_DEVRES_LOGCONFIGLIST,
                                         CB_ADDSTRING, (WPARAM)0, (LPARAM)(LPSTR)szString);
        SendDlgItemMessage(hDlg, IDC_DEVRES_LOGCONFIGLIST, CB_SETITEMDATA,(WPARAM)iIndex, (LPARAM)0);
        SendDlgItemMessage(hDlg, IDC_DEVRES_LOGCONFIGLIST, CB_SETCURSEL,(WPARAM)0, (LPARAM)0);
    }
    //
    // now fill in alternate configurations
    // override preferred over basic (in that order)
    // don't use Filtered, filtered might remove configs that require reboot
    // but they are fine to have here
    //
    if(CM_Get_First_Log_Conf_Ex(&LogConf,lpdmpd->lpdi->DevInst,OVERRIDE_LOG_CONF,hMachine) == CR_SUCCESS) {
        lpdmpd->ConfigListLCType = OVERRIDE_LOG_CONF;
        LoadString(MyDllModuleHandle, IDS_OVERRIDECONFIG, szConfigType, MAX_PATH);
        bHasConfigList = TRUE;
    } else if(CM_Get_First_Log_Conf_Ex(&LogConf,lpdmpd->lpdi->DevInst,BASIC_LOG_CONF,hMachine) == CR_SUCCESS) {
        lpdmpd->ConfigListLCType = BASIC_LOG_CONF;
        LoadString(MyDllModuleHandle, IDS_BASICCONFIG, szConfigType, MAX_PATH);
        bHasConfigList = TRUE;
    } else {
        //
        // If there are no alternate configs, we cannot allow a forced config
        //
        bNoForcedConfig = TRUE; // cannot force
        bHasConfigList = FALSE;
        lpdmpd->ConfigListLCType = BASIC_LOG_CONF;
        lpdmpd->dwFlags |= DMPROP_FLAG_SINGLE_CONFIG;
    }
    if(bHasConfigList) {

        ulIndex = 0;
        if (!pConfigHasNoAlternates(lpdmpd,LogConf)) {
            //
            // first configuration has more than one alternative
            //
            lpdmpd->dwFlags &= ~DMPROP_FLAG_SINGLE_CONFIG;
        } else {
            //
            // begin with the assumption there is a single fixed 'basic' config
            // we will generally be proved wrong
            //
            lpdmpd->dwFlags |= DMPROP_FLAG_SINGLE_CONFIG;
        }

        while (Status == CR_SUCCESS) {
            //
            // Add this config to the Combobox
            //
            wsprintf(szTemp, TEXT("%s %04u"), szConfigType, ulIndex);

            wItem = (WORD)SendDlgItemMessage(hDlg, IDC_DEVRES_LOGCONFIGLIST,
                                             CB_ADDSTRING, 0,
                                             (LPARAM)(LPSTR)szTemp);

            //
            // Save the log config handle as the item data in the combobox
            //
            SendDlgItemMessage(hDlg, IDC_DEVRES_LOGCONFIGLIST, CB_SETITEMDATA,
                               wItem, (LPARAM)LogConf);

            //
            // Get the next config
            //
            Status = CM_Get_Next_Log_Conf_Ex(&LogConf, LogConf, 0,hMachine);
            ulIndex++;
        }

        if (ulIndex > 1) {
            //
            // there is more than one config
            //
            lpdmpd->dwFlags &= ~DMPROP_FLAG_SINGLE_CONFIG;
        }

        if (lpdmpd->dwFlags & DMPROP_FLAG_SINGLE_CONFIG) {
            bNoForcedConfig = TRUE;
        }

        if (bHasCurrent) {
            //
            // try to find a matching LC now, and if we could find one,
            // re-load current display (this applies editable ranges to the resources)
            //
            if(pFindMatchingAllocConfig(lpdmpd)) {
                pLoadCurrentConfig(lpdmpd,TRUE);
            }
        }
    } else {
    }
    //
    // Get ConfigFlags here, we may use this info further down
    //
    ulSize = sizeof(ConfigFlags);
    if (CM_Get_DevInst_Registry_Property_Ex(lpdmpd->lpdi->DevInst,
                                         CM_DRP_CONFIGFLAGS,
                                         NULL, (LPBYTE)&ConfigFlags,
                                         &ulSize, 0,hMachine) != CR_SUCCESS) {
        ConfigFlags = 0;
    }
    if (ConfigFlags & CONFIGFLAG_NEEDS_FORCED_CONFIG) {
        //
        // registry says that we need a forced config
        // registry can only say this to us once
        //
        bNeedsForcedConfig = TRUE;
        ConfigFlags &= ~CONFIGFLAG_NEEDS_FORCED_CONFIG;
        CM_Set_DevInst_Registry_Property_Ex(lpdmpd->lpdi->DevInst,
                                         CM_DRP_CONFIGFLAGS,
                                         (LPBYTE)&ConfigFlags,
                                         sizeof(ConfigFlags),
                                         0,
                                         hMachine);
    }

    //
    // determine if it can be software-config'd or not
    // we need to do this prior to any initial display
    //
    dwPriority = pGetMinLCPriority(lpdmpd->lpdi->DevInst, lpdmpd->ConfigListLCType,hMachine);
    if (dwPriority < LCPRI_HARDRECONFIG) {
        //
        // doesn't need to be manually configured
        //
        lpdmpd->dwFlags &= ~DMPROP_FLAG_FORCEDONLY;
    } else {
        //
        // this cannot be software config'd
        // FORCEDONLY & bNoForcedConfig is a quandry, shouldn't happen
        //
        lpdmpd->dwFlags |= DMPROP_FLAG_FORCEDONLY;
        if(!bHasConfigList) {
            MYASSERT(bHasConfigList);
        } else {
            MYASSERT(!bNoForcedConfig);
            bNoForcedConfig = FALSE;
        }
    }

    //
    // Try to determine initial display
    //
    // we've already covered pShowViewNoResources (no actual or potential configs)
    // and pShowViewMFReadOnly (it's a multi-function device)
    //
    // we're currently showing as pShowViewReadOnly
    //
    // some cases....
    // (1) show forced config, don't allow auto-config (config flags say requires forced)
    // (2) show forced config, allow auto-config
    // (3) don't show any config, but maybe show a forced-config button
    // (4) auto-config, don't allow forced config
    // (5) show auto-config, allow forced-config
    //
    if (bNeedsForcedConfig) {
        if (!bHasConfigList) {
            MYASSERT(bHasConfigList);
            bNeedsForcedConfig = FALSE;
        } else {
            MYASSERT(!bNoForcedConfig);
            bNoForcedConfig = FALSE;
            if (bHasForced) {
                //
                // already got one, but we'll go through the motions
                // we'll show what we have, allow user to change it
                // but we wont needlessly save it
                //
                bNeedsForcedConfig = FALSE;
            }
            //
            // caller said that device must have forced config, so go immediately there
            // = case (1) unless we've otherwise said we cannot have a forced config
            //
            lpdmpd->dwFlags |= DMPROP_FLAG_FORCEDONLY;
            pSelectLogConf(lpdmpd,(LOG_CONF)0,ALLOC_LOG_CONF,TRUE);
            pShowViewAllEdit(lpdmpd);
            goto Final;
        }
    }
    if ((!bShowCurrent) || (lpdmpd->dwFlags & DMPROP_FLAG_HASPROBLEM)) {
        //
        //  determine between pShowViewNoAlloc and pShowViewNeedForced
        //
        if (bNoForcedConfig) {
            //
            // there is a problem - device doesn't currently have a current configuration
            // but we don't have the option of letting them set forced config
            // so this ends up display only (tough-luck scenario)
            // if there are current resources, show them
            //
            pShowViewReadOnly(lpdmpd,FALSE);
        } else {
            //
            // we show the problem and we give the user
            // an option to force config
            //
            pShowViewNeedForced(lpdmpd);
        }
        goto Final;
    }
    if (!bHasConfigList) {
        //
        // If we have a current config, but no basic configs, we just display what we have
        // and don't give option to edit
        //
        pShowViewReadOnly(lpdmpd,FALSE);
        goto Final;
    }
    if ((lpdmpd->dwFlags & DMPROP_FLAG_USESYSSETTINGS) && bNoForcedConfig) {
        //
        // we can't force a bNoForcedConfig item - display only
        //
        pShowViewReadOnly(lpdmpd,FALSE);
        goto Final;
    }
    //
    // we already have and will be displaying a current config
    //
    pShowViewAllEdit(lpdmpd);
    bNeedsForcedConfig = (BOOL)!bHasCurrent; // rarely, if ever, will this return TRUE

  Final:

    return bNeedsForcedConfig;

} // InitDevResourceDlg

PITEMDATA
pGetResourceToChange(
    IN  LPDMPROP_DATA   lpdmpd,
    OUT int             *pCur
    )
/*++

Routine Description:

    Gets resource to change
    NULL if we cannot change resource

Arguments:

    lpdmpd = dialog data
    pCur = (out) index

Return Value:

    PITEMDATA saved for selected resource

--*/
{
    HWND     hList =  GetDlgItem(lpdmpd->hDlg, IDC_DEVRES_SETTINGSLIST);
    PITEMDATA pItemData = NULL;
    int     iCur;

    //
    // first check the obvious
    //
    if (lpdmpd->dwFlags & DMPROP_FLAG_VIEWONLYRES) {
        //
        // no editing allowed
        //
        return NULL;
    }
    if (lpdmpd->dwFlags & DMPROP_FLAG_USESYSSETTINGS) {
        //
        // showing system settings
        //
        return NULL;
    }


    //
    // Check if there is a selected item.
    // If yes, then activate the change button
    // if the LC allows editing.
    //
    iCur = (int)ListView_GetNextItem(hList,-1, LVNI_SELECTED);
    if (iCur == LB_ERR) {
        //
        // no selection
        //
        return NULL;
    }
    pItemData = (PITEMDATA)pGetListViewItemData(hList, iCur, 0);
    if (pItemData == NULL) {
        //
        // shouldn't happen
        //
        MYASSERT(pItemData);
        return NULL;
    }
    if (pItemData->bFixed) {
        //
        // this is an un-editable setting
        //
        return NULL;
    }
    if (pItemData->MatchingResDes == (RES_DES)0) {
        //
        // should be caught by bFixed
        //
        MYASSERT(pItemData->MatchingResDes != (RES_DES)0);
        return NULL;
    }
    //
    // we're happy
    //
    if (pCur) {
        *pCur = iCur;
    }
    return pItemData;
}

VOID
pCheckEnableResourceChange(
    LPDMPROP_DATA   lpdmpd
    )
/*++

Routine Description:

    enables/disable change button

Arguments:


Return Value:

    none

--*/
{
#if 0 // this seems to confuse people
    EnableWindow(GetDlgItem(lpdmpd->hDlg, IDC_DEVRES_CHANGE),
                    pGetResourceToChange(lpdmpd,NULL)!=NULL);
#endif // 0

    //
    // show this button enabled if we are in EDIT mode
    //
    EnableWindow(GetDlgItem(lpdmpd->hDlg, IDC_DEVRES_CHANGE),
                 (lpdmpd->dwFlags & DMPROP_FLAG_VIEWONLYRES)==0 &&
                 (lpdmpd->dwFlags & DMPROP_FLAG_USESYSSETTINGS)==0);
}

BOOL
pDevHasConfig(
    DEVINST     DevInst,
    ULONG       ulConfigType,
    HMACHINE    hMachine
    )

/*++

Routine Description:

    This routine determines whether a log config of the specified type
    exists for this device instance.
    MEMPHIS COMPATIBLE.

Arguments:

    DevInst         Device instance to query log configs for.

    ulConfigType    Specifies the type of log conf to check for the presense of.

Return Value:

   TRUE if the device has a config of that type and FALSE if it does not.

--*/

{
    BOOL bRet = (CM_Get_First_Log_Conf_Ex(NULL, DevInst, ulConfigType,hMachine) == CR_SUCCESS);
    return bRet;

} // DevHasConfig

DWORD
pGetMinLCPriority(
    IN DEVINST DevInst,
    IN ULONG   ulConfigType,
    IN HMACHINE hMachine
    )

/*++

Routine Description:

    This routine returns the minimum priority value of all log confs of the
    specified type for this device. MEMPHIS COMPATIBLE.

Arguments:

    DevInst         Device instance to query log configs for.

    ulConfigType    Specifies the type of log conf.

Return Value:

   Returns the minimum priority value found or LCPRI_LASTSOFTCONFIG if no priorities
   are found.

--*/

{
    CONFIGRET Status = CR_SUCCESS;
    ULONG priority, minPriority = MAX_LCPRI;
    LOG_CONF LogConf, tempLC;
    BOOL FoundOneLogConfWithPriority = FALSE;

    //
    // Walk through each log conf of this type for this device and
    // save the smallest value.
    //

    Status = CM_Get_First_Log_Conf_Ex(&LogConf, DevInst, ulConfigType,hMachine);
    while (Status == CR_SUCCESS) {

        if (CM_Get_Log_Conf_Priority_Ex(LogConf, &priority, 0,hMachine) == CR_SUCCESS) {
            FoundOneLogConfWithPriority = TRUE;
            minPriority = min(minPriority, priority);
        }

        tempLC = LogConf;
        Status = CM_Get_Next_Log_Conf_Ex(&LogConf, LogConf, 0,hMachine);
        CM_Free_Log_Conf_Handle(tempLC);
    }

    if(FoundOneLogConfWithPriority) {
        return minPriority;
    } else {
        //
        // None of the LogConfigs had an associated priority. This is common on
        // NT, because the bus drivers don't specify ConfigMgr-style priorities
        // when responding to IRQ_MN_QUERY_RESOURCE_REQUIREMENTS.  Since these
        // cases are all PnP bus drivers, however, it is most correct to specify
        // these LogConfigs as soft-settable.
        //
        return LCPRI_LASTSOFTCONFIG;
    }

} // GetMinLCPriority

BOOL
pDevRequiresResources(
    DEVINST DevInst,
    HMACHINE hMachine
    )
{
    if (CM_Get_First_Log_Conf_Ex(NULL, DevInst, BASIC_LOG_CONF,hMachine) == CR_SUCCESS) {
        return TRUE;
    }

    if (CM_Get_First_Log_Conf_Ex(NULL, DevInst, FILTERED_LOG_CONF,hMachine) == CR_SUCCESS) {
        return TRUE;
    }

    if (CM_Get_First_Log_Conf_Ex(NULL, DevInst, OVERRIDE_LOG_CONF,hMachine) == CR_SUCCESS) {
        return TRUE;
    }

    if (CM_Get_First_Log_Conf_Ex(NULL, DevInst, FORCED_LOG_CONF,hMachine) == CR_SUCCESS) {
        return TRUE;
    }

    if (CM_Get_First_Log_Conf_Ex(NULL, DevInst, BOOT_LOG_CONF,hMachine) == CR_SUCCESS) {
        return TRUE;
    }

    if (CM_Get_First_Log_Conf_Ex(NULL, DevInst, ALLOC_LOG_CONF,hMachine) == CR_SUCCESS) {
        return TRUE;
    }

    return FALSE;

} // DevRequiresResources

BOOL
pGetCurrentConfig(
    IN OUT  LPDMPROP_DATA lpdmpd
    )

/*++

Routine Description:

    This routine determines the current known configuration
    current configs are either forced, alloc or boot configs.

Arguments:

    lpdmpd          Property data.

Return Value:

   TRUE if we set the current config

--*/

{
    PDEVICE_INFO_SET pDeviceInfoSet;
    HMACHINE         hMachine;
    ULONG            Status;
    ULONG            Problem;

    MYASSERT(lpdmpd!=NULL);
    MYASSERT(lpdmpd->lpdi!=NULL);
    MYASSERT(lpdmpd->CurrentLC==0);
    MYASSERT(lpdmpd->lpdi->DevInst!=0);

    if (lpdmpd==NULL ||
        lpdmpd->lpdi==NULL ||
        lpdmpd->lpdi->DevInst==0) {
        return FALSE;
    }

    lpdmpd->dwFlags &= ~DMPROP_FLAG_DISPLAY_MASK;

    if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdmpd->hDevInfo))) {
            return FALSE;
    }

    hMachine = pDeviceInfoSet->hMachine;

    UnlockDeviceInfoSet (pDeviceInfoSet);

    if (CM_Get_DevNode_Status_Ex(&Status, &Problem, lpdmpd->lpdi->DevInst,
                              0,hMachine) != CR_SUCCESS) {
        Problem = 0;
        Status = 0;
    } else if((Status & DN_HAS_PROBLEM)==0) {
        //
        // If this device is running, does this devinst have a ALLOC log config?
        //
        if (CM_Get_First_Log_Conf_Ex(&lpdmpd->CurrentLC,
                                     lpdmpd->lpdi->DevInst,
                                     ALLOC_LOG_CONF,
                                     hMachine) == CR_SUCCESS) {

            lpdmpd->dwFlags |= DMPROP_FLAG_DISPLAY_ALLOC;
            lpdmpd->CurrentLCType = ALLOC_LOG_CONF;
            return TRUE;
        }
    }
    //
    // If no config so far, does it have a FORCED log config?
    //

    if (CM_Get_First_Log_Conf_Ex(&lpdmpd->CurrentLC,
                                    lpdmpd->lpdi->DevInst,
                                    FORCED_LOG_CONF,
                                    hMachine) == CR_SUCCESS) {

        lpdmpd->dwFlags |= DMPROP_FLAG_DISPLAY_FORCED;
        lpdmpd->CurrentLCType = FORCED_LOG_CONF;
        return TRUE;
    }

    //
    // if there's a hardware-disabled problem, boot-config isn't valid
    //
    if(((Status & DN_HAS_PROBLEM)==0) || (Problem != CM_PROB_HARDWARE_DISABLED)) {
        //
        // Does it have a BOOT log config?
        //
        if (CM_Get_First_Log_Conf_Ex(&lpdmpd->CurrentLC,
                                        lpdmpd->lpdi->DevInst,
                                        BOOT_LOG_CONF,
                                        hMachine) == CR_SUCCESS) {

            lpdmpd->dwFlags |= DMPROP_FLAG_DISPLAY_BOOT;
            lpdmpd->CurrentLCType = BOOT_LOG_CONF;
            return TRUE;
        }
    }

    return FALSE;
}

void
pGetHdrValues(
    IN  LPBYTE      pData,
    IN  RESOURCEID  ResType,
    OUT PULONG64    pulValue,
    OUT PULONG64    pulLen,
    OUT PULONG64    pulEnd,
    OUT PULONG      pulFlags
    )
{
    switch (ResType) {

        case ResType_Mem: {

            PMEM_RESOURCE  pMemData = (PMEM_RESOURCE)pData;

            *pulValue = pMemData->MEM_Header.MD_Alloc_Base;
            *pulLen   = (pMemData->MEM_Header.MD_Alloc_End -
                        pMemData->MEM_Header.MD_Alloc_Base + 1);
            *pulEnd   = pMemData->MEM_Header.MD_Alloc_End;
            *pulFlags = pMemData->MEM_Header.MD_Flags;
            break;
        }

        case ResType_IO: {

            PIO_RESOURCE   pIoData = (PIO_RESOURCE)pData;

            *pulValue = pIoData->IO_Header.IOD_Alloc_Base;
            *pulLen   = (pIoData->IO_Header.IOD_Alloc_End -
                        pIoData->IO_Header.IOD_Alloc_Base + 1);
            *pulEnd   = pIoData->IO_Header.IOD_Alloc_End;
            *pulFlags = pIoData->IO_Header.IOD_DesFlags;
            break;
        }

        case ResType_DMA: {

            PDMA_RESOURCE  pDmaData = (PDMA_RESOURCE)pData;

            *pulValue = pDmaData->DMA_Header.DD_Alloc_Chan;
            *pulLen   = 1;
            *pulEnd   = *pulValue;
            *pulFlags = pDmaData->DMA_Header.DD_Flags;
            break;
        }

        case ResType_IRQ: {

            DEVRES_PIRQ_RESOURCE  pIrqData = (DEVRES_PIRQ_RESOURCE)pData;

            *pulValue = pIrqData->IRQ_Header.IRQD_Alloc_Num;
            *pulLen   = 1;
            *pulEnd   = *pulValue;
            *pulFlags = pIrqData->IRQ_Header.IRQD_Flags;
            break;
        }
    }

    if(*pulEnd < *pulValue) {
        //
        // filter out bad/zero-length range
        //
        *pulLen = 0;
    }

    return;

} // GetHdrValues

void
pGetRangeValues(
    IN  LPBYTE      pData,
    IN  RESOURCEID  ResType,
    IN  ULONG       ulIndex,
    OUT PULONG64    pulValue, OPTIONAL
    OUT PULONG64    pulLen, OPTIONAL
    OUT PULONG64    pulEnd, OPTIONAL
    OUT PULONG64    pulAlign, OPTIONAL
    OUT PULONG      pulFlags OPTIONAL
    )
{
    //
    // keep local copies
    // we transfer to parameters at end
    //
    ULONG64 ulValue;
    ULONG64 ulLen;
    ULONG64 ulEnd;
    ULONG64 ulAlign;
    ULONG ulFlags;

    switch (ResType) {

        case ResType_Mem: {

            PMEM_RESOURCE  pMemData = (PMEM_RESOURCE)pData;

            ulValue = pMemData->MEM_Data[ulIndex].MR_Min;
            ulLen   = pMemData->MEM_Data[ulIndex].MR_nBytes;
            ulEnd   = pMemData->MEM_Data[ulIndex].MR_Max;
            ulFlags = pMemData->MEM_Data[ulIndex].MR_Flags;
            ulAlign = pMemData->MEM_Data[ulIndex].MR_Align;
            break;
        }

        case ResType_IO:  {

            PIO_RESOURCE   pIoData = (PIO_RESOURCE)pData;

            ulValue = pIoData->IO_Data[ulIndex].IOR_Min;
            ulLen   = pIoData->IO_Data[ulIndex].IOR_nPorts;
            ulEnd   = pIoData->IO_Data[ulIndex].IOR_Max;
            ulFlags = pIoData->IO_Data[ulIndex].IOR_RangeFlags;
            ulAlign = pIoData->IO_Data[ulIndex].IOR_Align;
            break;
        }

        case ResType_DMA: {

            PDMA_RESOURCE  pDmaData = (PDMA_RESOURCE)pData;

            ulValue = pDmaData->DMA_Data[ulIndex].DR_Min;
            ulLen   = 1;
            ulEnd   = ulValue;
            ulFlags = pDmaData->DMA_Data[ulIndex].DR_Flags;
            ulAlign = 1;
            break;
        }

        case ResType_IRQ: {

            DEVRES_PIRQ_RESOURCE  pIrqData = (DEVRES_PIRQ_RESOURCE)pData;

            ulValue = pIrqData->IRQ_Data[ulIndex].IRQR_Min;
            ulLen   = 1;
            ulEnd   = ulValue;
            ulFlags = pIrqData->IRQ_Data[ulIndex].IRQR_Flags;
            ulAlign = 1;
            break;
        }
    }

    if(ulEnd < ulValue) {
        //
        // filter out bad/zero-length range
        //
        ulLen = 0;
    }

    pAlignValues(&ulValue, ulValue, ulLen, ulEnd, ulAlign,1);

    //
    // copy return parameters
    //
    if (pulValue) {
        *pulValue = ulValue;
    }
    if (pulLen) {
        *pulLen = ulLen;
    }
    if (pulEnd) {
        *pulEnd = ulEnd;
    }
    if (pulAlign) {
        *pulAlign = ulAlign;
    }
    if (pulFlags) {
        *pulFlags = ulFlags;
    }


    return;

}

BOOL
pAlignValues(
    IN OUT PULONG64  pulValue,
    IN     ULONG64   ulStart,
    IN     ULONG64   ulLen,
    IN     ULONG64   ulEnd,
    IN     ULONG64   ulAlignment,
    IN     int       Increment
    )
{
    ULONG64 NtAlign = ~ulAlignment + 1;   // convert from mask to modulus
    ULONG64 Value;
    ULONG64 Upp;
    ULONG64 Remainder;

    Value = *pulValue;

    if (NtAlign == 0) {
        return FALSE;   // bogus alignment value
    }

    if (NtAlign != 1 && Increment != 0) {
        //
        // see if we are aligned
        //

        Remainder = Value % NtAlign;

        if (Remainder != 0) {
            //
            // need to re-align
            //
            if (Increment>0) {
                //
                // Return the first valid aligned value greater than this value
                //
                Value += NtAlign - Remainder;

                if (Value <= *pulValue) {
                    //
                    // overflow detected
                    //
                    return FALSE;
                }

            } else {
                //
                // Return the first valid aligned value less than this value
                //
                Value -= Remainder;
                //
                // we never overflow going down, since zero is a common denominator
                // of alignment
                //
            }

        }
    }

    //
    // now check boundaries
    //

    if (Value < ulStart) {
        return FALSE;
    }

    Upp = Value+ulLen-1;
    if (Upp < Value) {
        //
        // catch overflow error
        //
        return FALSE;
    }
    if (Upp > ulEnd) {
        return FALSE;
    }

    //
    // set newly aligned value
    //

    *pulValue = Value;

    return TRUE;

}

void
pFormatResString(
    LPDMPROP_DATA lpdmpd,
    LPTSTR      lpszString,
    ULONG64     ulVal,
    ULONG64     ulLen,
    RESOURCEID  ResType
    )
{
    if (ulLen == 0) {
        wsprintf(lpszString, szNoValue);
    } else if ((ResType == ResType_DMA) || (ResType == ResType_IRQ)) {
        wsprintf(lpszString, szOneDecNoConflict, (UINT)ulVal);
    } else if (ResType == ResType_IO) {
        wsprintf(lpszString, szTwoWordHexNoConflict, (ULONG)ulVal,
                 (ULONG)(ulVal + ulLen - 1));
    } else if (lpdmpd && (lpdmpd->dwFlags & DMPROP_FLAG_64BIT_RANGE)) {
        wsprintf(lpszString, szTwo64bitHexNoConflict, (ULONG64)ulVal,
                 (ULONG64)(ulVal + ulLen - 1));
    } else {
        wsprintf(lpszString, szTwoDWordHexNoConflict, (ULONG)ulVal,
                 (ULONG)(ulVal + ulLen - 1));
    }

}

BOOL
pUnFormatResString(
    LPTSTR      lpszString,
    PULONG64    pulVal,
    PULONG64    pulEnd,
    RESOURCEID  ridResType
    )
{
    BOOL     bRet = FALSE;
    LPTSTR   lpszTemp = NULL;
    LPTSTR   lpszTemp2 = NULL;
    LPTSTR   lpszCopy;

    // ISSUE-2000/02/03 Fix pUnFormatResString bugs
    //
    // - extend this to handling DWORDLONG values
    // - use correct Prev/Next functions for parsing string
    //

    //
    // Allocate space for, and make a copy of the input string
    //
    lpszCopy = MyMalloc((lstrlen(lpszString)+1) * sizeof(TCHAR));

    if (lpszCopy == NULL) {
        return FALSE;
    }

    lstrcpy(lpszCopy, lpszString);

    //
    // Locate the dash if there is one, and convert the white space prev to
    // the dash to a NULL. (ie 0200 - 0400 while be 0200)
    //
    lpszTemp = lpszCopy;
    while ((*lpszTemp != '-') && (*lpszTemp != '\0')) {
        lpszTemp++; // AnsiNext?
    }

    if (*lpszTemp != '\0') {
        lpszTemp2 = lpszTemp-1;
        ++lpszTemp;
    }

    //
    // Search back to set the NULL for the Value
    //
    if (lpszTemp2 != NULL) {
        while ((*lpszTemp2 == ' ') || (*lpszTemp2 == '\t'))
            lpszTemp2--; // AnsiPrev?
        *(lpszTemp2+1)= '\0';
    }

    //
    // Convert the first entry
    //
    if (pConvertEditText(lpszCopy, pulVal, ridResType)) {
        //
        // If there is a second entry, convert it, otherwise assume a length
        // of one.
        //
        if (*lpszTemp != '\0') {
            if (pConvertEditText(lpszTemp, pulEnd,ridResType)) {
                bRet = TRUE;
            }
        } else {
            *pulEnd = *pulVal;
            bRet = TRUE;
        }
    }

    MyFree(lpszCopy);
    return bRet;

}

BOOL
pConvertEditText(
    LPTSTR      lpszConvert,
    PULONG64    pulVal,
    RESOURCEID  ridResType
    )
{
    LPTSTR   lpConvert;

    if ((ridResType == ResType_Mem) || (ridResType == ResType_IO)) {
        *pulVal = _tcstoul(lpszConvert, &lpConvert, (WORD)16);
    } else {
        *pulVal = _tcstoul(lpszConvert, &lpConvert, (WORD)10);
    }

    if (lpConvert == lpszConvert+lstrlen(lpszConvert)) {
        return TRUE;
    } else {
        return FALSE;
    }

} // ConvertEditText

void
pWarnResSettingNotEditable(
    HWND    hDlg,
    WORD    idWarning
    )
{
    TCHAR    szTitle[MAX_PATH];
    TCHAR    szMessage[MAX_PATH * 2];

    //
    // Give some warning Messages.  If there is no logconfig,
    // then we cannot edit any settings, if there is, then
    // just the setting they are choosing is not editable.
    //
    LoadString(MyDllModuleHandle, IDS_DEVRES_NOMODIFYTITLE, szTitle, MAX_PATH);
    LoadString(MyDllModuleHandle, idWarning, szMessage, MAX_PATH * 2);
    MessageBox(hDlg, szMessage, szTitle, MB_OK | MB_TASKMODAL | MB_ICONEXCLAMATION);

} // WarnResSettingsNotEditable

int
pWarnNoSave(
    HWND    hDlg,
    WORD    idWarning
    )
/*++

Routine Description:

    Warn that the settings will not be saved

Arguments:

Return Value:

    IDCANCEL = don't proceed
    IDOK/IDYES/IDNO = proceed without saving

--*/
{
    TCHAR    szTitle[MAX_PATH];
    TCHAR    szMessage[MAX_PATH * 2];
    int      res;

    //
    // Give a warning message of why we can't save settings
    //
    LoadString(MyDllModuleHandle, IDS_MAKE_FORCED_TITLE, szTitle, MAX_PATH);
    LoadString(MyDllModuleHandle, idWarning, szMessage, MAX_PATH * 2);

    //res = MessageBox(hDlg, szMessage, szTitle, MB_OKCANCEL | MB_TASKMODAL | MB_ICONEXCLAMATION);
    //return res;
    res = MessageBox(hDlg, szMessage, szTitle, MB_OK | MB_TASKMODAL | MB_ICONEXCLAMATION);
    return IDCANCEL;
}

LPVOID
pGetListViewItemData(
    HWND hList,
    int iItem,
    int iSubItem
    )
{
    LV_ITEM lviItem;

    lviItem.mask = LVIF_PARAM;
    lviItem.iItem = iItem;
    lviItem.iSubItem = iSubItem;

    if (ListView_GetItem(hList, &lviItem)) {
        return (LPVOID)lviItem.lParam;
    } else {
        return NULL;
    }

} // GetListViewItemData

BOOL
pSaveDevResSettings(
    LPDMPROP_DATA   lpdmpd
    )

/*++

Routine Description:

    This routine saves the resources based on the users selections.
    MEMPHIS COMPATIBLE.

Arguments:

    lpdmpd          Property data.

Return Value:

   Returns TRUE if the function succeeded and FALSE if it failed.

--*/

{
    HWND        hDlg = lpdmpd->hDlg;
    HWND        hList =  GetDlgItem(hDlg, IDC_DEVRES_SETTINGSLIST);

    CONFIGRET   Status = CR_SUCCESS;
    LOG_CONF    ForcedLogConf;
    RES_DES     ResDes, ResDesTemp, ResDes1;
    RESOURCEID  ResType;
    ULONG       ulSize = 0, ulCount = 0, i = 0, iCur = 0;
    LPBYTE      pData = NULL;
    PITEMDATA   pItemData = NULL;
    BOOL        bRet = TRUE;
    SP_PROPCHANGE_PARAMS PropChangeParams;
    HMACHINE        hMachine = pGetMachine(lpdmpd);

    if ((lpdmpd->dwFlags & DMPROP_FLAG_USESYSSETTINGS)!=0) {

        //-------------------------------------------------------------------
        // If the user checked the "Use Automatic Settings" checkbox, then
        // delete any Boot/Forced configs, otherwise write the current settings
        // as a forced config.
        //-------------------------------------------------------------------

        if (CM_Get_First_Log_Conf_Ex(&ForcedLogConf, lpdmpd->lpdi->DevInst,
                                     FORCED_LOG_CONF,hMachine) == CR_SUCCESS) {
            CM_Free_Log_Conf_Ex(ForcedLogConf, 0,hMachine);
            CM_Free_Log_Conf_Handle(ForcedLogConf);
        }

        // Let the helper modules (class installer/co-installers) get in on the act...
        //
        PropChangeParams.ClassInstallHeader.cbSize          = sizeof(SP_CLASSINSTALL_HEADER);
        PropChangeParams.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;

        PropChangeParams.StateChange = DICS_PROPCHANGE;
        PropChangeParams.Scope       = DICS_FLAG_GLOBAL;
        // no need to set PropChangeParams.HwProfile, since this is a global property change.

        DoInstallActionWithParams(DIF_PROPERTYCHANGE,
                                  lpdmpd->hDevInfo,
                                  lpdmpd->lpdi,
                                  (PSP_CLASSINSTALL_HEADER)&PropChangeParams,
                                  sizeof(PropChangeParams),
                                  INSTALLACTION_CALL_CI
                                  );
    } else {

        //-------------------------------------------------------------------
        // The Use Automatic Settings is not selected.
        //-------------------------------------------------------------------

        bRet = pSaveCustomResSettings(lpdmpd,hMachine);
    }

    return bRet;
}

BOOL
pSaveCustomResSettings(
    LPDMPROP_DATA   lpdmpd,
    IN HMACHINE     hMachine
    )

/*++

Routine Description:

    This routine saves custom (user edited) resources. MEMPHIS COMPATIBLE but
    extracted from Memphis version of SaveDevResSetting().

Arguments:

    lpdmpd      Property data.

Return Value:

   Returns TRUE if the function succeeded and FALSE if it failed.

--*/

{
    HWND        hDlg = lpdmpd->hDlg;

    TCHAR       szWarn[MAX_MSG_LEN];
    TCHAR       szTitle[MAX_MSG_LEN];
    TCHAR       szTemp[MAX_MSG_LEN];
    DWORD       dwPriority, dwLCPri;
    LOG_CONF    ForcedLogConf;
    RES_DES     ResDes;
    HWND        hList = GetDlgItem(hDlg, IDC_DEVRES_SETTINGSLIST);
    PITEMDATA   pItemData = NULL;
    LONG        iCur;
    BOOL        bRet = FALSE;
    SP_PROPCHANGE_PARAMS PropChangeParams;
    DWORD       HardReconfigFlag;
    SP_DEVINSTALL_PARAMS DevInstallParams;
    PRESDES_ENTRY pResList = NULL, pResDesEntry = NULL, pTemp = NULL;
    PITEMDATA_LISTNODE ItemDataList = NULL, ItemDataListEntry, ItemDataListEnd = NULL;
    PGENERIC_RESOURCE pGenRes;
    ULONG       i, ulFlags;
    ULONG64     ulValue, ulLen, ulEnd;
    LOG_CONF    LogConf;
    ULONG       ulSize;
    ULONG       ulConfigFlags;
    HCURSOR     hOldCursor;
    BOOL        UsingMatch = FALSE;

    LogConf = lpdmpd->SelectedLC;
    if (LogConf == 0) {
        LogConf = lpdmpd->MatchingLC;
        UsingMatch = TRUE;
    }
    if (LogConf == 0) {
        LogConf = lpdmpd->CurrentLC;
        UsingMatch = FALSE;
    }
    if (LogConf == 0) {
        //MYASSERT(FALSE);
        return FALSE;
    }
    //
    // form the "warning - do you want to continue" message
    //
    if(!LoadString(MyDllModuleHandle, IDS_MAKE_FORCED_TITLE, szTitle, MAX_MSG_LEN)) {
        szTitle[0]=TEXT('\0');
    }
    if(!LoadString(MyDllModuleHandle, IDS_FORCEDCONFIG_WARN1, szWarn, MAX_MSG_LEN)) {
        szWarn[0]=TEXT('\0');
    }
    if(LoadString(MyDllModuleHandle, IDS_FORCEDCONFIG_WARN2, szTemp, MAX_MSG_LEN)) {
        lstrcat(szWarn, szTemp);
    }
    if(LoadString(MyDllModuleHandle, IDS_FORCEDCONFIG_WARN3, szTemp, MAX_MSG_LEN)) {
        lstrcat(szWarn, szTemp);
    }
    if(LoadString(MyDllModuleHandle, IDS_FORCEDCONFIG_WARN4, szTemp, MAX_MSG_LEN)) {
        lstrcat(szWarn, szTemp);
    }

    //
    // If the LCPRI is soft configurable, and the user chooses YES to the
    // warning, then save the new config.  If the LCPRI is not soft
    // configurable, just save with no warning
    //
    dwLCPri = pGetMinLCPriority(lpdmpd->lpdi->DevInst, lpdmpd->ConfigListLCType,hMachine);

    if (((dwLCPri >= LCPRI_DESIRED) && (dwLCPri <= LCPRI_LASTSOFTCONFIG)) &&
          (MessageBox(hDlg, szWarn, szTitle, MB_YESNO|MB_ICONEXCLAMATION) == IDNO)) {
        //
        // user doesn't want to change anything
        //
        bRet = FALSE;

    } else {
        //
        // We're still using the selected basic LC, but use the range index
        // embedded in the listview control
        // ISSUE-2000/02/03-JamieHun Selected Basic LC, check if user overrode
        // Need to check the value to see if a user overrode it (is this possible?)
        //
        bRet = TRUE;

        if (CM_Get_First_Log_Conf_Ex(&ForcedLogConf, lpdmpd->lpdi->DevInst,
                                  FORCED_LOG_CONF,hMachine) == CR_SUCCESS) {
            CM_Free_Log_Conf_Ex(ForcedLogConf, 0,hMachine);
            CM_Free_Log_Conf_Handle(ForcedLogConf);
        }

        //
        // Save the current choices as the forced config
        //
        CM_Add_Empty_Log_Conf_Ex(&ForcedLogConf, lpdmpd->lpdi->DevInst, LCPRI_FORCECONFIG,
                              FORCED_LOG_CONF | PRIORITY_EQUAL_FIRST,hMachine);

        pGetResDesDataList(LogConf, &pResList, FALSE,hMachine);
        pResDesEntry = pResList;

        if (UsingMatch && (lpdmpd->dwFlags & DMPROP_FLAG_MATCH_OUT_OF_ORDER)) {
            //
            // The resource descriptors are out-of-order.  Maintain the original ordering.
            //
            // First, build up a linked list of the data in the listview resource items.
            //
            iCur = (int)ListView_GetNextItem(hList, -1, LVNI_ALL);

            while (iCur != -1) {

                pItemData = (PITEMDATA)pGetListViewItemData(hList, iCur, 0);
                if (pItemData) {
                    //
                    // Allocate an item data list node for this data.
                    //
                    ItemDataListEntry = MyMalloc(sizeof(ITEMDATA_LISTNODE));
                    if (!ItemDataListEntry) {
                        bRet = FALSE;
                        goto clean0;
                    }

                    ItemDataListEntry->ItemData = pItemData;
                    ItemDataListEntry->Next = NULL;

                    //
                    // Append this new item to the end of our list.
                    //
                    if (ItemDataListEnd) {
                        ItemDataListEnd->Next = ItemDataListEntry;
                    } else {
                        ItemDataList = ItemDataListEntry;
                    }
                    ItemDataListEnd = ItemDataListEntry;
                }

                iCur = (int)ListView_GetNextItem(hList, iCur, LVNI_ALL);
            }

            //
            // Now loop through each resdes entry, writing each one out.  For each one, check
            // to see if it has a corresponding entry in our listview item data list.
            //
            while (pResDesEntry) {
                pGenRes = (PGENERIC_RESOURCE)pResDesEntry->ResDesData;

                for(ItemDataListEntry = ItemDataList, ItemDataListEnd = NULL;
                    ItemDataListEntry;
                    ItemDataListEnd = ItemDataListEntry, ItemDataListEntry = ItemDataListEntry->Next)
                {
                    if(pResDesEntry->ResDesType == ItemDataListEntry->ItemData->ResType) {

                        for (i = 0; i < pGenRes->GENERIC_Header.GENERIC_Count; i++) {

                            pGetRangeValues(pResDesEntry->ResDesData, pResDesEntry->ResDesType, i,
                                           &ulValue, &ulLen, &ulEnd, NULL, &ulFlags);

                            if ((ItemDataListEntry->ItemData->ulLen == ulLen) &&
                                (ItemDataListEntry->ItemData->ulValue >= ulValue) &&
                                (ItemDataListEntry->ItemData->ulEnd <= ulEnd)) {
                                //
                                // We found the matching resource descriptor.  Write this out.
                                //
                                pWriteValuesToForced(ForcedLogConf,
                                                    ItemDataListEntry->ItemData->ResType,
                                                    ItemDataListEntry->ItemData->RangeCount,
                                                    ItemDataListEntry->ItemData->MatchingResDes,
                                                    ItemDataListEntry->ItemData->ulValue,
                                                    ItemDataListEntry->ItemData->ulLen,
                                                    ItemDataListEntry->ItemData->ulEnd,
                                                    hMachine );
                                //
                                // Remove this item from our list.
                                //
                                if (ItemDataListEnd) {
                                    ItemDataListEnd->Next = ItemDataListEntry->Next;
                                } else {
                                    ItemDataList = ItemDataListEntry->Next;
                                }
                                MyFree(ItemDataListEntry);

                                break;
                            }
                        }

                        if(i < pGenRes->GENERIC_Header.GENERIC_Count) {
                            //
                            // Then we broke out of the loop early, which means we found a match
                            // already.
                            //
                            break;
                        }
                    }
                }

                //
                // If we didn't find a match, then go ahead and write out the non-arbitrated
                // resdes.
                //
                if (!ItemDataListEntry) {
                    pWriteResDesRangeToForced(ForcedLogConf,
                                             pResDesEntry->ResDesType,
                                             0,
                                             0,
                                             pResDesEntry->ResDesData,
                                             hMachine);
                }

                pResDesEntry = (PRESDES_ENTRY)pResDesEntry->Next;
            }

        } else {

            iCur = (int)ListView_GetNextItem(hList, -1, LVNI_ALL);

            while (iCur != -1) {

                pItemData = (PITEMDATA)pGetListViewItemData(hList, iCur, 0);

                if (pItemData) {

                    // retrieve values

                    while (pResDesEntry &&
                           (pItemData->ResType != pResDesEntry->ResDesType)) {
                        //
                        // write out any preceding non arbitrated resources
                        //
                        pWriteResDesRangeToForced(ForcedLogConf,
                                                 pResDesEntry->ResDesType,
                                                 0,
                                                 0,
                                                 pResDesEntry->ResDesData,
                                                 hMachine);

                        pResDesEntry = (PRESDES_ENTRY)pResDesEntry->Next;
                    }
                    if (pGetMatchingResDes(pItemData->ulValue,
                                          pItemData->ulLen,
                                          pItemData->ulEnd,
                                          pItemData->ResType,
                                          LogConf,
                                          &ResDes,
                                          hMachine)) {
                        //
                        // Write the first range as the chosen forced resource
                        //
                        pWriteValuesToForced(ForcedLogConf, pItemData->ResType,
                                            pItemData->RangeCount, ResDes,
                                            pItemData->ulValue,
                                            pItemData->ulLen,
                                            pItemData->ulEnd,
                                            hMachine);
                    }
                }

                if (pResDesEntry) {
                    pResDesEntry = (PRESDES_ENTRY)pResDesEntry->Next;
                } else {
                    MYASSERT(pResDesEntry);
                }
                iCur = (int)ListView_GetNextItem(hList, iCur, LVNI_ALL);
            }

            while (pResDesEntry) {
                //
                // write out any subsequent non arbitrated resources
                //
                pWriteResDesRangeToForced(ForcedLogConf,
                                         pResDesEntry->ResDesType,
                                         0,
                                         0,
                                         pResDesEntry->ResDesData,
                                         hMachine);

                pResDesEntry = (PRESDES_ENTRY)pResDesEntry->Next;
            }
        }

        CM_Free_Log_Conf_Handle(ForcedLogConf);

        //
        // consider clearing problem flags
        //
        ulSize = sizeof(ulConfigFlags);
        if (CM_Get_DevInst_Registry_Property_Ex(lpdmpd->lpdi->DevInst,
                                             CM_DRP_CONFIGFLAGS,
                                             NULL, (LPBYTE)&ulConfigFlags,
                                             &ulSize, 0,hMachine) == CR_SUCCESS) {
            if ((ulConfigFlags & CONFIGFLAG_PARTIAL_LOG_CONF) != 0) {
                //
                // have flag(s) to change
                // CONFIGFLAG_PARTIAL_LOG_CONF should be cleared - we should have written a complete config now
                //
                ulConfigFlags &= ~ (CONFIGFLAG_PARTIAL_LOG_CONF);
                CM_Set_DevInst_Registry_Property_Ex(lpdmpd->lpdi->DevInst,
                                                 CM_DRP_CONFIGFLAGS,
                                                 (LPBYTE)&ulConfigFlags,
                                                 sizeof(ulConfigFlags),
                                                 0,
                                                 hMachine);
            }

        }

        //
        // Give the class installer/co-installers a crack at the propchange process.
        //
        PropChangeParams.ClassInstallHeader.cbSize          = sizeof(SP_CLASSINSTALL_HEADER);
        PropChangeParams.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;

        PropChangeParams.StateChange = DICS_PROPCHANGE;
        PropChangeParams.Scope       = DICS_FLAG_GLOBAL;
        // no need to set PropChangeParams.HwProfile, since this is a global property change.

        DoInstallActionWithParams(DIF_PROPERTYCHANGE,
                                  lpdmpd->hDevInfo,
                                  lpdmpd->lpdi,
                                  (PSP_CLASSINSTALL_HEADER)&PropChangeParams,
                                  sizeof(PropChangeParams),
                                  INSTALLACTION_CALL_CI | INSTALLACTION_NO_DEFAULT
                                 );

        //
        // Check the Priority of this LC.  If it is greater
        // than LCPRI_LASTSOFTCONFIG, then we need to reboot
        // otherwise try the dynamic changestate route.
        //

        if (CM_Get_Log_Conf_Priority_Ex(LogConf, &dwPriority, 0,hMachine) != CR_SUCCESS) {
            dwPriority = LCPRI_LASTSOFTCONFIG;
        }

        if (dwPriority <= LCPRI_LASTSOFTCONFIG) {
            //
            // Do the default action for SoftConfigable devices, which
            // will attempt to restart the device with the new config
            // This could take a while so use an hourglass
            //
            hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
            DoInstallActionWithParams(DIF_PROPERTYCHANGE,
                                      lpdmpd->hDevInfo,
                                      lpdmpd->lpdi,
                                      (PSP_CLASSINSTALL_HEADER)&PropChangeParams,
                                      sizeof(PropChangeParams),
                                      0  // don't call class-installer, just do default action
                                     );
            SetCursor(hOldCursor);
            HardReconfigFlag = 0;

        } else if((dwPriority > LCPRI_LASTSOFTCONFIG) && (dwPriority <= LCPRI_RESTART)) {
            HardReconfigFlag = DI_NEEDRESTART;
        } else {
            HardReconfigFlag = DI_NEEDREBOOT;
        }

        lpdmpd->dwFlags |= DMPROP_FLAG_CHANGESSAVED;

        //
        // Properties have changed, so set flags to indicate if restart/reboot is required,
        // and to tell DevMgr to re-init the UI.
        //
        DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
        if(SetupDiGetDeviceInstallParams(lpdmpd->hDevInfo,
                                         lpdmpd->lpdi,
                                         &DevInstallParams)) {

            DevInstallParams.Flags |= (HardReconfigFlag | DI_PROPERTIES_CHANGE);

            SetupDiSetDeviceInstallParams(lpdmpd->hDevInfo,
                                          lpdmpd->lpdi,
                                          &DevInstallParams
                                         );
        }

        //
        // If we need to reboot, then set a problem on the device that indicates this (in case
        // the user doesn't listen to us, we want to flag this devnode so that the user will see
        // that this devnode needs a reboot if they go into DevMgr, etc.)
        //
        if(HardReconfigFlag) {
            PDEVICE_INFO_SET pDeviceInfoSet;
            PDEVINFO_ELEM DevInfoElem;

            if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdmpd->hDevInfo))) {
                //
                // We'd better be able to access this device information set!
                // In case we couldn't don't bother trying to set the needs-reboot problem,
                // because the whole mess is invalid!
                //
                MYASSERT(pDeviceInfoSet);
            } else {

                try {
                    DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet, lpdmpd->lpdi, NULL);
                    //
                    // We'd better be able to find this element!
                    //
                    MYASSERT(DevInfoElem);
                    //
                    // In case we can't find it, don't try to set any problem on the devnode.
                    //
                    if(DevInfoElem) {

                        SetDevnodeNeedsRebootProblem(DevInfoElem,
                                                     pDeviceInfoSet,
                                                     MSG_LOG_REBOOT_DEVRES
                                                    );
                    }
                } finally {
                    UnlockDeviceInfoSet(pDeviceInfoSet);
                }
            }

        }
    }

clean0:

    while (ItemDataList) {
        ItemDataListEntry = ItemDataList->Next;
        MyFree(ItemDataList);
        ItemDataList = ItemDataListEntry;
    }

    pDeleteResDesDataList(pResList);

    return bRet;

} // SaveCustomResSettings

BOOL
pWriteResDesRangeToForced(
    IN LOG_CONF     ForcedLogConf,
    IN RESOURCEID   ResType,
    IN ULONG        RangeIndex,
    IN RES_DES      RD,             OPTIONAL
    IN LPBYTE       ResDesData,     OPTIONAL
    IN HMACHINE     hMachine        OPTIONAL
    )
{
    RES_DES ResDes;
    ULONG   ulSize;
    LPBYTE  pData = NULL;
    BOOL Success = FALSE;


    if ((RD == 0) && (ResDesData == NULL)) {
        return FALSE;   // pass in data or handle!
    }

    if (!ResDesData) {

        if (CM_Get_Res_Des_Data_Size_Ex(&ulSize, RD, DEVRES_WIDTH_FLAGS,hMachine) != CR_SUCCESS) {
            return FALSE;
        }

        pData = MyMalloc(ulSize);
        if (pData == NULL) {
            return FALSE;
        }

        if (CM_Get_Res_Des_Data_Ex(RD, pData, ulSize, DEVRES_WIDTH_FLAGS,hMachine) != CR_SUCCESS) {
            MyFree(pData);
            return FALSE;
        }
    } else {
        pData = ResDesData;
    }

    //
    // convert the first range data into hdr data
    //
    switch (ResType) {

        case ResType_Mem: {

            PMEM_RESOURCE pMemData = (PMEM_RESOURCE)pData;
            PMEM_RESOURCE pForced = (PMEM_RESOURCE)MyMalloc(sizeof(MEM_RESOURCE));
            if (!pForced) {
                break;
            }

            pForced->MEM_Header.MD_Count      = 0;
            pForced->MEM_Header.MD_Type       = MType_Range;
            pForced->MEM_Header.MD_Alloc_Base = pMemData->MEM_Data[RangeIndex].MR_Min;
            pForced->MEM_Header.MD_Alloc_End  = pMemData->MEM_Data[RangeIndex].MR_Min +
                                                pMemData->MEM_Data[RangeIndex].MR_nBytes - 1;
            pForced->MEM_Header.MD_Flags      = pMemData->MEM_Data[RangeIndex].MR_Flags;
            pForced->MEM_Header.MD_Reserved   = 0;

            Success = CM_Add_Res_Des_Ex(NULL,
                                        ForcedLogConf,
                                        ResType_Mem,
                                        pForced,
                                        sizeof(MEM_RESOURCE),
                                        DEVRES_WIDTH_FLAGS,
                                        hMachine) == CR_SUCCESS;
            MyFree(pForced);
            break;
        }

        case ResType_IO:  {

            PIO_RESOURCE pIoData = (PIO_RESOURCE)pData;
            PIO_RESOURCE pForced = (PIO_RESOURCE)MyMalloc(sizeof(IO_RESOURCE));
            if (!pForced) {
                break;
            }

            pForced->IO_Header.IOD_Count      = 0;
            pForced->IO_Header.IOD_Type       = IOType_Range;
            pForced->IO_Header.IOD_Alloc_Base = pIoData->IO_Data[RangeIndex].IOR_Min;
            pForced->IO_Header.IOD_Alloc_End  = pIoData->IO_Data[RangeIndex].IOR_Min +
                                                pIoData->IO_Data[RangeIndex].IOR_nPorts - 1;
            pForced->IO_Header.IOD_DesFlags   = pIoData->IO_Data[RangeIndex].IOR_RangeFlags;

            Success = CM_Add_Res_Des_Ex(NULL,
                                        ForcedLogConf,
                                        ResType_IO,
                                        pForced,
                                        sizeof(IO_RESOURCE),
                                        DEVRES_WIDTH_FLAGS,
                                        hMachine) == CR_SUCCESS;
            MyFree(pForced);
            break;
        }

        case ResType_DMA: {

            PDMA_RESOURCE pDmaData = (PDMA_RESOURCE)pData;
            PDMA_RESOURCE pForced = (PDMA_RESOURCE)MyMalloc(sizeof(DMA_RESOURCE));
            if (!pForced) {
                break;
            }

            pForced->DMA_Header.DD_Count      = 0;
            pForced->DMA_Header.DD_Type       = DType_Range;
            pForced->DMA_Header.DD_Flags      = pDmaData->DMA_Data[RangeIndex].DR_Flags;
            pForced->DMA_Header.DD_Alloc_Chan = pDmaData->DMA_Data[RangeIndex].DR_Min;

            Success = CM_Add_Res_Des_Ex(NULL,
                                        ForcedLogConf,
                                        ResType_DMA,
                                        pForced,
                                        sizeof(DMA_RESOURCE),
                                        DEVRES_WIDTH_FLAGS,
                                        hMachine) == CR_SUCCESS;
            MyFree(pForced);
            break;
        }

        case ResType_IRQ: {

            DEVRES_PIRQ_RESOURCE pIrqData = (DEVRES_PIRQ_RESOURCE)pData;
            DEVRES_PIRQ_RESOURCE pForced = (DEVRES_PIRQ_RESOURCE)MyMalloc(sizeof(DEVRES_IRQ_RESOURCE));
            if (!pForced) {
                break;
            }

            pForced->IRQ_Header.IRQD_Count     = 0;
            pForced->IRQ_Header.IRQD_Type      = IRQType_Range;
            pForced->IRQ_Header.IRQD_Flags     = pIrqData->IRQ_Data[RangeIndex].IRQR_Flags;
            pForced->IRQ_Header.IRQD_Alloc_Num = pIrqData->IRQ_Data[RangeIndex].IRQR_Min;
            pForced->IRQ_Header.IRQD_Affinity  = (DEVRES_AFFINITY)(-1);

            Success = CM_Add_Res_Des_Ex(NULL,
                                        ForcedLogConf,
                                        ResType_IRQ,
                                        pForced,
                                        sizeof(DEVRES_IRQ_RESOURCE),
                                        DEVRES_WIDTH_FLAGS,
                                        hMachine) == CR_SUCCESS;
            MyFree(pForced);
            break;
        }

        case ResType_BusNumber: {

            PBUSNUMBER_RESOURCE pBusData = (PBUSNUMBER_RESOURCE)pData;
            PBUSNUMBER_RESOURCE pForced = (PBUSNUMBER_RESOURCE)MyMalloc(sizeof(BUSNUMBER_RESOURCE));
            if (!pForced) {
                break;
            }

            pForced->BusNumber_Header.BUSD_Count      = 0;
            pForced->BusNumber_Header.BUSD_Type       = BusNumberType_Range;
            pForced->BusNumber_Header.BUSD_Flags      = pBusData->BusNumber_Data[RangeIndex].BUSR_Flags;
            pForced->BusNumber_Header.BUSD_Alloc_Base = pBusData->BusNumber_Data[RangeIndex].BUSR_Min;
            pForced->BusNumber_Header.BUSD_Alloc_End  = pBusData->BusNumber_Data[RangeIndex].BUSR_Min +
                                                  pBusData->BusNumber_Data[RangeIndex].BUSR_nBusNumbers;

            Success = CM_Add_Res_Des_Ex(NULL,
                                        ForcedLogConf,
                                        ResType_BusNumber,
                                        pForced,
                                        sizeof(BUSNUMBER_RESOURCE),
                                        DEVRES_WIDTH_FLAGS,
                                        hMachine) == CR_SUCCESS;
            MyFree(pForced);
            break;
        }

        case ResType_DevicePrivate: {

            PDEVPRIVATE_RESOURCE pPrvData = (PDEVPRIVATE_RESOURCE)pData;
            PDEVPRIVATE_RESOURCE pForced = (PDEVPRIVATE_RESOURCE)MyMalloc(sizeof(DEVPRIVATE_RESOURCE));
            if (!pForced) {
                break;
            }

            pForced->PRV_Header.PD_Count = 0;
            pForced->PRV_Header.PD_Type  = PType_Range;
            pForced->PRV_Header.PD_Data1 = pPrvData->PRV_Data[RangeIndex].PR_Data1;
            pForced->PRV_Header.PD_Data2 = pPrvData->PRV_Data[RangeIndex].PR_Data2;
            pForced->PRV_Header.PD_Data3 = pPrvData->PRV_Data[RangeIndex].PR_Data3;
            pForced->PRV_Header.PD_Flags = 0;

            Success = CM_Add_Res_Des_Ex(NULL,
                                        ForcedLogConf,
                                        ResType_DevicePrivate,
                                        pForced,
                                        sizeof(DEVPRIVATE_RESOURCE),
                                        DEVRES_WIDTH_FLAGS,
                                        hMachine) == CR_SUCCESS;
            MyFree(pForced);
            break;
        }

        case ResType_PcCardConfig: {

            Success = CM_Add_Res_Des_Ex(NULL,
                                        ForcedLogConf,
                                        ResType_PcCardConfig,
                                        pData,
                                        sizeof(PCCARD_RESOURCE),
                                        DEVRES_WIDTH_FLAGS,
                                        hMachine);
            break;
        }
    }

    if (pData != ResDesData) {
        MyFree(pData);
    }

    return Success;

} // WriteResDesRangeToForced

BOOL
pWriteValuesToForced(
    IN LOG_CONF     ForcedLogConf,
    IN RESOURCEID   ResType,
    IN ULONG        RangeIndex,
    IN RES_DES      RD,
    IN ULONG64      ulValue,
    IN ULONG64      ulLen,
    IN ULONG64      ulEnd,
    IN HMACHINE     hMachine
    )
{
    RES_DES ResDes;
    ULONG   ulSize;
    LPBYTE  pData = NULL;
    BOOL Success = FALSE;


    if (CM_Get_Res_Des_Data_Size_Ex(&ulSize, RD, DEVRES_WIDTH_FLAGS,hMachine) != CR_SUCCESS) {
        return FALSE;
    }

    pData = MyMalloc(ulSize);
    if (pData == NULL) {
        return FALSE;
    }

    if (CM_Get_Res_Des_Data_Ex(RD, pData, ulSize, DEVRES_WIDTH_FLAGS,hMachine) != CR_SUCCESS) {
        MyFree(pData);
        return FALSE;
    }

    //
    // convert the first range data into hdr data
    //
    switch (ResType) {

        case ResType_Mem: {

            PMEM_RESOURCE pMemData = (PMEM_RESOURCE)pData;
            PMEM_RESOURCE pForced = (PMEM_RESOURCE)MyMalloc(sizeof(MEM_RESOURCE));
            if (!pForced) {
                break;
            }

            pForced->MEM_Header.MD_Count      = 0;
            pForced->MEM_Header.MD_Type       = MType_Range;
            pForced->MEM_Header.MD_Alloc_Base = ulValue;
            pForced->MEM_Header.MD_Alloc_End  = ulEnd;
            pForced->MEM_Header.MD_Flags      = pMemData->MEM_Data[RangeIndex].MR_Flags;
            pForced->MEM_Header.MD_Reserved   = 0;

            Success = CM_Add_Res_Des_Ex(NULL,
                                        ForcedLogConf,
                                        ResType_Mem,
                                        pForced,
                                        sizeof(MEM_RESOURCE),
                                        DEVRES_WIDTH_FLAGS,
                                        hMachine) == CR_SUCCESS;
            MyFree(pForced);
            break;
        }

        case ResType_IO:  {

            PIO_RESOURCE pIoData = (PIO_RESOURCE)pData;
            PIO_RESOURCE pForced = (PIO_RESOURCE)MyMalloc(sizeof(IO_RESOURCE));
            if (!pForced) {
                break;
            }

            pForced->IO_Header.IOD_Count      = 0;
            pForced->IO_Header.IOD_Type       = IOType_Range;
            pForced->IO_Header.IOD_Alloc_Base = ulValue;
            pForced->IO_Header.IOD_Alloc_End  = ulEnd;
            pForced->IO_Header.IOD_DesFlags   = pIoData->IO_Data[RangeIndex].IOR_RangeFlags;

            Success = CM_Add_Res_Des_Ex(NULL,
                                        ForcedLogConf,
                                        ResType_IO,
                                        pForced,
                                        sizeof(IO_RESOURCE),
                                        DEVRES_WIDTH_FLAGS,
                                        hMachine) == CR_SUCCESS;
            MyFree(pForced);
            break;
        }

        case ResType_DMA: {

            PDMA_RESOURCE pDmaData = (PDMA_RESOURCE)pData;
            PDMA_RESOURCE pForced = (PDMA_RESOURCE)MyMalloc(sizeof(DMA_RESOURCE));
            if (!pForced) {
                break;
            }

            pForced->DMA_Header.DD_Count      = 0;
            pForced->DMA_Header.DD_Type       = DType_Range;
            pForced->DMA_Header.DD_Flags      = pDmaData->DMA_Data[RangeIndex].DR_Flags;
            pForced->DMA_Header.DD_Alloc_Chan = (ULONG)ulValue;

            Success = CM_Add_Res_Des_Ex(NULL,
                                        ForcedLogConf,
                                        ResType_DMA,
                                        pForced,
                                        sizeof(DMA_RESOURCE),
                                        DEVRES_WIDTH_FLAGS,
                                        hMachine) == CR_SUCCESS;
            MyFree(pForced);
            break;
        }

        case ResType_IRQ: {

            DEVRES_PIRQ_RESOURCE pIrqData = (DEVRES_PIRQ_RESOURCE)pData;
            DEVRES_PIRQ_RESOURCE pForced = (DEVRES_PIRQ_RESOURCE)MyMalloc(sizeof(DEVRES_IRQ_RESOURCE));
            if (!pForced) {
                break;
            }

            pForced->IRQ_Header.IRQD_Count     = 0;
            pForced->IRQ_Header.IRQD_Type      = IRQType_Range;
            pForced->IRQ_Header.IRQD_Flags     = pIrqData->IRQ_Data[RangeIndex].IRQR_Flags;
            pForced->IRQ_Header.IRQD_Alloc_Num = (ULONG)ulValue;
            pForced->IRQ_Header.IRQD_Affinity  = (DEVRES_AFFINITY)(-1);

            Success = CM_Add_Res_Des_Ex(NULL,
                                        ForcedLogConf,
                                        ResType_IRQ,
                                        pForced,
                                        sizeof(DEVRES_IRQ_RESOURCE),
                                        DEVRES_WIDTH_FLAGS,
                                        hMachine) == CR_SUCCESS;
            MyFree(pForced);
            break;
        }

        case ResType_BusNumber: {

            PBUSNUMBER_RESOURCE pBusData = (PBUSNUMBER_RESOURCE)pData;
            PBUSNUMBER_RESOURCE pForced = (PBUSNUMBER_RESOURCE)MyMalloc(sizeof(BUSNUMBER_RESOURCE));
            if (!pForced) {
                break;
            }

            pForced->BusNumber_Header.BUSD_Count      = 0;
            pForced->BusNumber_Header.BUSD_Type       = BusNumberType_Range;
            pForced->BusNumber_Header.BUSD_Flags      = pBusData->BusNumber_Data[RangeIndex].BUSR_Flags;
            pForced->BusNumber_Header.BUSD_Alloc_Base = (ULONG)ulValue;
            pForced->BusNumber_Header.BUSD_Alloc_End  = (ULONG)ulEnd;

            Success = CM_Add_Res_Des_Ex(NULL,
                                        ForcedLogConf,
                                        ResType_BusNumber,
                                        pForced,
                                        sizeof(BUSNUMBER_RESOURCE),
                                        DEVRES_WIDTH_FLAGS,
                                        hMachine) == CR_SUCCESS;
            MyFree(pForced);
            break;
        }

        case ResType_DevicePrivate: {
            break;
        }

        case ResType_PcCardConfig: {
            break;
        }
    }

    if (pData) {
        MyFree(pData);
    }
    return Success;

} // WriteValuesToForced

BOOL
MakeResourceData(
    OUT LPBYTE     *ppResourceData,
    OUT PULONG     pulSize,
    IN  RESOURCEID ResType,
    IN  ULONG64    ulValue,
    IN  ULONG64    ulLen,
    IN  ULONG      ulFlags
    )
{
    BOOL bStatus = TRUE;

    try {

        switch (ResType) {

            case ResType_Mem: {

                PMEM_RESOURCE p;

                *pulSize = sizeof(MEM_RESOURCE);
                if (ppResourceData) {
                    *ppResourceData = MyMalloc(*pulSize);
                    p = (PMEM_RESOURCE)(*ppResourceData);
                    if (!p) {
                        bStatus = FALSE;
                        break;
                    }

                    p->MEM_Header.MD_Count      = 0;
                    p->MEM_Header.MD_Type       = MType_Range;
                    p->MEM_Header.MD_Alloc_Base = ulValue;
                    p->MEM_Header.MD_Alloc_End  = ulValue + ulLen - 1;
                    p->MEM_Header.MD_Flags      = ulFlags;
                    p->MEM_Header.MD_Reserved   = 0;
                }
                break;
            }

            case ResType_IO:  {

                PIO_RESOURCE p;

                *pulSize = sizeof(IO_RESOURCE);
                if (ppResourceData) {
                    *ppResourceData = MyMalloc(*pulSize);
                    p = (PIO_RESOURCE)(*ppResourceData);
                    if (!p) {
                        bStatus = FALSE;
                        break;
                    }

                    p->IO_Header.IOD_Count      = 0;
                    p->IO_Header.IOD_Type       = IOType_Range;
                    p->IO_Header.IOD_Alloc_Base = ulValue;
                    p->IO_Header.IOD_Alloc_End  = ulValue + ulLen - 1;
                    p->IO_Header.IOD_DesFlags   = ulFlags;
                }
                break;
            }

            case ResType_DMA: {

                PDMA_RESOURCE p;

                *pulSize = sizeof(DMA_RESOURCE);
                if (ppResourceData) {
                    *ppResourceData = MyMalloc(*pulSize);
                    p = (PDMA_RESOURCE)(*ppResourceData);
                    if (!p) {
                        bStatus = FALSE;
                        break;
                    }

                    p->DMA_Header.DD_Count      = 0;
                    p->DMA_Header.DD_Type       = DType_Range;
                    p->DMA_Header.DD_Flags      = ulFlags;
                    p->DMA_Header.DD_Alloc_Chan = (ULONG)ulValue;
                }
                break;
            }

            case ResType_IRQ: {

                DEVRES_PIRQ_RESOURCE p;

                *pulSize = sizeof(DEVRES_IRQ_RESOURCE);
                if (ppResourceData) {
                    *ppResourceData = MyMalloc(*pulSize);
                    p = (DEVRES_PIRQ_RESOURCE)(*ppResourceData);
                    if (!p) {
                        bStatus = FALSE;
                        break;
                    }

                    p->IRQ_Header.IRQD_Count     = 0;
                    p->IRQ_Header.IRQD_Type      = IRQType_Range;
                    p->IRQ_Header.IRQD_Flags     = ulFlags;
                    p->IRQ_Header.IRQD_Alloc_Num = (ULONG)ulValue;
                    p->IRQ_Header.IRQD_Affinity  = (DEVRES_AFFINITY)(-1); // for any processor
                }
                break;
            }

            default:
                //
                // ResTypeEditable or ResType_MAX may be wrong if this ASSERT's
                //
                MYASSERT(FALSE);
                bStatus = FALSE;
        }

    } except(EXCEPTION_EXECUTE_HANDLER) {
        bStatus = FALSE;
    }

    return bStatus;

} // MakeResourceData


BOOL
pShowWindow(
    IN HWND hWnd,
    IN int nShow
    )
/*++

Routine Description:

    A variation of ShowWindow that enables/disables window

Arguments:

    (See ShowWindow)
    hWnd  - handle of window to show
    nShow - typically SW_HIDE or SW_SHOW

Return Value:

    success status of ShowWindow

--*/
{
    EnableWindow(hWnd,nShow!=SW_HIDE);
    return ShowWindow(hWnd,nShow);
}


BOOL
pEnableWindow(
    IN HWND hWnd,
    IN BOOL Enable
    )
/*++

Routine Description:

    A variation of EnableWindow that only enables a window if it is visible

Arguments:

    (See EnableWindow)
    hWnd  - handle of window to enable/disable
    Enable - TRUE enables window (if window visible) FALSE disables window

Return Value:

    success status of EnableWindow

--*/
{
    //
    // I had to use GetWindowLong, as IsWindowVisible also checks parent flag
    // and parent is hidden until dialog is initialized
    //
    if((GetWindowLong(hWnd,GWL_STYLE) & WS_VISIBLE) == FALSE) {
        Enable = FALSE;
    }
    return EnableWindow(hWnd,Enable);
}

BOOL
pGetResDesDataList(
    IN LOG_CONF LogConf,
    IN OUT PRESDES_ENTRY *pResList,
    IN BOOL bArbitratedOnly,
    IN HMACHINE hMachine
    )
/*++

Routine Description:

    Creates a list of resource descriptors for further processing

Arguments:

    LogConf  - log config of interest
    pResList - list out
    bArbitratedOnly - filter out non-arbitrated resources
    hMachine - machine that LogConf is on

Return Value:

   None.

--*/
{
    BOOL bStatus = TRUE;
    CONFIGRET Status = CR_SUCCESS;
    PRESDES_ENTRY pHead = NULL, pEntry = NULL, pPrevious = NULL, pTemp = NULL;
    RES_DES     ResDes;
    RESOURCEID  ResType;
    ULONG       ulSize;
    LPBYTE      pData = NULL;

    //
    // Retrieve each res des in this log conf
    //

    Status = CM_Get_Next_Res_Des_Ex(&ResDes, LogConf, ResType_All, &ResType, 0,hMachine);

    while (Status == CR_SUCCESS) {

        if (bArbitratedOnly && (ResType <= ResType_None || ResType > ResType_MAX)) {
            goto NextResDes;
        }
        if (bArbitratedOnly && ResTypeEditable[ResType] == FALSE) {
            goto NextResDes;
        }

        if (CM_Get_Res_Des_Data_Size_Ex(&ulSize, ResDes, DEVRES_WIDTH_FLAGS,hMachine) != CR_SUCCESS) {
            CM_Free_Res_Des_Handle(ResDes);
            bStatus = FALSE;
            goto Clean0;
        }

        if (ulSize>0) {
            pData = MyMalloc(ulSize);
            if (pData == NULL) {
                CM_Free_Res_Des_Handle(ResDes);
                bStatus = FALSE;
                goto Clean0;
            }

            if (CM_Get_Res_Des_Data_Ex(ResDes, pData, ulSize, DEVRES_WIDTH_FLAGS,hMachine) != CR_SUCCESS) {
                CM_Free_Res_Des_Handle(ResDes);
                MyFree(pData);
                bStatus = FALSE;
                goto Clean0;
            }
        } else {
            pData = NULL;
        }

        //
        // Allocate a node for this res des and attach it to the list
        //

        pEntry = MyMalloc(sizeof(RESDES_ENTRY));
        if (pEntry == NULL) {
            CM_Free_Res_Des_Handle(ResDes);
            MyFree(pData);
            bStatus = FALSE;
            goto Clean0;
        }

        pEntry->ResDesData = pData;
        pEntry->ResDesType = ResType;
        pEntry->ResDesDataSize = ulSize;
        pEntry->ResDesHandle = ResDes;
        pEntry->Next = NULL;
        pEntry->CrossLink = NULL;

        if (!pHead) {
            pHead = pEntry;             // first entry
        }

        if (pPrevious) {
            pPrevious->Next = pEntry; // attach to previous entry
        }

        pPrevious = pEntry;

        //
        // Get next res des in LogConf
        //
    NextResDes:

        Status = CM_Get_Next_Res_Des_Ex(&ResDes, ResDes, ResType_All, &ResType, 0,hMachine);
    }

    bStatus = TRUE;

    Clean0:

    if (!bStatus) {
        pDeleteResDesDataList(pHead);
    } else {
        *pResList = pHead;
    }

    return bStatus;

} // GetResDesDataList

VOID
pDeleteResDesDataList(
    IN PRESDES_ENTRY pResList
    )
/*++

Routine Description:

    Deletes memory used by RESDES list

Arguments:

    pResList - list returned by GetResDesDataList

Return Value:

   None.

--*/
{
    PRESDES_ENTRY pTemp;
    while (pResList) {
        pTemp = pResList;
        pResList = (PRESDES_ENTRY)pResList->Next;
        if (pTemp->ResDesData) {
            MyFree(pTemp->ResDesData);
        }
        if (pTemp->ResDesHandle) {
            CM_Free_Res_Des_Handle(pTemp->ResDesHandle);
        }
        MyFree(pTemp);
    }
}

VOID
pHideAllControls(
    IN LPDMPROP_DATA lpdmpd
    )
/*++

Routine Description:

    Hide (and disable) all controls - start off with a clean slate
    Only Icon & device description will be visible

Arguments:

    hDlg = dialog handle of controls
    lpdmpd = property data

Return Value:

    none

--*/
{
    HWND hDlg = lpdmpd->hDlg;

    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_SETTINGSTATE), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_NO_RESOURCES_TEXT), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_SETTINGSLIST), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_NOALLOCTEXT), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_LCTEXT), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_LOGCONFIGLIST), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_NO_CHANGE_TEXT ), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_USESYSSETTINGS), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_CHANGE), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_MAKEFORCED), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_MFPARENT), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_MFPARENT_DESC), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_CONFLICTDEVTEXT), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_CONFLICTINFOLIST), SW_HIDE);

    lpdmpd->dwFlags |= DMPROP_FLAG_VIEWONLYRES;
}

VOID
pShowViewNoResources(
    IN LPDMPROP_DATA lpdmpd
    )
/*++

Routine Description:

    Show page indicating this device has no resources

Arguments:

    hDlg = dialog handle of controls
    lpdmpd = property data

Return Value:

    none

--*/
{
    HWND hDlg = lpdmpd->hDlg;
    TCHAR           szString[MAX_PATH];

    pHideAllControls(lpdmpd); // all hidden and disabled
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_NO_RESOURCES_TEXT), SW_SHOW);   // show and enable text
    LoadString(MyDllModuleHandle, IDS_DEVRES_NO_RESOURCES, szString, MAX_PATH);
    SetDlgItemText(hDlg, IDC_DEVRES_NO_RESOURCES_TEXT, szString);
}

BOOL
pShowViewMFReadOnly(
    IN LPDMPROP_DATA lpdmpd,
    IN BOOL HideIfProb
    )
/*++

Routine Description:

    Show page apropriate for multifunction card that cannot be edited
    Resource settings are visible

Arguments:

    hDlg = dialog handle of controls
    lpdmpd = property data

Return Value:

    none

--*/
{
    TCHAR           szString[MAX_PATH];
    DEVNODE         dnParent;
    ULONG           ulSize;
    HWND hDlg = lpdmpd->hDlg;
    HMACHINE        hMachine = pGetMachine(lpdmpd);

    pHideAllControls(lpdmpd); // all hidden and disabled
    //pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_LCTEXT), SW_SHOW);   // show config information
    //pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_LOGCONFIGLIST), SW_SHOW); // show
    //pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_CONFLICTDEVTEXT), SW_SHOW); // show conflict information space
    //pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_CONFLICTINFOLIST), SW_SHOW);
    //
    // indicate we cannot change as it's multi-function
    //
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_NO_CHANGE_TEXT), SW_SHOW);
    if (LoadString(MyDllModuleHandle, IDS_DEVRES_NO_CHANGE_MF, szString, MAX_PATH)) {
        SetDlgItemText(hDlg, IDC_DEVRES_NO_CHANGE_TEXT,  szString);
    }
    //
    // for parent description
    //
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_MFPARENT), SW_SHOW);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_MFPARENT_DESC), SW_SHOW);
    //
    // Get the Parent's Description.
    //
    LoadString(MyDllModuleHandle, IDS_DEVNAME_UNK, szString, MAX_PATH);

    if (lpdmpd->lpdi->DevInst) {

        if (CM_Get_Parent_Ex(&dnParent, lpdmpd->lpdi->DevInst, 0,hMachine)
                          == CR_SUCCESS) {


            //
            // First, try to retrieve friendly name, then fall back to device description.
            //
            ulSize = MAX_PATH * sizeof(TCHAR);
            if(CM_Get_DevNode_Registry_Property_Ex(dnParent, CM_DRP_FRIENDLYNAME,
                                                NULL, szString, &ulSize, 0,hMachine) != CR_SUCCESS) {

                ulSize = MAX_PATH * sizeof(TCHAR);
                CM_Get_DevNode_Registry_Property_Ex(dnParent, CM_DRP_DEVICEDESC,
                                                 NULL, szString, &ulSize, 0,hMachine);
            }
        }
    }

    SetDlgItemText(hDlg, IDC_DEVRES_MFPARENT_DESC, szString);

    //
    // load and display current config (if any)
    // return FALSE if no current config
    //
    return pLoadCurrentConfig(lpdmpd,HideIfProb);
}

BOOL
pShowViewReadOnly(
    IN LPDMPROP_DATA lpdmpd,
    IN BOOL HideIfProb
    )
/*++

Routine Description:

    Show page of resources, don't allow editing, don't show editing controls

Arguments:

    hDlg = dialog handle of controls
    lpdmpd = property data

Return Value:

    none

--*/
{
    HWND hDlg = lpdmpd->hDlg;

    pHideAllControls(lpdmpd); // all hidden and disabled
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_LCTEXT), SW_SHOW);   // show
    EnableWindow(GetDlgItem(hDlg, IDC_DEVRES_LCTEXT), FALSE);
    ShowWindow(GetDlgItem(hDlg, IDC_DEVRES_LOGCONFIGLIST), SW_SHOW); // shown disabled
    EnableWindow(GetDlgItem(hDlg, IDC_DEVRES_LOGCONFIGLIST), FALSE);
    ShowWindow(GetDlgItem(hDlg, IDC_DEVRES_USESYSSETTINGS), SW_SHOW); // shown disabled
    EnableWindow(GetDlgItem(hDlg, IDC_DEVRES_USESYSSETTINGS), FALSE);
    ShowWindow(GetDlgItem(hDlg, IDC_DEVRES_CHANGE), SW_SHOW); // shown disabled
    EnableWindow(GetDlgItem(hDlg, IDC_DEVRES_CHANGE), FALSE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_CONFLICTDEVTEXT), SW_SHOW); // show conflict information space
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_CONFLICTINFOLIST), SW_SHOW);

    //
    // will indicate if we're showing system settings or forced settings
    //
    CheckDlgButton(hDlg, IDC_DEVRES_USESYSSETTINGS, (lpdmpd->dwFlags & DMPROP_FLAG_USESYSSETTINGS ) ? TRUE : FALSE);

    //
    // load and display current config (if any)
    // return FALSE if no current config
    //
    return pLoadCurrentConfig(lpdmpd,HideIfProb);
}

VOID
pShowViewNoAlloc(
    IN LPDMPROP_DATA lpdmpd
    )
/*++

Routine Description:

    Modify the middle part of the control to indicate there is a problem (and there isn't much we can do about it)

Arguments:

    hDlg = dialog handle of controls
    lpdmpd = property data

Return Value:

    none

--*/
{
    HWND hDlg = lpdmpd->hDlg;

    //
    // hide all middle controls
    //
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_LCTEXT), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_LOGCONFIGLIST), SW_HIDE);
    //pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_NO_CHANGE_TEXT ), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_USESYSSETTINGS), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_CHANGE), SW_HIDE);
    //pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_MAKEFORCED), SW_HIDE);
    lpdmpd->dwFlags |= DMPROP_FLAG_VIEWONLYRES;

    //pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_NO_CHANGE_TEXT), SW_SHOW);  // this may say why
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_MAKEFORCED), SW_HIDE);

    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_CONFLICTDEVTEXT), SW_HIDE); // no alloc, so hide this header & textbox
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_CONFLICTINFOLIST), SW_HIDE);
}

VOID
pShowViewNeedForced(
    IN LPDMPROP_DATA lpdmpd
    )
/*++

Routine Description:

    Modify the middle part of the control to indicate a forced config is required

Arguments:

    hDlg = dialog handle of controls
    lpdmpd = property data

Return Value:

    none

--*/
{
    HWND hDlg = lpdmpd->hDlg;

    pShowViewNoAlloc(lpdmpd);
    //
    // show what we need for make forced config
    //
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_NO_CHANGE_TEXT), SW_SHOW);  // this may say why
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_MAKEFORCED), SW_SHOW);
}

VOID
pShowViewAllEdit(
    IN LPDMPROP_DATA lpdmpd
    )
/*++

Routine Description:

    Allow editing

Arguments:

    lpdmpd = property data

Return Value:

    none

--*/
{
    HWND hDlg = lpdmpd->hDlg;

    //
    // show middle controls for editing
    //
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_LCTEXT), SW_SHOW);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_LOGCONFIGLIST), SW_SHOW);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_NO_CHANGE_TEXT ), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_USESYSSETTINGS), SW_SHOW);
    ShowWindow(GetDlgItem(hDlg, IDC_DEVRES_CHANGE), SW_SHOW); // shown, but disabled
    EnableWindow(GetDlgItem(hDlg, IDC_DEVRES_CHANGE), FALSE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_MAKEFORCED), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_CONFLICTDEVTEXT), SW_SHOW); // show conflict information space
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_CONFLICTINFOLIST), SW_SHOW);

    pShowUpdateEdit(lpdmpd);
}

VOID
pShowUpdateEdit(
    IN LPDMPROP_DATA lpdmpd
    )
/*++

Routine Description:

    Allow editing

Arguments:

    lpdmpd = property data

Return Value:

    none

--*/
{
    HWND hDlg = lpdmpd->hDlg;

    //
    // modify editing status - we can edit
    //
    lpdmpd->dwFlags &= ~DMPROP_FLAG_VIEWONLYRES;

    if(lpdmpd->dwFlags & DMPROP_FLAG_FORCEDONLY) {
        //
        // in this case, we will never be able to use system settings
        //
        lpdmpd->dwFlags &= ~ DMPROP_FLAG_USESYSSETTINGS;
        EnableWindow(GetDlgItem(hDlg, IDC_DEVRES_USESYSSETTINGS), FALSE);
    }
    //
    // indicate if it's system settings or not
    //
    CheckDlgButton(hDlg, IDC_DEVRES_USESYSSETTINGS,
                    (lpdmpd->dwFlags & DMPROP_FLAG_USESYSSETTINGS)?TRUE:FALSE);
    //
    // we can change logconfiglist if it's not system settings
    //
    EnableWindow(GetDlgItem(hDlg, IDC_DEVRES_LCTEXT), (lpdmpd->dwFlags & DMPROP_FLAG_USESYSSETTINGS)?FALSE:TRUE);
    EnableWindow(GetDlgItem(hDlg, IDC_DEVRES_LOGCONFIGLIST), (lpdmpd->dwFlags & DMPROP_FLAG_USESYSSETTINGS)?FALSE:TRUE);
    //
    // change "Change Settings" button
    //
    pCheckEnableResourceChange(lpdmpd);
}

BOOL
pLoadCurrentConfig(
    IN LPDMPROP_DATA lpdmpd,
    BOOL HideIfProb
    )
/*++

Routine Description:

    Modify the top part, to show current configuration, if any

Arguments:

    hDlg = dialog handle of controls
    lpdmpd = property data

Return Value:

    TRUE if we're showing current config

--*/
{
    TCHAR    szMessage[MAX_PATH];
    ULONG    Problem;
    ULONG    Status;
    HWND hDlg = lpdmpd->hDlg;
    HMACHINE hMachine = pGetMachine(lpdmpd);
    BOOL     DoLoadConfig = FALSE;

    lpdmpd->SelectedLC = 0;
    lpdmpd->SelectedLCType = lpdmpd->CurrentLCType;

    if (lpdmpd->CurrentLC != 0) {
        DoLoadConfig = TRUE;
    }
    if(HideIfProb && (lpdmpd->dwFlags & DMPROP_FLAG_HASPROBLEM)) {
        //
        // if there's a problem and HideIfProb is TRUE, don't bother showing current config
        //
        DoLoadConfig = FALSE;
    }
    if (DoLoadConfig) {
        //
        // load in current configuration
        //
        pLoadConfig(lpdmpd,lpdmpd->CurrentLC,lpdmpd->CurrentLCType);
        return TRUE;
    }
    //
    // case where there is no suitable configuration
    //
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_NO_RESOURCES_TEXT), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_SETTINGSLIST), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_LCTEXT), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_LOGCONFIGLIST), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_USESYSSETTINGS), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_CHANGE), SW_HIDE);
    pShowViewNoAlloc(lpdmpd);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_SETTINGSTATE), SW_SHOW);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_NOALLOCTEXT), SW_SHOW);

    //
    // explain why there is a problem
    // goes into NOALLOCTEXT
    //
    LoadString(MyDllModuleHandle, IDS_DEVRES_NOALLOC_PROBLEM, szMessage, MAX_PATH);

    //
    // consider being more descriptive
    //
    if ((lpdmpd->lpdi->DevInst==0)
        || (CM_Get_DevNode_Status_Ex(&Status, &Problem, lpdmpd->lpdi->DevInst,
                                      0,hMachine) != CR_SUCCESS)) {
        Status = 0;
        Problem = 0;
    }

    if ((Status & DN_HAS_PROBLEM)!=0) {

        switch (Problem) {
            case CM_PROB_DISABLED:
            case CM_PROB_HARDWARE_DISABLED:
                LoadString(MyDllModuleHandle, IDS_DEVRES_NOALLOC_DISABLED, szMessage, MAX_PATH);
                break;

            case CM_PROB_NORMAL_CONFLICT:
                LoadString(MyDllModuleHandle, IDS_DEVRES_NORMAL_CONFLICT, szMessage, MAX_PATH);
                break;
            default:
                break;
        }
    }
    SetDlgItemText(hDlg, IDC_DEVRES_NOALLOCTEXT, szMessage);

    return FALSE; // display in NoAlloc state
}

BOOL
pConfigHasNoAlternates(
    LPDMPROP_DATA lpdmpd,
    LOG_CONF testLC
    )
/*++

Routine Description:

    A Basic config could be restrictive "these are the set of resources to use"
    This determines if the basic config passed is such a config

Arguments:

    testLC = basic config to test

Return Value:

    TRUE if it's a singular config

--*/
{
    HMACHINE      hMachine = NULL;
    PRESDES_ENTRY pConfigValues = NULL;
    PRESDES_ENTRY pValue = NULL;
    BOOL          bSuccess = TRUE;
    ULONG64       ulValue = 0, ulLen = 0, ulEnd = 0;
    ULONG         ulFlags = 0;
    PGENERIC_RESOURCE pGenRes = NULL;

    hMachine = pGetMachine(lpdmpd);
    pGetResDesDataList(testLC, &pConfigValues, TRUE, hMachine); // arbitratable resources
    for(pValue = pConfigValues;pValue;pValue = pValue->Next) {
        //
        // is this a singular value?
        //
        pGenRes = (PGENERIC_RESOURCE)(pValue->ResDesData);
        if(pGenRes->GENERIC_Header.GENERIC_Count != 1) {
            //
            // more than one entry - not singular
            //
            bSuccess = FALSE;
            break;
        }
        pGetRangeValues(pValue->ResDesData, pValue->ResDesType, 0, &ulValue, &ulLen, &ulEnd, NULL, &ulFlags);
        if (ulValue+(ulLen-1) != ulEnd) {
            //
            // not singular
            //
            bSuccess = FALSE;
            break;
        }
    }
    pDeleteResDesDataList(pConfigValues);

    return bSuccess;
}

BOOL
pLoadConfig(
    LPDMPROP_DATA lpdmpd,
    LOG_CONF forceLC,
    ULONG forceLCType
    )
/*++

Routine Description:

    Display a configuration

Arguments:

    hDlg = dialog handle of controls
    lpdmpd = property data
    forceLC = LogConf to display
    forceLCType = type for LogConf

Return Value:

    TRUE if config loaded

--*/
{
    HWND hDlg = lpdmpd->hDlg;
    CONFIGRET   Status = CR_SUCCESS;
    HWND        hWndList;
    LV_ITEM     lviItem;
    TCHAR       szTemp[MAX_PATH];
    int         iNewItem = 0;
    ULONG       ulSize,ulFlags;
    ULONG64     ulValue, ulLen, ulEnd, ulAlign;
    ULONG64     ulMaxMem = 0;
    ULONG       ulRange;
    LPBYTE      pData = NULL;
    RES_DES     ResDes;
    RESOURCEID  ResType;
    PITEMDATA   pItemData = NULL;
    HMACHINE    hMachine = NULL;
    PDEVICE_INFO_SET pDeviceInfoSet;
    BOOL        RetCode = FALSE;
    PRESDES_ENTRY pKnownValues = NULL;
    PRESDES_ENTRY pShowValues = NULL;
    PRESDES_ENTRY pShowEntry = NULL;
    BOOL        bFixedConfig = FALSE;
    BOOL        bNoMatch;
    BOOL        bFixed;
    ULONG       MatchLevel = NO_LC_MATCH;


    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_SETTINGSTATE), SW_SHOW);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_NO_RESOURCES_TEXT), SW_HIDE);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_SETTINGSLIST), SW_SHOW);
    pShowWindow(GetDlgItem(hDlg, IDC_DEVRES_NOALLOCTEXT), SW_HIDE);

    hWndList = GetDlgItem(hDlg, IDC_DEVRES_SETTINGSLIST);
    SendMessage(hWndList, WM_SETREDRAW, (WPARAM)FALSE, 0);
    ListView_DeleteAllItems(hWndList);

    lpdmpd->dwFlags |= DMPROP_FLAG_FIXEDCONFIG; // until we determine there is at least one setting we can edit


    if (forceLC == 0) {
        forceLC = lpdmpd->CurrentLC;
        forceLCType = lpdmpd->CurrentLCType;
    }
    if (forceLC == 0) {
        MYASSERT(FALSE);
        goto Final;
    }
    hMachine = pGetMachine(lpdmpd);

    //
    // setup values that will remain the same each time I add an item
    //
    lviItem.mask     = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
    lviItem.pszText  = szTemp;          // reuse the szTemp buffer
    lviItem.iSubItem = 0;
    lviItem.iImage   = IDI_RESOURCE - IDI_RESOURCEFIRST;

    pGetResDesDataList(forceLC, &pShowValues, TRUE, hMachine); // editable resources
    if (forceLCType == BOOT_LOG_CONF || forceLCType == FORCED_LOG_CONF || forceLCType == ALLOC_LOG_CONF) {
        bFixedConfig = TRUE;
        if (forceLC == lpdmpd->CurrentLC && lpdmpd->MatchingLC != 0) {
            //
            // we're displaying CurrentLC, use flags & resdes's from matching LC where possible
            //
            if (pGetResDesDataList(lpdmpd->MatchingLC, &pKnownValues, TRUE, hMachine)) {
                //
                // match-up currentLC with some matching LC, so we can use flags/ranges from matching LC
                //
                MatchLevel = pMergeResDesDataLists(pShowValues,pKnownValues,NULL);
            }
        }
    } else if (lpdmpd->CurrentLC != 0) {
        //
        // the config we're displaying may allow ranges of values
        // we're going to try and match up what we are displaying to current config
        //
        if (pGetResDesDataList(lpdmpd->CurrentLC, &pKnownValues, TRUE, hMachine)) {
            //
            // try and use current values where possible
            //
            MatchLevel = pMergeResDesDataLists(pKnownValues,pShowValues,NULL);
        }
    }

    pShowEntry = pShowValues;

    while (pShowEntry) {
        bNoMatch = FALSE;
        bFixed = FALSE;
        ResDes = (RES_DES)0;
        ResType = pShowEntry->ResDesType;
        ulRange = 0;

        if (bFixedConfig) {
            //
            // we've got a current config
            //
            pGetHdrValues(pShowEntry->ResDesData, pShowEntry->ResDesType, &ulValue, &ulLen, &ulEnd, &ulFlags);
            if((ResType ==ResType_Mem) && (ulEnd > ulMaxMem)) {
                //
                // base our memory display on the fixed-config only
                //
                ulMaxMem = ulEnd;
            }
            if (pShowEntry->CrossLink) {
                //
                // use range's res-des
                //
                ResDes = pShowEntry->CrossLink->ResDesHandle;
                pShowEntry->CrossLink->ResDesHandle = (RES_DES)0;
                //
                // allow adjustment based on nearest basic config
                //
                pGetMatchingRange(ulValue,ulLen,pShowEntry->CrossLink->ResDesData, pShowEntry->CrossLink->ResDesType,&ulRange,&bFixed,NULL);
            } else {
                //
                // no range res-des
                //
                ResDes = (RES_DES)0;
                //
                // indicate that this is a non-adjustable value
                //
                bFixed = TRUE;
            }
        } else {
            //
            // we've got resource-ranges
            //
            if (pShowEntry->CrossLink) {
                //
                // take current settings from what we merged in
                //
                pGetHdrValues(pShowEntry->CrossLink->ResDesData, pShowEntry->CrossLink->ResDesType, &ulValue, &ulLen, &ulEnd, &ulFlags);
            } else {
                //
                // just take first range
                //
                pGetRangeValues(pShowEntry->ResDesData, pShowEntry->ResDesType, 0, &ulValue, &ulLen, &ulEnd, &ulAlign, &ulFlags);
            }
            //
            // range check if this is a memory resource - find highest memory value
            //
            if(ResType ==ResType_Mem) {
                ULONG64 HighVal;
                ULONG64 HighLen;
                if(LocateClosestValue(pShowEntry->ResDesData, pShowEntry->ResDesType,(ULONG64)(-1),ulLen, 0 ,&HighVal,&HighLen,NULL)) {
                    HighVal += HighLen-1;
                    if(HighVal > ulMaxMem) {
                        ulMaxMem = ulEnd;
                    }
                }
            }

            pGetMatchingRange(ulValue,ulLen,pShowEntry->ResDesData, pShowEntry->ResDesType,&ulRange,&bFixed,&ulFlags);
            //
            // use res-des from range
            //
            ResDes = pShowEntry->ResDesHandle;
            pShowEntry->ResDesHandle = (RES_DES)0;

            if (pShowEntry->CrossLink == NULL && bFixed == FALSE) {
                //
                // unknown value
                //
                bNoMatch = TRUE;
            }
        }

        if (ulLen>0) {
            //
            // Write first column text field (uses szTemp, lParam is res type)
            //
            LoadString(MyDllModuleHandle, IDS_RESOURCE_BASE + ResType, szTemp, MAX_PATH);
            ulRange = 0;

            pItemData = (PITEMDATA)MyMalloc(sizeof(ITEMDATA));
            if (pItemData != NULL) {
                pItemData->ResType = ResType;
                pItemData->MatchingResDes = ResDes;
                pItemData->RangeCount = ulRange;
                pItemData->ulValue = ulValue;                   // selected value
                pItemData->ulLen = ulLen;
                pItemData->ulEnd = ulValue + ulLen - 1;
                pItemData->ulFlags = ulFlags;
                pItemData->bValid = !bNoMatch;                  // if no chosen value
                pItemData->bFixed = bFixed;
            }
            if (bFixed == FALSE) {
                //
                // we have at least one editable value
                //
                lpdmpd->dwFlags &= ~DMPROP_FLAG_FIXEDCONFIG;
            }

            lviItem.iItem = iNewItem;
            lviItem.lParam = (LPARAM)pItemData;
            ListView_InsertItem(hWndList, &lviItem);

            //
            // Write second column text field (uses szTemp, lParam is res handle)
            //
            if (bNoMatch) {
                pFormatResString(lpdmpd,szTemp, 0, 0, ResType);
            } else {
                pFormatResString(lpdmpd,szTemp, ulValue, ulLen, ResType);
            }
            ListView_SetItemText(hWndList, iNewItem, 1, szTemp);

            ++iNewItem;
        }
        pShowEntry = pShowEntry->Next;
    }

    if(ulMaxMem > 0xFFFFFFFF) {
        //
        // an excuse to require 64-bit address range
        //
        lpdmpd->dwFlags |= DMPROP_FLAG_64BIT_RANGE;
    }

    SendMessage(hWndList, WM_SETREDRAW, (WPARAM)TRUE, 0);

    RetCode = TRUE;

Final:

    pDeleteResDesDataList(pKnownValues);
    pDeleteResDesDataList(pShowValues);

    //
    // initialize listview headings here
    //
    ListView_SetColumnWidth(hWndList, 0, LVSCW_AUTOSIZE_USEHEADER);
    ListView_SetColumnWidth(hWndList, 1, LVSCW_AUTOSIZE_USEHEADER);
    //
    // change "Change Settings" button
    //
    pCheckEnableResourceChange(lpdmpd);

    return RetCode;
}

BOOL
bIsMultiFunctionChild(
    PSP_DEVINFO_DATA lpdi,
    HMACHINE         hMachine
    )
/*++

Routine Description:

    Returns flag indicating if this is a child of a
    multifunction device
Arguments:

Return Value:

    TRUE if MF child

--*/
{
    ULONG   Status;
    ULONG   ProblemNumber;

    if (lpdi->DevInst) {

        if (CM_Get_DevNode_Status_Ex(&Status, &ProblemNumber,
                                  lpdi->DevInst, 0,hMachine) == CR_SUCCESS) {
            //
            // If the passed in dev is not an MF child, then it is the top
            // level MF_Parent
            //
            if (Status & DN_MF_CHILD) {
                return TRUE;
            } else {
                return FALSE;
            }
        }
    }

    return FALSE;

}

VOID
pSelectLogConf(
    LPDMPROP_DATA lpdmpd,
    LOG_CONF forceLC,
    ULONG forceLCType,
    BOOL Always
)
/*++

Routine Description:

    Selects a LogConf, showing the config in the LC control

Arguments:

Return Value:

    TRUE if MF child

--*/
{
    HWND hDlg = lpdmpd->hDlg;
    int count;
    int i;
    LOG_CONF LogConf;

    if (Always == FALSE && forceLC == lpdmpd->SelectedLC) {
        //
        // selection remains the same
        //
        return;
    }

    count = (int)SendDlgItemMessage(hDlg, IDC_DEVRES_LOGCONFIGLIST,CB_GETCOUNT, (WPARAM)0, (LPARAM)0);
    if (count == 0) {
        MYASSERT(FALSE/*shouldn't get here*/);
        pLoadCurrentConfig(lpdmpd,FALSE);
        return;
    }

    if (forceLC == 0 && lpdmpd->CurrentLC == 0) {
        //
        // no currentLC, so select first default
        //
        forceLC = (LOG_CONF)SendDlgItemMessage(hDlg, IDC_DEVRES_LOGCONFIGLIST,
                                               CB_GETITEMDATA, (WPARAM)0, (LPARAM)0);
        if (forceLC == (LOG_CONF)0) {
            MYASSERT(FALSE/*shouldn't get here*/);
            pLoadCurrentConfig(lpdmpd,FALSE);
            return;
        }
        forceLCType = lpdmpd->ConfigListLCType;
    }

    for (i=0;i<count;i++) {
        LogConf = (LOG_CONF)SendDlgItemMessage(hDlg, IDC_DEVRES_LOGCONFIGLIST,
                                               CB_GETITEMDATA, (WPARAM)i, (LPARAM)0);
        if (LogConf == forceLC) {
            //
            // set these first so we don't recurse around
            //
            lpdmpd->SelectedLC = forceLC;
            lpdmpd->SelectedLCType = forceLCType;
            //
            // change dialog to reflect new selection
            //
            SendDlgItemMessage(hDlg, IDC_DEVRES_LOGCONFIGLIST,CB_SETCURSEL, (WPARAM)i, (LPARAM)0);
            pLoadConfig(lpdmpd,forceLC,forceLCType);
            pShowConflicts(lpdmpd);
            return;
        }
    }
    SendDlgItemMessage(hDlg, IDC_DEVRES_LOGCONFIGLIST,CB_SETCURSEL, (WPARAM)(-1), (LPARAM)0);
    pLoadConfig(lpdmpd,forceLC,forceLCType);
    pShowConflicts(lpdmpd);
}


VOID
pChangeCurrentResSetting(
    IN LPDMPROP_DATA lpdmpd
    )
/*++

Routine Description:

    Brings up edit dialog to change currently selected resource

Arguments:

Return Value:

    none

--*/
{
    HWND                hDlg = lpdmpd->hDlg;
    RESOURCEEDITINFO    rei;
    HWND                hList =  GetDlgItem(hDlg, IDC_DEVRES_SETTINGSLIST);
    int                 iCur;
    PITEMDATA           pItemData = NULL;
    LV_ITEM             lviItem;
    GENERIC_RESOURCE    GenResInfo;
    PDEVICE_INFO_SET    pDeviceInfoSet;
    BOOL                changed = FALSE;
    TCHAR               szTemp[MAX_PATH];

    pItemData = pGetResourceToChange(lpdmpd,&iCur);
    if (pItemData == NULL) {
        //
        // we cannot edit this resource for some reason, give the user a hint
        // and maybe I'll get less ear-ache "I cannot change the settings"
        //
        if ((lpdmpd->dwFlags & DMPROP_FLAG_VIEWONLYRES)!=0 ||
                (lpdmpd->dwFlags & DMPROP_FLAG_USESYSSETTINGS)!=0) {
            //
            // editing not allowed - prob double-clicked on settings
            //
            return;
        }
        if (lpdmpd->dwFlags & DMPROP_FLAG_FIXEDCONFIG) {
            pWarnResSettingNotEditable(hDlg, IDS_DEVRES_NOMODIFYALL);
        } else {
            //
            // see if user needs to select a resource
            //
            iCur = (int)ListView_GetNextItem(hList,-1, LVNI_SELECTED);
            if (iCur == LB_ERR) {
                //
                // no selection
                //
                pWarnResSettingNotEditable(hDlg, IDS_DEVRES_NOMODIFYSELECT);
            } else {
                //
                // resource is just not editable
                //
                pWarnResSettingNotEditable(hDlg, IDS_DEVRES_NOMODIFYSINGLE);
            }
        }
        goto clean0;
    }

    ZeroMemory(&rei,sizeof(rei));
    rei.hMachine = pGetMachine(lpdmpd);
    rei.KnownLC = lpdmpd->CurrentLC;
    rei.MatchingBasicLC = lpdmpd->MatchingLC;
    rei.SelectedBasicLC = lpdmpd->SelectedLC;
    rei.lpdi = lpdmpd->lpdi;
    rei.dwPropFlags = lpdmpd->dwFlags;
    rei.bShareable = FALSE;
    rei.ridResType = pItemData->ResType;
    rei.ResDes = pItemData->MatchingResDes;
    rei.ulCurrentVal = pItemData->ulValue;
    rei.ulCurrentLen = pItemData->ulLen;
    rei.ulCurrentEnd = pItemData->ulEnd;
    rei.ulCurrentFlags = pItemData->ulFlags;
    rei.ulRangeCount = pItemData->RangeCount;
    rei.pData = NULL;

    if (DialogBoxParam(MyDllModuleHandle,
                       MAKEINTRESOURCE(IDD_EDIT_RESOURCE),
                       hDlg,
                       EditResourceDlgProc,
                       (LPARAM)(PRESOURCEEDITINFO)&rei) != IDOK) {
        goto clean0;
    }
    //
    // Update The Current Resource settings to Future
    // Settings, and update the Conflict list.
    //
    pItemData->ulValue = rei.ulCurrentVal;
    pItemData->ulLen = rei.ulCurrentLen;
    pItemData->ulEnd = rei.ulCurrentEnd;
    pItemData->ulFlags = rei.ulCurrentFlags;
    pItemData->RangeCount = rei.ulRangeCount;
    pItemData->bValid = TRUE; // indicate that user has explicitly changed this value

    pFormatResString(lpdmpd,szTemp,
                    rei.ulCurrentVal,
                    rei.ulCurrentLen,
                    rei.ridResType);

    ListView_SetItemText(hList, iCur, 1, szTemp);
    pShowConflicts(lpdmpd);

    //
    // clear the flag for saving changes
    //
    lpdmpd->dwFlags &= ~DMPROP_FLAG_CHANGESSAVED;
    PropSheet_Changed(GetParent(hDlg), hDlg);

clean0:
    ;
}

VOID
pShowConflicts(
    IN LPDMPROP_DATA lpdmpd
    )
/*++

Routine Description:

    Selects a LogConf, showing the config in the LC control

Arguments:

Return Value:

    TRUE if MF child

--*/
{
    HWND        hDlg = lpdmpd->hDlg;
    CONFIGRET   Status = CR_SUCCESS;
    LPVOID      vaArray[4];
    TCHAR       szTemp[MAX_PATH+4], szBuffer[MAX_PATH+16], szSetting[MAX_PATH];
    TCHAR       szFormat[MAX_PATH], szItemFormat[MAX_PATH];
    TCHAR       szUnavailable[MAX_PATH];
    LPTSTR      pszConflictList = NULL, pszConflictList2 = NULL;
    ULONG       ulSize = 0, ulLength, ulBufferLen, ulNewLength;
    ULONG       ulStartOffset = 0;
    int         Count = 0, i = 0;
    PITEMDATA   pItemData = NULL;
    LPBYTE      pResourceData = NULL;
    HWND        hwndResList = GetDlgItem(hDlg, IDC_DEVRES_SETTINGSLIST);
    HMACHINE    hMachine;
    ULONG       ConflictCount = 0;
    ULONG       ConflictIndex = 0;
    CONFLICT_LIST ConflictList = 0;
    PDEVICE_INFO_SET pDeviceInfoSet;
    CONFLICT_DETAILS ConflictDetails;
    BOOL        ReservedResource = FALSE;
    BOOL        AnyReportedResources = FALSE;
    BOOL        AnyBadResources = FALSE;
    PCONFLICT_EXCEPTIONS pConflictExceptions = NULL;
    //
    // number of resources listed
    //
    Count = ListView_GetItemCount(hwndResList);
    if (Count <= 0) {
       goto Clean0;
    }

    //
    // initial buffer that holds the strings
    // with all the conflict info in them
    //
    ulBufferLen = 2048;
    ulLength = 0;

    pszConflictList = MyMalloc(ulBufferLen * sizeof(TCHAR));
    if (pszConflictList == NULL) {
        goto Clean0;
    }
    pszConflictList[0] = 0;

    //
    // obtain machine
    //
    if(!(pDeviceInfoSet = AccessDeviceInfoSet(lpdmpd->hDevInfo))) {
        goto Clean0;
    }
    hMachine = pDeviceInfoSet->hMachine;
    UnlockDeviceInfoSet (pDeviceInfoSet);

    //
    // do these once - these format strings use %1!s! type formats (FormatMessage)
    //
    LoadString(MyDllModuleHandle, IDS_CONFLICT_FMT, szFormat, MAX_PATH);
    LoadString(MyDllModuleHandle, IDS_CONFLICT_UNAVAILABLE, szUnavailable, MAX_PATH);

    //
    // for every listed resource
    //

    for (i = 0; i < Count; i++) {

        ConflictList = 0;
        ConflictCount = 0;

        //
        // get the resource we're about to test
        //
        pItemData = (PITEMDATA)pGetListViewItemData(hwndResList, i, 0);
        if (pItemData == NULL || pItemData->bValid == FALSE) {
            //
            // for whatever reason, we don't want to show conflict information on this resource
            //
            ListView_SetItemState(hwndResList, i,
                                  INDEXTOOVERLAYMASK(0),
                                  LVIS_OVERLAYMASK);
            goto NextResource;
        }

        //
        // this is set to indicate conflict not reported, but is reserved
        //
        ReservedResource = FALSE;

        //
        // need resource-data for determining conflict
        //
        if (MakeResourceData(&pResourceData, &ulSize,
                             pItemData->ResType,
                             pItemData->ulValue,
                             pItemData->ulLen,
                             pItemData->ulFlags)) {

            Status = CM_Query_Resource_Conflict_List(&ConflictList,
                                                        lpdmpd->lpdi->DevInst,
                                                        pItemData->ResType,
                                                        pResourceData,
                                                        ulSize,
                                                        DEVRES_WIDTH_FLAGS,
                                                        hMachine);

            if (Status != CR_SUCCESS) {
                //
                // on the unlikely event of an error, remember an error occurred
                //
                ConflictList = 0;
                ConflictCount =  0;
                AnyBadResources = TRUE;
            } else {
                //
                // find out how many things conflicted
                //
                Status = CM_Get_Resource_Conflict_Count(ConflictList,&ConflictCount);
                if (Status != CR_SUCCESS) {
                    MYASSERT(Status == CR_SUCCESS);
                    ConflictCount =  0;
                    AnyBadResources = TRUE;
                }
            }
            if(ConflictCount && (lpdmpd->dwFlags & DMPROP_FLAG_SINGLE_CONFIG) && !(lpdmpd->dwFlags & DMPROP_FLAG_HASPROBLEM)) {
                //
                // NTRAID#166214-2000/08/19-JamieHun Conflict Supression Hack
                //
                // rules are
                //   (1) device doesn't have a problem
                //   (2) device can only have one configuration (ie, there's no basic config, or the basic config is singular)
                //   (3) it has a ResourcePickerExceptions string, and that string indicates that the exception is allowed for the specific conflict

                if(pConflictExceptions==NULL) {
                    pConflictExceptions = pLoadConflictExceptions(lpdmpd);
                }

                if (pConflictExceptions) {

                    BOOL muted = TRUE;
                    //
                    // count from 0 (first conflict) through to ConflictCount (excl)
                    //
                    for(ConflictIndex = 0; ConflictIndex < ConflictCount; ConflictIndex ++) {
                        //
                        // obtain details for this conflict
                        //
                        ZeroMemory(&ConflictDetails,sizeof(ConflictDetails));
                        ConflictDetails.CD_ulSize = sizeof(ConflictDetails);
                        ConflictDetails.CD_ulMask = CM_CDMASK_DEVINST | CM_CDMASK_DESCRIPTION | CM_CDMASK_FLAGS;
                        Status = CM_Get_Resource_Conflict_Details(ConflictList,ConflictIndex,&ConflictDetails);
                        if (Status == CR_SUCCESS) {
                            if (!pIsConflictException(lpdmpd,pConflictExceptions,ConflictDetails.CD_dnDevInst,ConflictDetails.CD_szDescription,pItemData->ResType,pItemData->ulValue,pItemData->ulLen)) {
                                muted = FALSE;
                                break;
                            }
                        }
                    }
                    if(muted) {
                        ConflictCount = 0;
                    }
                }
            }
            if (ConflictCount || ReservedResource) {
                ulStartOffset = ulLength;  // record start in case we decide to backtrack
                AnyReportedResources = TRUE; // say we reported at least one problem

            TreatAsReserved:

                ulLength = ulStartOffset;
                pszConflictList[ulLength] = 0;
                //
                // we're going to mark the resource as a problem
                //

                ListView_GetItemText(hwndResList, i, 1, szSetting, MAX_PATH);


                switch (pItemData->ResType) {
                    case ResType_Mem:
                        LoadString(MyDllModuleHandle, IDS_MEMORY_FULL, szBuffer, MAX_PATH);
                        break;
                    case ResType_IO:
                        LoadString(MyDllModuleHandle, IDS_IO_FULL, szBuffer, MAX_PATH);
                        break;
                    case ResType_DMA:
                        LoadString(MyDllModuleHandle, IDS_DMA_FULL, szBuffer, MAX_PATH);
                        break;
                    case ResType_IRQ:
                        LoadString(MyDllModuleHandle, IDS_IRQ_FULL, szBuffer, MAX_PATH);
                        break;
                    default:
                        MYASSERT(FALSE);
                }

                if ( ReservedResource == FALSE) {

                    //
                    // count from 0 (header) 1 (first conflict) through to ConflictCount
                    //
                    for(ConflictIndex = 0; ConflictIndex <= ConflictCount; ConflictIndex ++) {
                        if (ConflictIndex == 0) {
                            //
                            // first pass through, do header message
                            //
                            vaArray[0] = szBuffer;
                            vaArray[1] = szSetting;
                            FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY ,
                                                szFormat,
                                                0,0,
                                                szTemp,MAX_PATH,
                                                (va_list*)vaArray); // FORMAT_MESSAGE_ARGUMENT_ARRAY

                        } else {

                            //
                            // obtain details for this conflict
                            //
                            ZeroMemory(&ConflictDetails,sizeof(ConflictDetails));
                            ConflictDetails.CD_ulSize = sizeof(ConflictDetails);
                            ConflictDetails.CD_ulMask = CM_CDMASK_DEVINST | CM_CDMASK_DESCRIPTION | CM_CDMASK_FLAGS;

                            Status = CM_Get_Resource_Conflict_Details(ConflictList,ConflictIndex-1,&ConflictDetails);
                            if (Status == CR_SUCCESS) {
                                if ((ConflictDetails.CD_ulFlags & CM_CDFLAGS_RESERVED) != 0) {
                                    //
                                    // treat as reserved - backtrack
                                    //
                                    ReservedResource = TRUE;
                                    goto TreatAsReserved;
                                } else {
                                    if (ConflictDetails.CD_szDescription[0] == 0) {
                                        //
                                        // treat as reserved - backtrack
                                        //
                                        ReservedResource = TRUE;
                                        goto TreatAsReserved;
                                    }
                                    wsprintf(szBuffer,TEXT("  %s\r\n"),ConflictDetails.CD_szDescription);
                                }
                            } else {
                                //
                                // treat as reserved
                                //
                                ReservedResource = TRUE;
                                goto TreatAsReserved;
                            }
                            lstrcpyn(szTemp,szBuffer,MAX_PATH);
                        }

                        ulNewLength = ulLength + lstrlen(szTemp);   // excluding terminating NUL

                        if ((ulNewLength+1) < ulBufferLen) {
                            //
                            // need to allocate more space - we'll double it and add some more every time
                            //
                            pszConflictList2 = MyRealloc(pszConflictList,(ulBufferLen+ulNewLength+1)  * sizeof(TCHAR));
                            if (pszConflictList2 != NULL) {
                                //
                                // succeeded in resizing buffer
                                //
                                pszConflictList = pszConflictList2;
                                ulBufferLen = ulBufferLen+ulNewLength+1;
                            }
                        }
                        if ((ulNewLength+1) < ulBufferLen) {
                            lstrcpy(pszConflictList + ulLength , szTemp);
                            ulLength = ulNewLength;
                        }

                    }
                } else {
                    //
                    // there is some other problem with resource
                    //

                    vaArray[0] = szBuffer;
                    vaArray[1] = szSetting;
                    FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY ,
                                        szUnavailable,
                                        0,0,
                                        szTemp,MAX_PATH,
                                        (va_list*)vaArray); // FORMAT_MESSAGE_ARGUMENT_ARRAY

                    ulNewLength = ulLength + lstrlen(szTemp);   // excluding terminating NUL

                    if ((ulNewLength+1) < ulBufferLen) {
                        //
                        // need to allocate more space - we'll double it and add some more every time
                        //
                        pszConflictList2 = MyRealloc(pszConflictList,(ulBufferLen+ulNewLength+1)  * sizeof(TCHAR));
                        if (pszConflictList2 != NULL) {
                            //
                            // succeeded in resizing buffer
                            //
                            pszConflictList = pszConflictList2;
                            ulBufferLen = ulBufferLen+ulNewLength+1;
                        }
                    }
                    if ((ulNewLength+1) < ulBufferLen) {
                        lstrcpy(pszConflictList + ulLength , szTemp);
                        ulLength = ulNewLength;
                    }
                }

                //
                // Set the Conflict Overlay for this resource.
                //
                ListView_SetItemState(hwndResList, i,
                               INDEXTOOVERLAYMASK(IDI_CONFLICT - IDI_RESOURCEOVERLAYFIRST + 1),
                               LVIS_OVERLAYMASK);

            } else {
                //
                // resource is (aparently) working fine
                //
                ListView_SetItemState(hwndResList, i,
                                      INDEXTOOVERLAYMASK(0),
                                      LVIS_OVERLAYMASK);
            }

            if (ConflictList) {
                CM_Free_Resource_Conflict_Handle(ConflictList);
            }

            if (pResourceData != NULL) {
                MyFree(pResourceData);
            }
        } else {
            //
            // couldn't make the resource descriptor
            AnyBadResources = TRUE;
        }

        NextResource:
            ;
    }


Clean0:
    ;

    //
    // If there were any conflicts, put the list in the multiline edit box.
    //
    if (AnyReportedResources) {
        SetDlgItemText(hDlg, IDC_DEVRES_CONFLICTINFOLIST, pszConflictList);
    } else if (AnyBadResources) {
        //
        // this would most likely occur on
        // (1) running this on 95/98 (shouldn't happen)
        // (2) using new setupapi on old cfgmgr32
        //
        LoadString(MyDllModuleHandle, IDS_CONFLICT_GENERALERROR, szBuffer, MAX_PATH);
        SetDlgItemText(hDlg, IDC_DEVRES_CONFLICTINFOLIST, szBuffer);
    } else {
        LoadString(MyDllModuleHandle, IDS_DEVRES_NOCONFLICTDEVS, szBuffer, MAX_PATH);
        SetDlgItemText(hDlg, IDC_DEVRES_CONFLICTINFOLIST, szBuffer);
    }
    if(pszConflictList != NULL) {
        MyFree(pszConflictList);
    }
    if (pConflictExceptions != NULL) {
        pFreeConflictExceptions(pConflictExceptions);
    }

    return;

}

int
pOkToSave(
    IN LPDMPROP_DATA lpdmpd
    )
/*++

Routine Description:

    Check to see if there's something the user hasn't done

Arguments:

Return Value:

    IDYES = save settings
    IDNO  = don't save settings
    IDCANCEL = don't exit

--*/
{
    HWND        hDlg = lpdmpd->hDlg;
    HWND        hList = GetDlgItem(hDlg, IDC_DEVRES_SETTINGSLIST);
    int         iCur;
    int         nRes;
    PITEMDATA   pItemData;

    if (lpdmpd->dwFlags & DMPROP_FLAG_NO_RESOURCES) {
        //
        // no changes - because there are no resources
        //
        return IDNO;
    }
    if (lpdmpd->dwFlags & DMPROP_FLAG_CHANGESSAVED) {
        //
        // no changes
        //
        return IDNO;
    }
    if (lpdmpd->dwFlags & DMPROP_FLAG_USESYSSETTINGS) {
        //
        // always ok to "use sys settings"
        //
        return IDYES;
    }
    //
    // user is forcing a config - let's see if all settings are valid
    //
    //
    // The resource descriptors are out-of-order.  Maintain the original ordering.
    //
    // First, build up a linked list of the data in the listview resource items.
    //
    iCur = (int)ListView_GetNextItem(hList, -1, LVNI_ALL);

    while (iCur >= 0) {

        pItemData = (PITEMDATA)pGetListViewItemData(hList, iCur, 0);
        if (pItemData) {
            if (pItemData->bValid == FALSE) {
                //
                // we've got an invalid entry - can't save
                //
                nRes = pWarnNoSave(hDlg,IDS_FORCEDCONFIG_PARTIAL);
                if (nRes != IDOK) {
                    return IDCANCEL;
                }
                return IDNO;
            }
        }

        iCur = (int)ListView_GetNextItem(hList, iCur, LVNI_ALL);
    }

    //
    // everything checks out
    //

    return IDYES;
}

