/*++

Copyright (c) 1995-2001  Microsoft Corporation

Module Name:

    hwprof.c

Abstract:

    This module contains the dialog box procedure for the Hardware Profiles
    Dialog Box in the System Applet.

Author:

    Paula Tomlinson (paulat) 8-22-1995

Environment:

    User mode only.

Revision History:

    22-Aug-1995     paulat

        Creation and initial implementation.

    21-Jan-1999     jamesca

        Added handling for hardware profile aliases and hardware-detected
        profile attributes.

--*/


//
// include files
//

#include "sysdm.h"
#include <stdlib.h>
#include <usp10.h>
#include <dbt.h>

//
// private types and definitions
//

#define MAX_PROFILES             9999
#define MAX_ALIASES              9999
#define MAX_FRIENDLYNAME_LEN     80
#define MAX_PROFILEID_LEN        5
#define MAX_DOCKID_LEN           128
#define MAX_SERIALNUM_LEN        128

#define MAX_USER_WAIT            500
#define MIN_USER_WAIT            0
#define DEFAULT_USER_WAIT        30

#define MAX_GUID_STRING_LEN      39   // 38 chars + terminating null

typedef struct HWPROFILE_s {
   HWND     hParent;
   ULONG    ulFromProfileID;
   TCHAR    szFromFriendlyName[MAX_FRIENDLYNAME_LEN];
   ULONG    ulToProfileID;
   TCHAR    szToFriendlyName[MAX_FRIENDLYNAME_LEN];
} HWPROFILE, *PHWPROFILE;


typedef struct HWPROF_VALUES_s {
   ULONG    ulAction;
   ULONG    ulProfile;
   ULONG    ulPreferenceOrder;
   ULONG    ulDockState;
   BOOL     bAliasable;
   BOOL     bCloned;
   BOOL     bPortable;
   ULONG    ulCreatedFrom;
   WCHAR    szDockID[MAX_DOCKID_LEN];
   WCHAR    szSerialNumber[MAX_SERIALNUM_LEN];
   WCHAR    szFriendlyName[MAX_FRIENDLYNAME_LEN];
} HWPROF_VALUES, *PHWPROF_VALUES;


typedef struct HWPROF_INFO_s {
   ULONG             ulNumProfiles;
   ULONG             ulActiveProfiles;
   PHWPROF_VALUES    pHwProfValues;
   ULONG             ulSelectedProfile;
   ULONG             ulSelectedProfileIndex;
   BOOL              bPortable;
   BOOL              bHwDetectedPortable;
   ULONG             ulUndockedProfileNameCount;
   ULONG             ulDockedProfileNameCount;
   ULONG             ulUnknownProfileNameCount;
} HWPROF_INFO, *PHWPROF_INFO;


#define HWP_NO_ACTION   0x00000000
#define HWP_DELETE      0x00000001
#define HWP_CREATE      0x00000002
#define HWP_RENAME      0x00000004
#define HWP_REORDER     0x00000008
#define HWP_PROPERTIES  0x00000010
#define HWP_NEWPROFILE  0x00001000

//
// private prototypes
//

BOOL
GetCurrentProfile(
      PULONG  pulProfile
      );

BOOL
GetRegProfileCount(
      PULONG   pulProfiles
      );

BOOL
FillProfileList(
      HWND  hDlg
      );

BOOL
IsProfileNameInUse(
      HWND     hDlg,
      LPTSTR   pszFriendlyName
      );

BOOL
CopyHardwareProfile(
      HWND   hDlg,
      ULONG_PTR ulIndex,
      ULONG  ulProfile,
      LPTSTR szNewFriendlyName
      );

BOOL
RenameHardwareProfile(
      HWND   hDlg,
      ULONG_PTR ulIndex,
      ULONG  ulProfile,
      LPTSTR szNewFriendlyName
      );

BOOL
DeleteHardwareProfile(
      HWND   hDlg,
      ULONG_PTR ulIndex
      );

BOOL
GetUserWaitInterval(
      PULONG   pulWait
      );

BOOL
SetUserWaitInterval(
      ULONG   ulWait
      );

BOOL
GetFreeProfileID(
      PHWPROF_INFO   pInfo,
      PULONG         pulProfile
      );

ULONG
GetOriginalProfile(
      PHWPROF_INFO  pInfo,
      ULONG         ulProfile,
      ULONG         ulBufferIndex
      );

BOOL
InsertRank(
      PHWPROF_INFO   pInfo,
      ULONG          ulRank
      );

BOOL
DeleteRank(
      PHWPROF_INFO   pInfo,
      ULONG          ulRank
      );

BOOL
FlushProfileChanges(
      HWND hDlg,
      HWND hList
      );

BOOL
WriteProfileInfo(
      PHWPROF_VALUES pProfValues
      );

BOOL
RemoveNewProfiles(
      PHWPROF_INFO   pInfo
      );

BOOL
SwapPreferenceOrder(
      HWND  hDlg,
      HWND  hList,
      ULONG_PTR ulIndex1,
      ULONG_PTR ulIndex2
      );

BOOL
DeleteProfileDependentTree(
      ULONG ulProfile
      );

BOOL
CopyAliasEntries(
      ULONG ulSrcProfile,
      ULONG ulDestProfile
      );

BOOL
DeleteAliasEntries(
      ULONG ulProfile
      );

BOOL
CopyAliasEntryType(
      ULONG  ulSrcProfile,
      ULONG  ulDestProfile,
      LPWSTR szSubKeyName
      );

BOOL
DeleteAliasEntryType(
      ULONG  ulProfile,
      LPWSTR szSubKeyName
      );

BOOL
CopyRegistryNode(
      HKEY     hSrcKey,
      HKEY     hDestKey
      );

BOOL
DeleteRegistryNode(
      HKEY     hNodeKey,
      LPTSTR   szSubKey
      );
VOID
AdjustProfileTypeCounter(
      PHWPROF_INFO   pInfo,
      ULONG          ulDockState,
      BOOL           bIncrement
      );

BOOL
StripCurrentTag(
      LPTSTR   szOriginalName,
      ULONG    ulProfile,
      ULONG    ulCurrentProfile
      );

BOOL
AppendCurrentTag(
      LPTSTR   szTaggedName,
      LPCTSTR  szOriginalName,
      ULONG    ulProfile,
      ULONG    ulCurrentProfile
      );

VOID
CreateHwProfileFriendlyName(
      IN  HWND    hDlg,
      IN  ULONG   ulDockState,
      OUT LPTSTR  szFriendlyName
   );

VOID
DisplayPrivateMessage(
      HWND  hDlg,
      UINT  uiPrivateError
      );

VOID
DisplaySystemMessage(
      HWND  hWnd,
      UINT  uiSystemError
      );

BOOL
UpdateOrderButtonState(
      HWND  hDlg
      );

BOOL
DisplayProperties(
      IN HWND           hOwnerDlg,
      IN PHWPROF_INFO   pProfValues
      );

typedef BOOL (CALLBACK FAR * LPFNADDPROPSHEETPAGE)(HPROPSHEETPAGE, LPARAM);


//
// global strings
//
WCHAR pszErrorCaption[MAX_PATH];
WCHAR pszRegDefaultFriendlyName[MAX_FRIENDLYNAME_LEN];
WCHAR pszCurrentTag[64];
WCHAR pszUnavailable[64];
WCHAR pszDocked[64];
WCHAR pszUndocked[64];
WCHAR pszUnknown[64];

WCHAR pszRegIDConfigDB[] = TEXT("System\\CurrentControlSet\\Control\\IDConfigDB");
WCHAR pszRegHwProfiles[] = TEXT("System\\CurrentControlSet\\Hardware Profiles");
WCHAR pszRegKnownDockingStates[] =  TEXT("Hardware Profiles");
WCHAR pszRegCurrentDockInfo[] =     TEXT("CurrentDockInfo");
WCHAR pszRegDockingState[] =        TEXT("DockingState");
WCHAR pszRegAlias[] =               TEXT("Alias");
WCHAR pszRegAcpiAlias[] =           TEXT("AcpiAlias");
WCHAR pszRegProfileNumber[] =       TEXT("ProfileNumber");
WCHAR pszRegCurrentConfig[] =       TEXT("CurrentConfig");
WCHAR pszRegUserWaitInterval[] =    TEXT("UserWaitInterval");
WCHAR pszRegFriendlyName[] =        TEXT("FriendlyName");
WCHAR pszRegPristine[] =            TEXT("Pristine");
WCHAR pszRegHwProfileGuid[] =       TEXT("HwProfileGuid");
WCHAR pszRegPreferenceOrder[] =     TEXT("PreferenceOrder");
WCHAR pszRegDockState[] =           TEXT("DockState");
WCHAR pszRegAliasable[] =           TEXT("Aliasable");
WCHAR pszRegCloned[] =              TEXT("Cloned");
WCHAR pszRegIsPortable[] =          TEXT("IsPortable");
WCHAR pszRegDockID[] =              TEXT("DockID");
WCHAR pszRegSerialNumber[] =        TEXT("SerialNumber");
WCHAR pszRegAcpiSerialNumber[] =    TEXT("AcpiSerialNumber");
WCHAR pszRegPropertyProviders[] =   TEXT("PropertyProviders");
WCHAR pszRegDocked[] =              TEXT("Docked");
WCHAR pszRegUndocked[] =            TEXT("Undocked");
WCHAR pszRegUnknown[] =             TEXT("Unknown");

//
// global mutex for synchronization
//
WCHAR  pszNamedMutex[] =            TEXT("System-HardwareProfiles-PLT");
HANDLE g_hMutex = NULL;

//
// global info for property sheet extensions
//
#define MAX_EXTENSION_PROVIDERS  32
HMODULE        hLibs[MAX_EXTENSION_PROVIDERS];
HPROPSHEETPAGE hPages[MAX_EXTENSION_PROVIDERS];
ULONG          ulNumPages = 0;
BOOL           bAdmin = FALSE;


static int HwProfileHelpIds[] = {
   IDD_HWP_PROFILES,        (IDH_HWPROFILE + IDD_HWP_PROFILES),
   IDD_HWP_PROPERTIES,      (IDH_HWPROFILE + IDD_HWP_PROPERTIES),
   IDD_HWP_COPY,            (IDH_HWPROFILE + IDD_HWP_COPY),
   IDD_HWP_RENAME,          (IDH_HWPROFILE + IDD_HWP_RENAME),
   IDD_HWP_DELETE,          (IDH_HWPROFILE + IDD_HWP_DELETE),
   IDD_HWP_ST_MULTIPLE,     (IDH_HWPROFILE + IDD_HWP_ST_MULTIPLE),
   IDD_HWP_WAITFOREVER,     (IDH_HWPROFILE + IDD_HWP_WAITFOREVER),
   IDD_HWP_WAITUSER,        (IDH_HWPROFILE + IDD_HWP_WAITUSER),
   IDD_HWP_SECONDS,         (IDH_HWPROFILE + IDD_HWP_SECONDS),
   IDD_HWP_SECSCROLL,       (IDH_HWPROFILE + IDD_HWP_SECSCROLL),
   IDD_HWP_COPYTO,          (IDH_HWPROFILE + IDD_HWP_COPYTO),
   IDD_HWP_COPYTO_CAPTION,  (IDH_HWPROFILE + IDD_HWP_COPYTO),
   IDD_HWP_COPYFROM,        (IDH_HWPROFILE + IDD_HWP_COPYFROM),
   IDD_HWP_ST_DOCKID,       (IDH_HWPROFILE + IDD_HWP_ST_DOCKID),
   IDD_HWP_ST_SERIALNUM,    (IDH_HWPROFILE + IDD_HWP_ST_SERIALNUM),
   IDD_HWP_DOCKID,          (IDH_HWPROFILE + IDD_HWP_DOCKID),
   IDD_HWP_SERIALNUM,       (IDH_HWPROFILE + IDD_HWP_SERIALNUM),
   IDD_HWP_PORTABLE,        (IDH_HWPROFILE + IDD_HWP_PORTABLE),
   IDD_HWP_ALIASABLE,       IDH_HWP_PROPERTIES_SELECTION_CHECKBOX,
   IDD_HWP_UNKNOWN,         (IDH_HWPROFILE + IDD_HWP_UNKNOWN),
   IDD_HWP_DOCKED,          (IDH_HWPROFILE + IDD_HWP_DOCKED),
   IDD_HWP_UNDOCKED,        (IDH_HWPROFILE + IDD_HWP_UNDOCKED),
   IDD_HWP_ST_PROFILE,      (IDH_HWPROFILE + IDD_HWP_ST_PROFILE),
   IDD_HWP_ORDERUP,         (IDH_HWPROFILE + IDD_HWP_ORDERUP),
   IDD_HWP_ORDERDOWN,       (IDH_HWPROFILE + IDD_HWP_ORDERDOWN),
   IDD_HWP_RENAMEFROM,      (IDH_HWPROFILE + IDD_HWP_RENAMEFROM),
   IDD_HWP_RENAMETO,        (IDH_HWPROFILE + IDD_HWP_RENAMETO),
   IDD_HWP_RENAMETO_CAPTION,(IDH_HWPROFILE + IDD_HWP_RENAMETO),
   IDD_HWP_WAITUSER_TEXT_1, (IDH_HWPROFILE + IDD_HWP_SECSCROLL),
   IDD_HWP_UNUSED_1,        -1,
   IDD_HWP_UNUSED_2,        -1,
   IDD_HWP_UNUSED_3,        -1,
   IDD_HWP_UNUSED_4,        -1,
   IDD_HWP_UNUSED_5,        -1,
   IDD_HWP_UNUSED_6,        -1,
   0, 0
};


/**-------------------------------------------------------------------------**/
INT_PTR
APIENTRY
HardwareProfilesDlg(
      HWND    hDlg,
      UINT    uMessage,
      WPARAM  wParam,
      LPARAM  lParam
      )

{
   BOOL           Status;
   ULONG          ulCurrentProfile, ulSelectedProfile,
                  ulWait, ulBufferIndex = 0;
   ULONG_PTR      ulIndex;
   LONG           lValue;
   TCHAR          szProfileName[MAX_PATH], szName[MAX_PATH];
   HWND           hList;
   int            nValue;
   HWPROFILE      HwSelectedProfile;
   LPNM_UPDOWN    pUDData;
   PHWPROF_INFO   pInfo;
   HICON          hIcon;


   switch (uMessage)
   {
      case WM_INITDIALOG:
         bAdmin = IsUserAdmin();

         //
         // attempt to claim the named mutex and lock other instances of
         // this dialog box out
         //
         g_hMutex = CreateMutex(NULL, TRUE, pszNamedMutex);

         if (g_hMutex == NULL) {

            if (GetLastError() == ERROR_ALREADY_EXISTS) {
               DisplayPrivateMessage(hDlg, HWP_ERROR_IN_USE);
            } else {
               DisplaySystemMessage(hDlg, GetLastError());
            }

            EndDialog(hDlg, FALSE);
            return FALSE;
         }

         //
         // load some global strings
         //
         LoadString(hInstance, HWP_CURRENT_TAG, pszCurrentTag, 64);
         LoadString(hInstance, HWP_UNAVAILABLE, pszUnavailable, 64);
         LoadString(hInstance, HWP_UNKNOWN_PROFILE,  pszUnknown, 64);
         LoadString(hInstance, HWP_DOCKED_PROFILE,   pszDocked, 64);
         LoadString(hInstance, HWP_UNDOCKED_PROFILE, pszUndocked, 64);
         LoadString(hInstance, HWP_ERROR_CAPTION, pszErrorCaption, MAX_PATH);
         LoadString(hInstance, HWP_DEF_FRIENDLYNAME, pszRegDefaultFriendlyName,
                  MAX_FRIENDLYNAME_LEN);

         //
         // fill the profiles listbox with all installed profiles,
         // this will also select the current profile
         //
         if (!FillProfileList(hDlg)) {
            EndDialog(hDlg, FALSE);
            return FALSE;
         }

         pInfo = (PHWPROF_INFO)GetWindowLongPtr(hDlg, DWLP_USER);

         //
         // place the icons on the up and down selection buttons
         //
         SendDlgItemMessage(
               hDlg, IDD_HWP_ORDERUP, BM_SETIMAGE, (WPARAM)IMAGE_ICON,
               (LPARAM)LoadIcon(hInstance, MAKEINTRESOURCE(UP_ICON)));

         SendDlgItemMessage(
               hDlg, IDD_HWP_ORDERDOWN, BM_SETIMAGE, (WPARAM)IMAGE_ICON,
               (LPARAM)LoadIcon(hInstance, MAKEINTRESOURCE(DOWN_ICON)));

         //
         // update button enable/disable states
         //
         UpdateOrderButtonState(hDlg);

         //
         // disable Delete for the current profile
         //
         EnableWindow(GetDlgItem(hDlg, IDD_HWP_DELETE), FALSE);

         //
         // disable copy if we're already at the max number of profiles
         // (including the pristine profile)
         //
         if ((pInfo->ulNumProfiles+1) > MAX_PROFILES) {
            EnableWindow(GetDlgItem(hDlg, IDD_HWP_COPY), FALSE);
         }

         //
         // initialize the user wait setting
         //
         SendDlgItemMessage(hDlg, IDD_HWP_SECSCROLL, UDM_SETBASE, 10, 0);
         SendDlgItemMessage(hDlg, IDD_HWP_SECSCROLL, UDM_SETRANGE, 0,
                  MAKELONG((SHORT)MAX_USER_WAIT, (SHORT)MIN_USER_WAIT));
         SendDlgItemMessage(hDlg, IDD_HWP_SECONDS, EM_LIMITTEXT, 3, 0L);

         GetUserWaitInterval(&ulWait);

         if (ulWait == 0xFFFFFFFF) {
             CheckRadioButton(hDlg, IDD_HWP_WAITFOREVER, IDD_HWP_WAITUSER,
                              IDD_HWP_WAITFOREVER);
            SendDlgItemMessage(hDlg, IDD_HWP_SECSCROLL, UDM_SETPOS, 0,
                               DEFAULT_USER_WAIT);
            EnableWindow(GetDlgItem(hDlg, IDD_HWP_SECONDS), FALSE);
            EnableWindow(GetDlgItem(hDlg, IDD_HWP_SECSCROLL), FALSE);
         }
         else {
            CheckRadioButton(hDlg, IDD_HWP_WAITFOREVER, IDD_HWP_WAITUSER,
                             IDD_HWP_WAITUSER);
            SendDlgItemMessage(hDlg, IDD_HWP_SECSCROLL, UDM_SETPOS, 0, ulWait);
         }

         //
         // Disable all actions if user not part of administrators local group
         //
         if (!bAdmin) {
             EnableWindow(GetDlgItem(hDlg, IDD_HWP_ORDERUP),    FALSE);
             EnableWindow(GetDlgItem(hDlg, IDD_HWP_PROPERTIES), FALSE);
             EnableWindow(GetDlgItem(hDlg, IDD_HWP_COPY),       FALSE);
             EnableWindow(GetDlgItem(hDlg, IDD_HWP_RENAME),     FALSE);
             EnableWindow(GetDlgItem(hDlg, IDD_HWP_DELETE),     FALSE);
             EnableWindow(GetDlgItem(hDlg, IDD_HWP_ORDERDOWN),  FALSE);
             EnableWindow(GetDlgItem(hDlg, IDD_HWP_WAITFOREVER),FALSE);
             EnableWindow(GetDlgItem(hDlg, IDD_HWP_WAITUSER),   FALSE);
             EnableWindow(GetDlgItem(hDlg, IDD_HWP_WAITUSER_TEXT_1), FALSE);
             EnableWindow(GetDlgItem(hDlg, IDD_HWP_SECONDS),    FALSE);
             EnableWindow(GetDlgItem(hDlg, IDD_HWP_SECSCROLL),  FALSE);
         }
         return 0;


      case WM_HELP:
         WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, HELP_FILE,
               HELP_WM_HELP, (DWORD_PTR)(LPTSTR)HwProfileHelpIds);
         break;

      case WM_CONTEXTMENU:
         WinHelp((HWND)wParam, HELP_FILE, HELP_CONTEXTMENU,
               (DWORD_PTR)(LPTSTR)HwProfileHelpIds);
         break;

      case WM_DESTROY:
          //
          // only free the buffer if we've already initialized
          //
          pInfo = (PHWPROF_INFO)GetWindowLongPtr(hDlg, DWLP_USER);

          if (pInfo) {
              LocalFree((HLOCAL)pInfo->pHwProfValues);
              LocalFree((HLOCAL)pInfo);

              hIcon = (HICON)SendDlgItemMessage(
                    hDlg, IDD_HWP_ORDERUP, BM_GETIMAGE, 0, 0);
              if (hIcon) {
                 DeleteObject(hIcon);
              }

              hIcon = (HICON)SendDlgItemMessage(
                    hDlg, IDD_HWP_ORDERDOWN, BM_GETIMAGE, 0, 0);
              if (hIcon) {
                 DeleteObject(hIcon);
              }
          }
          break;

      case WM_DEVICECHANGE:
          //
          // If a hardware profile change event takes place while the dialog is
          // up, just dismiss the dialog because things have changed.
          //
          if (wParam == DBT_CONFIGCHANGED) {
              EndDialog(hDlg, FALSE);
              return FALSE;
          }
          break;

      case WM_COMMAND:
      {
         switch (LOWORD(wParam))
         {
            case IDOK:
                if (bAdmin) {
                    //
                    // save the user wait interval in the registry
                    //
                    if (IsDlgButtonChecked(hDlg, IDD_HWP_WAITFOREVER)) {
                        ulWait = 0xFFFFFFFF;
                    }
                    else {
                        ulWait = GetDlgItemInt(hDlg, IDD_HWP_SECONDS,
                            &Status, FALSE);
                        if (!Status  ||  ulWait > MAX_USER_WAIT) {
                            TCHAR szCaption[MAX_PATH];
                            TCHAR szMsg[MAX_PATH];

                            LoadString(hInstance, HWP_ERROR_CAPTION, szCaption, MAX_PATH);
                            LoadString(hInstance, HWP_INVALID_WAIT, szMsg, MAX_PATH);
        
                            MessageBox(hDlg, szMsg, szCaption,
                                       MB_OK | MB_ICONEXCLAMATION);
        
                            SetFocus(GetDlgItem(hDlg, IDD_HWP_SECONDS));
        
                            return(TRUE);
                        }
                    }
                    SetUserWaitInterval(ulWait);
    
                    //
                    // flush the pending changes in profile buffer
                    //
                    hList = GetDlgItem(hDlg, IDD_HWP_PROFILES);
                    FlushProfileChanges(hDlg, hList);
                }
                EndDialog(hDlg, 0);
                break;

            case IDCANCEL:
                pInfo = (PHWPROF_INFO)GetWindowLongPtr(hDlg, DWLP_USER);

                if (pInfo) {
                    //
                    // If profile modifications have already been commited from
                    // within the Property Sheet, that's okay. But if accessing
                    // Properties caused any profiles to be created then they
                    // should be removed now since the user is effectively
                    // cancelling that creation now by cancelling from the main
                    // Hardware Profiles dialog.
                    //
                    if (bAdmin) {
                        RemoveNewProfiles(pInfo);
                    }
                }
                SetWindowLongPtr (hDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
                EndDialog(hDlg, 0);
                break;

            case IDD_HWP_ORDERUP:
               //
               // move selected profile "up" in preference order
               //
               hList = GetDlgItem(hDlg, IDD_HWP_PROFILES);

               ulIndex = SendMessage(hList, LB_GETCURSEL, 0, 0);
               if (ulIndex == LB_ERR) {
                  break;
               }

               pInfo = (PHWPROF_INFO)GetWindowLongPtr(hDlg, DWLP_USER);

               //
               // if we're not already at the top, swap preferences
               //
               if (ulIndex > 0) {
                  SwapPreferenceOrder(hDlg, hList, ulIndex, ulIndex-1);
                  UpdateOrderButtonState(hDlg);
                  PropSheet_Changed(GetParent(hDlg), hDlg);
               }
               break;


            case IDD_HWP_ORDERDOWN:
               //
               // move selected profile "down" in preference order
               //
               hList = GetDlgItem(hDlg, IDD_HWP_PROFILES);

               ulIndex = SendMessage(hList, LB_GETCURSEL, 0, 0);
               if (ulIndex == LB_ERR) {
                  break;
               }

               pInfo = (PHWPROF_INFO)GetWindowLongPtr(hDlg, DWLP_USER);

               //
               // if we're not already at the bottom, swap preferences
               //
               if (ulIndex < pInfo->ulNumProfiles-1) {
                  SwapPreferenceOrder(hDlg, hList, ulIndex, ulIndex+1);
                  UpdateOrderButtonState(hDlg);
                  PropSheet_Changed(GetParent(hDlg), hDlg);
               }
               break;


            case IDD_HWP_PROFILES:
               //
               // selection changed, enable/disable Delete button based
               // on whether it's the current config that is selected
               //

               if (bAdmin) {

                   if (HIWORD(wParam) == LBN_DBLCLK) {
                      SendMessage(hDlg, WM_COMMAND, MAKELONG(IDD_HWP_PROPERTIES,0), 0);
                   }
                   else if (HIWORD(wParam) == LBN_SELCHANGE) {

                      if (!GetCurrentProfile(&ulCurrentProfile)) {
                         break;
                      }

                      if ((ulIndex = SendMessage((HWND)lParam,
                            LB_GETCURSEL, 0, 0)) == LB_ERR) {
                         break;
                      }

                      if ((lValue = (LONG)SendMessage((HWND)lParam, LB_GETITEMDATA,
                            ulIndex, 0)) == LB_ERR) {
                         break;
                      }

                      if ((ULONG)lValue == ulCurrentProfile) {
                         EnableWindow(GetDlgItem(hDlg, IDD_HWP_DELETE), FALSE);
                      }
                      else {
                         EnableWindow(GetDlgItem(hDlg, IDD_HWP_DELETE), TRUE);
                      }


                      //
                      // update button enable/disable states
                      //
                      UpdateOrderButtonState(hDlg);
                   }
               }
               break;


            case IDD_HWP_WAITFOREVER:
               //
               // if user chooses wait forever, disable the seconds control
               //
               if (HIWORD(wParam) == BN_CLICKED) {
                  EnableWindow(GetDlgItem(hDlg, IDD_HWP_SECONDS), FALSE);
                  EnableWindow(GetDlgItem(hDlg, IDD_HWP_SECSCROLL), FALSE);
                  PropSheet_Changed(GetParent(hDlg), hDlg);
               }
               break;


            case IDD_HWP_WAITUSER:
               //
               // if user chooses a wait interval, reenable seconds control
               //
               if (HIWORD(wParam) == BN_CLICKED) {
                  EnableWindow(GetDlgItem(hDlg, IDD_HWP_SECONDS), TRUE);
                  EnableWindow(GetDlgItem(hDlg, IDD_HWP_SECSCROLL), TRUE);
                  PropSheet_Changed(GetParent(hDlg), hDlg);
               }
               break;


            case IDD_HWP_PROPERTIES:
               //
               // retrieve the profile buffer
               //
               pInfo = (PHWPROF_INFO)GetWindowLongPtr(hDlg, DWLP_USER);

               //
               // get the selected profile
               //
               hList = GetDlgItem(hDlg, IDD_HWP_PROFILES);

               ulIndex = SendMessage(hList, LB_GETCURSEL, 0, 0);
               if (ulIndex == LB_ERR) {
                  break;
               }

               //
               // find the profile entry in the buffer that matches the selection
               //
               hList = GetDlgItem(hDlg, IDD_HWP_PROFILES);
               ulSelectedProfile = (ULONG)SendMessage(hList, LB_GETITEMDATA, ulIndex, 0);

               while (ulBufferIndex < pInfo->ulNumProfiles) {
                  if (pInfo->pHwProfValues[ulBufferIndex].ulProfile == ulSelectedProfile) {
                     break;
                  }
                  ulBufferIndex++;
               }

               //
               // commit the changes for this profile before calling Properties
               //
               WriteProfileInfo(&pInfo->pHwProfValues[ulBufferIndex]);

               //
               // pass the HWPROF_VALUES struct for the selected profile
               // to the property sheet page when it's created. The
               // property sheet may update some of these fields and
               // may also commit changes for this profile to the registry.
               //
               pInfo->ulSelectedProfileIndex = ulBufferIndex;
               pInfo->ulSelectedProfile = (ULONG)SendMessage(hList, LB_GETITEMDATA,
                                                      ulIndex, 0);

               DisplayProperties(hDlg, pInfo);
               //DisplayProperties(hDlg, &pInfo->pHwProfValues[ulBufferIndex]);
               break;


            case IDD_HWP_COPY:               
               //
               // retrieve the profile buffer
               //
               pInfo = (PHWPROF_INFO)GetWindowLongPtr(hDlg, DWLP_USER);

               //
               // get the selected profile, this is the "From" selection
               //
               hList = GetDlgItem(hDlg, IDD_HWP_PROFILES);

               ulIndex = SendMessage(hList, LB_GETCURSEL, 0, 0);
               if (ulIndex == LB_ERR) {
                  break;
               }

               SendMessage(hList, LB_GETTEXT, ulIndex,
                     (LPARAM)(LPCTSTR)HwSelectedProfile.szFromFriendlyName);
               HwSelectedProfile.ulFromProfileID =
                     (ULONG)SendMessage(hList, LB_GETITEMDATA, ulIndex, 0);

               //
               // find the profile entry in the buffer that matches the selection
               //
               ulBufferIndex = 0;
               while (ulBufferIndex < pInfo->ulNumProfiles) {
                   if (pInfo->pHwProfValues[ulBufferIndex].ulProfile == HwSelectedProfile.ulFromProfileID) {
                       break;
                   }
                   ulBufferIndex++;
               }


               //
               // determine the next suggested name for the given profile
               //
               CreateHwProfileFriendlyName(hDlg,
                                           pInfo->pHwProfValues[ulBufferIndex].ulDockState,
                                           HwSelectedProfile.szToFriendlyName);

               //
               // pass selected profile info to copy profile dialog box
               //

               HwSelectedProfile.hParent = hDlg;

               if (!DialogBoxParam(hInstance,
                        MAKEINTRESOURCE(DLG_HWP_COPY), hDlg,
                        CopyProfileDlg,
                        (LPARAM)&HwSelectedProfile)) {
                  //
                  // if returns FALSE, either user canceled or no work
                  // required
                  //
                  break;
               }

               //
               // clone the profile in the in-memory profile buffer
               // and update the display
               //
               CopyHardwareProfile(
                        hDlg,
                        ulIndex,
                        HwSelectedProfile.ulFromProfileID,
                        HwSelectedProfile.szToFriendlyName);

               UpdateOrderButtonState(hDlg);
               PropSheet_Changed(GetParent(hDlg), hDlg);
               break;


            case IDD_HWP_RENAME:
               //
               // get the selected profile
               //
               hList = GetDlgItem(hDlg, IDD_HWP_PROFILES);

               ulIndex = SendMessage(hList, LB_GETCURSEL, 0, 0);
               if (ulIndex == LB_ERR) {
                  break;
               }

               SendMessage(hList, LB_GETTEXT, ulIndex,
                     (LPARAM)(LPCTSTR)HwSelectedProfile.szFromFriendlyName);
               HwSelectedProfile.ulFromProfileID =
                     (ULONG)SendMessage(hList, LB_GETITEMDATA, ulIndex, 0);

               //
               // strip off the current tag if it exists "(Current)"
               //
               GetCurrentProfile(&ulCurrentProfile);

               StripCurrentTag(
                     HwSelectedProfile.szFromFriendlyName,
                     HwSelectedProfile.ulFromProfileID,
                     ulCurrentProfile);

               // pass selected profile info to rename profile dialog box
               //

               HwSelectedProfile.hParent = hDlg;

               if (!DialogBoxParam(hInstance,
                        MAKEINTRESOURCE(DLG_HWP_RENAME), hDlg,
                        RenameProfileDlg,
                        (LPARAM)&HwSelectedProfile)) {
                  //
                  // if returns FASLE, either user canceled or no work
                  // required (i.e., user chose same name or zero-length
                  // name)
                  //
                  break;
               }

               //
               // rename the profile in the in-memory profile buffer
               // and update the display
               //
               RenameHardwareProfile(
                        hDlg,
                        ulIndex,
                        HwSelectedProfile.ulFromProfileID,
                        HwSelectedProfile.szToFriendlyName);

               PropSheet_Changed(GetParent(hDlg), hDlg);
               break;


            case IDD_HWP_DELETE: {

               TCHAR szCaption[MAX_PATH];
               TCHAR szMsg[MAX_PATH];
               TCHAR szMsg1[MAX_PATH];

               //
               // get the selected profile
               //
               hList = GetDlgItem(hDlg, IDD_HWP_PROFILES);

               ulIndex = SendMessage(hList, LB_GETCURSEL, 0, 0);
               if (ulIndex == LB_ERR) {
                  break;
               }

               //
               // confirm that user really wants to delete the profile
               // (the confirm message has a substitute symbol for
               // profile name)
               //
               SendMessage(hList, LB_GETTEXT, ulIndex,
                     (LPARAM)(LPCTSTR)szProfileName);

               LoadString(hInstance, HWP_CONFIRM_DELETE_CAP, szCaption, MAX_PATH);
               LoadString(hInstance, HWP_CONFIRM_DELETE, szMsg1, MAX_PATH);

               wsprintf(szMsg, szMsg1, szProfileName);

               if (MessageBox(hDlg, szMsg, szCaption,
                              MB_YESNO | MB_ICONQUESTION) == IDNO) {
                   break;
               }

               //
               // mark the profile as deleted in the in-memory buffer
               // and update the display
               //
               DeleteHardwareProfile(
                        hDlg,
                        ulIndex);

               UpdateOrderButtonState(hDlg);
               PropSheet_Changed(GetParent(hDlg), hDlg);
               break;
             }

             case IDD_HWP_SECONDS:

                if (HIWORD(wParam) == EN_UPDATE) {
                    PropSheet_Changed(GetParent(hDlg), hDlg);
                }
                break;

            default:
               return FALSE;
          }
          break;

       } // case WM_COMMAND...

       default:
          return FALSE;
          break;
    }

    return TRUE;

} // HardwareProfilesDlg



/**-------------------------------------------------------------------------**/
INT_PTR
APIENTRY
CopyProfileDlg(
      HWND    hDlg,
      UINT    uMessage,
      WPARAM  wParam,
      LPARAM  lParam
      )

{
   PHWPROFILE  pHwProfile;
   static HIMC himcOrg;


   switch (uMessage)
   {
      case WM_INITDIALOG:
         //
         // the profile info struct is passed in lparam, save in
         // Window word for thread-safe use in later messages
         //
         SetWindowLongPtr(hDlg, DWLP_USER, lParam);
         pHwProfile = (PHWPROFILE)lParam;

         //
         // initialize "To" and "From" fields
         //
         SendDlgItemMessage(hDlg, IDD_HWP_COPYTO, EM_LIMITTEXT,
               MAX_FRIENDLYNAME_LEN-1, 0L);
         SetDlgItemText(hDlg, IDD_HWP_COPYFROM, pHwProfile->szFromFriendlyName);
         SetDlgItemText(hDlg, IDD_HWP_COPYTO, pHwProfile->szToFriendlyName);
         SendDlgItemMessage(hDlg, IDD_HWP_COPYTO, EM_SETSEL, 0, -1);
         SetFocus(GetDlgItem(hDlg, IDD_HWP_COPYTO));
         //
         // Remove any association the window may have with an input context,
         // because we don't allow to use DBCS in H/W profile name.
         //
         himcOrg = ImmAssociateContext(GetDlgItem(hDlg, IDD_HWP_COPYTO), (HIMC)NULL);
         return FALSE;

      case WM_DEVICECHANGE:
         //
         // If a hardware profile change event takes place while the dialog is
         // up, just dismiss the dialog because things have changed.
         //
         if (wParam == DBT_CONFIGCHANGED) {
            EndDialog(hDlg, FALSE);
            return FALSE;
         }
         break;

      case WM_DESTROY:
         if (himcOrg)
           ImmAssociateContext(GetDlgItem(hDlg, IDD_HWP_COPYTO), himcOrg);
         return FALSE;

      case WM_HELP:
         WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, HELP_FILE,
               HELP_WM_HELP, (DWORD_PTR)(LPTSTR)HwProfileHelpIds);
         break;

      case WM_CONTEXTMENU:
         WinHelp((HWND)wParam, HELP_FILE, HELP_CONTEXTMENU,
                  (DWORD_PTR)(LPTSTR)HwProfileHelpIds);
         break;


      case WM_COMMAND:
      {
         switch (LOWORD(wParam))
         {
            case IDOK:
               pHwProfile = (PHWPROFILE)GetWindowLongPtr(hDlg, DWLP_USER);

               GetDlgItemText(hDlg, IDD_HWP_COPYTO,
                     pHwProfile->szToFriendlyName, MAX_FRIENDLYNAME_LEN);


               if (pHwProfile->szToFriendlyName == NULL ||
                        *pHwProfile->szToFriendlyName == '\0') {
                  //
                  // accept request to copy to zero-length string but
                  // do nothing (return FALSE from DialogBox call)
                  //
                  EndDialog(hDlg, FALSE);
                  return TRUE;
               }

               //
               // Check for duplicates
               //

               if (IsProfileNameInUse(pHwProfile->hParent,
                                      pHwProfile->szToFriendlyName)) {
                  //
                  // if name already used by a different profile (including
                  // the name of this profile), deny the request, but don't
                  // end the dialog box
                  //
                  DisplayPrivateMessage(hDlg, HWP_ERROR_PROFILE_IN_USE);
                  break;
               }

               //
               // Check for complex script names
               //
               if (S_OK == ScriptIsComplex(pHwProfile->szToFriendlyName, lstrlen(pHwProfile->szToFriendlyName), SIC_COMPLEX))
               {
                   DisplayPrivateMessage(hDlg, HWP_ERROR_COMPLEX_SCRIPT);
                   break;
               }                  

               //
               // otherwise, we'll accept the name
               //
               EndDialog(hDlg,TRUE);
               break;

            case IDCANCEL:
               EndDialog(hDlg,FALSE);
               break;

            default:
               return FALSE;
          }
          break;

       } // case WM_COMMAND...

       default:
          return FALSE;
          break;
    }

    return TRUE;

} // CopyProfileDlg



/**-------------------------------------------------------------------------**/
INT_PTR
APIENTRY
RenameProfileDlg(
      HWND    hDlg,
      UINT    uMessage,
      WPARAM  wParam,
      LPARAM  lParam
      )

{
   PHWPROFILE  pHwProfile;
   ULONG       ulReturn;
   static HIMC himcOrg;


   switch (uMessage)
   {
      case WM_INITDIALOG:
         //
         // the profile info struct is passed in lparam, save in
         // Window word for thread-safe use in later messages
         //
         SetWindowLongPtr(hDlg, DWLP_USER, lParam);
         pHwProfile = (PHWPROFILE)lParam;

         //
         // initialize "To" and "From" fields
         //
         SendDlgItemMessage(hDlg, IDD_HWP_RENAMETO, EM_LIMITTEXT,
               MAX_FRIENDLYNAME_LEN-1, 0L);
         SetDlgItemText(hDlg, IDD_HWP_RENAMEFROM, pHwProfile->szFromFriendlyName);
         SetDlgItemText(hDlg, IDD_HWP_RENAMETO, pHwProfile->szFromFriendlyName);
         SendDlgItemMessage(hDlg, IDD_HWP_RENAMETO, EM_SETSEL, 0, -1);
         SetFocus(GetDlgItem(hDlg, IDD_HWP_RENAMETO));
         //
         // Remove any association the window may have with an input context,
         // because we don't allow to use DBCS in H/W profile name.
         //
         himcOrg = ImmAssociateContext(GetDlgItem(hDlg, IDD_HWP_RENAMETO), (HIMC)NULL);
         return FALSE;


      case WM_DESTROY:
         if (himcOrg)
           ImmAssociateContext(GetDlgItem(hDlg, IDD_HWP_RENAMETO), himcOrg);
         return FALSE;

      case WM_DEVICECHANGE:
         //
         // If a hardware profile change event takes place while the dialog is
         // up, just dismiss the dialog because things have changed.
         //
         if (wParam == DBT_CONFIGCHANGED) {
            EndDialog(hDlg, FALSE);
            return FALSE;
         }
         break;


      case WM_HELP:
         WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, HELP_FILE,
               HELP_WM_HELP, (DWORD_PTR)(LPTSTR)HwProfileHelpIds);
         break;

      case WM_CONTEXTMENU:
         WinHelp((HWND)wParam, HELP_FILE, HELP_CONTEXTMENU,
                  (DWORD_PTR)(LPTSTR)HwProfileHelpIds);
         break;


      case WM_COMMAND:
      {
         switch (LOWORD(wParam))
         {
            case IDOK:
               pHwProfile = (PHWPROFILE)GetWindowLongPtr(hDlg, DWLP_USER);

               ulReturn = GetDlgItemText(hDlg, IDD_HWP_RENAMETO,
                     pHwProfile->szToFriendlyName, MAX_FRIENDLYNAME_LEN);


               if (pHwProfile->szToFriendlyName == NULL ||
                        *pHwProfile->szToFriendlyName == '\0') {
                  //
                  // accept request to copy to zero-length string but
                  // do nothing (return FALSE from DialogBox call)
                  //
                  EndDialog(hDlg, FALSE);
                  return TRUE;
               }

               if (lstrcmpi(pHwProfile->szToFriendlyName,
                        pHwProfile->szFromFriendlyName) == 0) {
                  //
                  // accept request to rename to same name but do
                  // nothing (return FALSE from DialogBox call)
                  //
                  EndDialog(hDlg, FALSE);
                  return TRUE;
               }

               //
               // Check for duplicates
               //

               if (IsProfileNameInUse(pHwProfile->hParent,
                                      pHwProfile->szToFriendlyName)) {
                  //
                  // if name already used by a different profile, deny
                  // the request, but don't end the dialog box
                  //
                  DisplayPrivateMessage(hDlg, HWP_ERROR_PROFILE_IN_USE);
                  break;
               }

               //
               // Check for complex script names
               //
               if (S_OK == ScriptIsComplex(pHwProfile->szToFriendlyName, lstrlen(pHwProfile->szToFriendlyName), SIC_COMPLEX))
               {
                   DisplayPrivateMessage(hDlg, HWP_ERROR_COMPLEX_SCRIPT);
                   break;
               }                  

               //
               // otherwise, we'll accept the name
               //
               EndDialog(hDlg,TRUE);
               break;

            case IDCANCEL:
               EndDialog(hDlg,FALSE);
               break;

            default:
               return FALSE;
          }
          break;

       } // case WM_COMMAND...

       default:
          return FALSE;
          break;
    }

    return TRUE;

} // RenameProfileDlg




/**-------------------------------------------------------------------------**/
BOOL
GetCurrentProfile(
      PULONG  pulProfile
      )
{
   WCHAR    RegStr[MAX_PATH];
   ULONG    ulSize;
   HKEY     hKey;


   //
   // open the IDConfigDB key
   //
   if (RegOpenKeyEx(
            HKEY_LOCAL_MACHINE, pszRegIDConfigDB, 0,
            KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) {

      DisplaySystemMessage(NULL, ERROR_REGISTRY_CORRUPT);
      return FALSE;
   }

   //
   // retrieve the CurrentConfig value
   //
   ulSize = sizeof(ULONG);
   if (RegQueryValueEx(
            hKey, pszRegCurrentConfig, NULL, NULL,
            (LPBYTE)pulProfile, &ulSize) != ERROR_SUCCESS) {

      RegCloseKey(hKey);
      DisplaySystemMessage(NULL, ERROR_REGISTRY_CORRUPT);
      return FALSE;
   }

   RegCloseKey(hKey);
   return TRUE;

} // GetCurrentProfile


/**-------------------------------------------------------------------------**/
BOOL
GetRegProfileCount(
      PULONG   pulProfiles
      )
{
   WCHAR    RegStr[MAX_PATH];
   HKEY     hKey;


   //
   // open the Known Docking States key
   //
   wsprintf(RegStr, TEXT("%s\\%s"),
            pszRegIDConfigDB,
            pszRegKnownDockingStates);

   if (RegOpenKeyEx(
            HKEY_LOCAL_MACHINE, RegStr, 0, KEY_READ,
            &hKey) != ERROR_SUCCESS) {

      *pulProfiles = 0;
      DisplaySystemMessage(NULL, ERROR_REGISTRY_CORRUPT);
      return FALSE;
   }

   //
   // find out the total number of profiles
   //
   if (RegQueryInfoKey(
            hKey, NULL, NULL, NULL, pulProfiles, NULL, NULL, NULL,
            NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {

      *pulProfiles = 0;
      RegCloseKey(hKey);
      DisplaySystemMessage(NULL, ERROR_REGISTRY_CORRUPT);
      return FALSE;
   }

   ASSERT(*pulProfiles > 0);  // The key for the pristine profile should be there, at least.
   *pulProfiles-= 1;          // Don't count the pristine in the number or working profiles.

   RegCloseKey(hKey);
   return TRUE;

} // GetRegProfileCount


/**-------------------------------------------------------------------------**/
BOOL
GetSelectedProfile(
      HWND     hCtl,
      PULONG   pulSelectedProfile
      )
{
    LONG lSelect;

    // THIS ISN"T BEING CALLED RIGHT NOW, IF IT STARTS GETTING CALLED,
    // THERE SHOULD BE LOGIC FOR REENABLING OR DISABLING THE DELETE
    // BUTTON BASED ON WHETHER THE FIRST INDEX IS THE CURRENT PROFILE
    // (ONLY APPLIES TO THE ERROR CASE WHERE THERE ARE NO CURRENT
    // SELECTIONS)

    lSelect = (LONG)SendMessage(hCtl, LB_GETCURSEL, 0, 0);

    if (lSelect != LB_ERR) {

      *pulSelectedProfile =
         (ULONG)SendMessage(hCtl, LB_GETITEMDATA, lSelect, 0);
    }
    else {
       //
       // no selections, assume first one
       //
       SendMessage(hCtl, LB_SETCURSEL, 0, 0);

       *pulSelectedProfile = (ULONG)SendMessage(hCtl, LB_GETITEMDATA, 0, 0);
    }

    return TRUE;

} // GetSelectedProfile



/**-------------------------------------------------------------------------**/
BOOL
FillProfileList(
      HWND  hDlg
      )

{
   HWND           hList;
   ULONG          ulCurrentProfile, ulCurrentIndex;
   ULONG          ulIndex=0, ulSize=0;
   ULONG          enumIndex = 0, ulProfileID=0;
   ULONG          ulCurrentDockingState = 0;
   ULONG          ulDockID = 0, ulSerialNumber=0;
   HKEY           hKey = NULL, hCfgKey = NULL;
   HKEY           hCurrent = NULL;
   WCHAR          RegStr[MAX_PATH], szName[MAX_PATH];
   ULONG          RegStatus = ERROR_SUCCESS;
   WCHAR          szFriendlyName[MAX_FRIENDLYNAME_LEN];
   WCHAR          szProfile[MAX_PROFILEID_LEN];
   PHWPROF_INFO   pInfo;
   LRESULT        lReturn;
   REGSAM         sam;


   //
   // retrieve a handle to the listbox window
   //
   hList = GetDlgItem(hDlg, IDD_HWP_PROFILES);

   //
   // retrieve the id of the current profile
   //
   if (!GetCurrentProfile(&ulCurrentProfile)) {
      DisplaySystemMessage(hDlg, ERROR_NOT_ENOUGH_MEMORY);
      return FALSE;
   }

   //
   // allocate a buffer for the main profile info struct
   //
   pInfo = (PHWPROF_INFO) LocalAlloc(LPTR, sizeof(HWPROF_INFO));

   if (pInfo == NULL) {
      DisplaySystemMessage(hDlg, ERROR_NOT_ENOUGH_MEMORY);
      return FALSE;
   }

   //
   // save the number of profiles currently in the registry
   //
   if (!GetRegProfileCount(&(pInfo->ulNumProfiles))) {
      LocalFree((HLOCAL)pInfo);
      return FALSE;
   }

   pInfo->ulActiveProfiles = pInfo->ulNumProfiles;

   //
   // Initialize the hardware detected portable flag
   //
   pInfo->bHwDetectedPortable = FALSE;

   //
   // allocate a buffer to hold all the profile values
   //
   pInfo->pHwProfValues = (PHWPROF_VALUES) LocalAlloc(LPTR, sizeof(HWPROF_VALUES) * pInfo->ulNumProfiles);

   if (pInfo->pHwProfValues == NULL) {
      LocalFree((HLOCAL)pInfo);
      return FALSE;
   }

   SetWindowLongPtr(hDlg, DWLP_USER, (LPARAM)pInfo);

   //
   // clear the listbox and turn redraw off
   //
   SendMessage(hList, LB_RESETCONTENT, 0, 0);
   SendMessage(hList, WM_SETREDRAW, (WPARAM)FALSE, 0);

   //
   // open the Hardware Profiles key
   //
   wsprintf(RegStr, TEXT("%s\\%s"),
            pszRegIDConfigDB,
            pszRegKnownDockingStates);

   if (RegOpenKeyEx(
            HKEY_LOCAL_MACHINE, RegStr, 0,
            KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
            &hKey) != ERROR_SUCCESS) {

      DisplaySystemMessage(hDlg, ERROR_REGISTRY_CORRUPT);
      LocalFree((HLOCAL)pInfo->pHwProfValues);
      LocalFree((HLOCAL)pInfo);
      return FALSE;
   }

   //
   // read the values for the name generating counters
   //
   ulSize = sizeof(DWORD);

   wsprintf(szName, pszRegUndocked);
   if (RegQueryValueEx(hKey, szName, NULL, NULL,
                       (LPBYTE)&pInfo->ulUndockedProfileNameCount, &ulSize)) {
       pInfo->ulUnknownProfileNameCount = 0;
   }

   wsprintf(szName, pszRegDocked);
   if (RegQueryValueEx(hKey, szName, NULL, NULL,
                       (LPBYTE)&pInfo->ulDockedProfileNameCount, &ulSize)) {
       pInfo->ulUnknownProfileNameCount = 0;
   }

   wsprintf(szName, pszRegUnknown);
   if (RegQueryValueEx(hKey, szName, NULL, NULL,
                       (LPBYTE)&pInfo->ulUnknownProfileNameCount, &ulSize)) {
       pInfo->ulUnknownProfileNameCount = 0;
   }

   //
   // pad the list box with a blank entry for each profile
   // (this facilitates adding the profiles in rank order
   //
   for (ulIndex = 0; ulIndex < pInfo->ulNumProfiles; ulIndex++) {
      SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR)TEXT(" "));
   }

   //
   // enumerate each of the existing hardware profiles
   //
   ulIndex = 0;
   enumIndex = 0;
   while (RegStatus != ERROR_NO_MORE_ITEMS) {

      //
      // enumerate the profile key
      //
      ulSize = MAX_PROFILEID_LEN;
      RegStatus = RegEnumKeyEx(
               hKey, enumIndex, szProfile, &ulSize, NULL, NULL, NULL, NULL);

      if (RegStatus == ERROR_SUCCESS) {
         //
         // open the enumerated profile key
         //
         if (bAdmin) {
             sam = KEY_QUERY_VALUE | KEY_SET_VALUE;
         } else {
             sam = KEY_QUERY_VALUE;
         }

         if (RegOpenKeyEx(
                  hKey, szProfile, 0, sam, &hCfgKey) != ERROR_SUCCESS) {

            RegCloseKey(hKey);
            LocalFree((HLOCAL)pInfo->pHwProfValues);
            LocalFree((HLOCAL)pInfo);
            if (bAdmin) {
                DisplaySystemMessage(hDlg, ERROR_REGISTRY_CORRUPT);
                return FALSE;
            }
            else {
                return TRUE;
            }
         }

         //
         // if this is the Pristine profile, ignore it, and move on to the next.
         //

         ulProfileID = _wtoi(szProfile);

         if (!ulProfileID) {
             enumIndex++;
             RegCloseKey(hCfgKey);
             continue;
         }

         //----------------------------------------------------------
         // retrieve the profile registry info, save in buffer
         //----------------------------------------------------------

         //
         // aliasable
         //
         ulSize = sizeof(DWORD);
         if (RegQueryValueEx(
             hCfgKey, pszRegAliasable, NULL, NULL,
             (LPBYTE)&pInfo->pHwProfValues[ulIndex].bAliasable,
             &ulSize) != ERROR_SUCCESS) {
             pInfo->pHwProfValues[ulIndex].bAliasable = TRUE;
         }

         //
         // cloned
         //
         ulSize = sizeof(DWORD);
         if (RegQueryValueEx(
             hCfgKey, pszRegCloned, NULL, NULL,
             (LPBYTE)&pInfo->pHwProfValues[ulIndex].bCloned,
             &ulSize) != ERROR_SUCCESS) {
             pInfo->pHwProfValues[ulIndex].bCloned = FALSE;
         }

         //
         // friendly name
         //
         ulSize = MAX_FRIENDLYNAME_LEN * sizeof(TCHAR);
         if (RegQueryValueEx(
                  hCfgKey, pszRegFriendlyName, NULL, NULL,
                  (LPBYTE)&pInfo->pHwProfValues[ulIndex].szFriendlyName,
                  &ulSize) != ERROR_SUCCESS) {

            //
            // if no FriendlyName then write out and use a default
            // value name (for compatibility with Win95)
            //
            if (bAdmin) {

                lstrcpy(pInfo->pHwProfValues[ulIndex].szFriendlyName,
                         pszRegDefaultFriendlyName);

                RegSetValueEx(
                         hCfgKey, pszRegFriendlyName, 0, REG_SZ,
                         (LPBYTE)pszRegDefaultFriendlyName,
                         (lstrlen(pszRegDefaultFriendlyName)+1) * sizeof(TCHAR));
            }
         }

         //
         // preference order ranking
         //
         ulSize = sizeof(ULONG);
         if (RegQueryValueEx(
                  hCfgKey, pszRegPreferenceOrder, NULL, NULL,
                  (LPBYTE)&pInfo->pHwProfValues[ulIndex].ulPreferenceOrder,
                  &ulSize) != ERROR_SUCCESS) {

            // FEATURE - rerank all profiles if this happens
         }

         //
         // dock state
         //
         ulSize = sizeof(ULONG);
         if (RegQueryValueEx(
                  hCfgKey, pszRegDockState, NULL, NULL,
                  (LPBYTE)&pInfo->pHwProfValues[ulIndex].ulDockState,
                  &ulSize) != ERROR_SUCCESS) {

            pInfo->pHwProfValues[ulIndex].ulDockState =
                     DOCKINFO_USER_SUPPLIED | DOCKINFO_DOCKED | DOCKINFO_UNDOCKED;
         }

         //
         // portable computer flag  - this is obsolete info, just save the current
         // setting if it exists and then delete it (might need the original
         // setting later)
         //
         ulSize = sizeof(ULONG);
         if (RegQueryValueEx(
                  hCfgKey, pszRegIsPortable, NULL, NULL,
                  (LPBYTE)&pInfo->pHwProfValues[ulIndex].bPortable,
                  &ulSize) != ERROR_SUCCESS) {

            pInfo->pHwProfValues[ulIndex].bPortable = FALSE;
         }

         RegDeleteValue(hCfgKey, pszRegIsPortable);

         pInfo->pHwProfValues[ulIndex].ulProfile = _wtoi(szProfile);
         pInfo->pHwProfValues[ulIndex].ulAction = HWP_NO_ACTION;
         RegCloseKey(hCfgKey);

         //
         // If this is the current profile, open the CurrentDockInfo key
         //
         if (pInfo->pHwProfValues[ulIndex].ulProfile == ulCurrentProfile) {

             wsprintf(RegStr, TEXT("%s\\%s"),
                      pszRegIDConfigDB,
                      pszRegCurrentDockInfo);
             
             if (RegOpenKeyEx(
                 HKEY_LOCAL_MACHINE, RegStr, 0,
                 KEY_QUERY_VALUE, &hCurrent) != ERROR_SUCCESS) {
                 
                 //
                 // Could not open the CurrentDockInfo key;
                 // Dock ID and Serial Number are unavailable
                 //
                 
                 pInfo->pHwProfValues[ulIndex].szDockID[0] = TEXT('\0');
                 pInfo->pHwProfValues[ulIndex].szSerialNumber[0] = TEXT('\0');

             } else {
                 
                 //
                 // Retrieve the hardware-detected dock state for the current profile
                 //
                 ulSize = sizeof(DWORD);
                 if ((RegQueryValueEx(
                     hCurrent, pszRegDockingState, NULL, NULL,
                     (LPBYTE)&ulCurrentDockingState,
                     &ulSize) == ERROR_SUCCESS) && 
                     ulCurrentDockingState &&
                     !((ulCurrentDockingState & DOCKINFO_DOCKED) &&
                       (ulCurrentDockingState & DOCKINFO_UNDOCKED))) {
                     //
                     // if the hardware-detected dockstate is present and known,
                     // override the user-supplied dockstate
                     //
                     pInfo->bHwDetectedPortable = TRUE;
                     pInfo->pHwProfValues[ulIndex].ulDockState = ulCurrentDockingState;
                 } else {
                     //
                     // keep the user-supplied dockstate
                     //
                     pInfo->bHwDetectedPortable = FALSE;
                     ulCurrentDockingState = pInfo->pHwProfValues[ulIndex].ulDockState;
                 }

                 if ((ulCurrentDockingState & DOCKINFO_UNDOCKED) &&
                     !(ulCurrentDockingState & DOCKINFO_DOCKED) &&
                     !(ulCurrentDockingState & DOCKINFO_USER_SUPPLIED)) {
                     //
                     // The hardware has detected an undocked state; set the global IsPortable flag
                     //
                     pInfo->bPortable = TRUE;

                 } else if ((ulCurrentDockingState & DOCKINFO_DOCKED) &&
                     !(ulCurrentDockingState & DOCKINFO_UNDOCKED) &&
                     !(ulCurrentDockingState & DOCKINFO_USER_SUPPLIED)) {                     
                     //
                     // The hardware has detected a docked state; set the global IsPortable flag
                     //
                     pInfo->bPortable = TRUE;
                     
                     //
                     // Serial Number and DockID are only valid for docked Profiles
                     //

                     //
                     // Retrieve the Serial Number for the current Profile.
                     //
                     // ("AcpiSerialNumber" is preferred because it is updated
                     // on Acpi dock events; "SerialNumber" is set only at boot
                     // time, from the BIOS, and may be inaccurate across Acpi
                     // dock transitions)
                     //
                     ulSize = MAX_SERIALNUM_LEN * sizeof(TCHAR);
                     if (RegQueryValueEx(
                         hCurrent, pszRegAcpiSerialNumber, NULL, NULL,
                         (LPBYTE)&pInfo->pHwProfValues[ulIndex].szSerialNumber,
                         &ulSize) != ERROR_SUCCESS) {
                         //
                         // No Acpi Serial Number, but we know the machine is
                         // docked (based on the DockState from the PnP BIOS
                         // info) so check for a PnP BIOS SerialNumber
                         //
                         ulSize = sizeof(DWORD);
                         if (RegQueryValueEx(
                             hCurrent, pszRegSerialNumber, NULL, NULL,
                             (LPBYTE)&ulSerialNumber,
                             &ulSize) != ERROR_SUCCESS) {                             
                             pInfo->pHwProfValues[ulIndex].szSerialNumber[0] = TEXT('\0');
                         } else {
                             wsprintf(pInfo->pHwProfValues[ulIndex].szSerialNumber,
                                      L"%X", 
                                      (ULONG)ulSerialNumber);
                         }
                     }
                     
                     //
                     // Retrieve the DockID for the current Profile, if available.
                     //
                     ulSize = sizeof(DWORD);
                     if (RegQueryValueEx(
                         hCurrent, pszRegDockID, NULL, NULL,
                         (LPBYTE)&ulDockID,
                         &ulSize) != ERROR_SUCCESS) {                 
                         pInfo->pHwProfValues[ulIndex].szDockID[0] = TEXT('\0');
                     } else {
                         wsprintf(pInfo->pHwProfValues[ulIndex].szDockID, 
                                  L"%X", 
                                  (ULONG)ulDockID);
                     }
                     
                 }
                 
             }
             
         } else {
             //
             // Serial Number and DockID are only valid for the current Profile
             //         
             pInfo->pHwProfValues[ulIndex].szSerialNumber[0] = TEXT('\0');
             pInfo->pHwProfValues[ulIndex].szDockID[0] = TEXT('\0');
         }
         
         //
         // delete the blank string in this spot, add the friendly name
         // (append current tag if necessary)
         //
         SendMessage(hList, LB_DELETESTRING,
                  pInfo->pHwProfValues[ulIndex].ulPreferenceOrder, 0);

         AppendCurrentTag(
                  szName,        // new fixed up name
                  pInfo->pHwProfValues[ulIndex].szFriendlyName,
                  pInfo->pHwProfValues[ulIndex].ulProfile,
                  ulCurrentProfile);

         lReturn = SendMessage(hList, LB_INSERTSTRING,
                  pInfo->pHwProfValues[ulIndex].ulPreferenceOrder,
                  (LPARAM)(LPCTSTR)szName);


         //
         // store the profile id along with the entry so we
         // can associate the string and the profile id later
         //
         SendMessage(hList, LB_SETITEMDATA,
            (WPARAM)lReturn, pInfo->pHwProfValues[ulIndex].ulProfile);

         //
         // if this is the current profile, save the index
         //
         if (pInfo->pHwProfValues[ulIndex].ulProfile == ulCurrentProfile) {
            ulCurrentIndex = pInfo->pHwProfValues[ulIndex].ulPreferenceOrder;
         }
      }

      ulIndex++;
      enumIndex++;

   } // while

   RegCloseKey(hKey);

   //---------------------------------------------------------------------
   // migrate portable information
   //---------------------------------------------------------------------
   
   if (bAdmin) {
       sam = KEY_READ | KEY_WRITE;
   } else {
       sam = KEY_READ;
   }

   if (RegOpenKeyEx(
       HKEY_LOCAL_MACHINE, pszRegIDConfigDB, 0,
       sam, &hKey) == ERROR_SUCCESS) {

       if (pInfo->bHwDetectedPortable) {
           if (bAdmin) {
               //
               // Set the global IsPortable setting as identified by the Hardware
               //
               RegSetValueEx(hKey, pszRegIsPortable, 0, REG_DWORD,
                             (LPBYTE)&pInfo->bPortable, sizeof(DWORD));
           }
           
       } else {
           //
           // Is there a global IsPortable setting?
           //
           ulSize = sizeof(DWORD);
           if (RegQueryValueEx(hKey, pszRegIsPortable, NULL, NULL,
                               (LPBYTE)&pInfo->bPortable, &ulSize) != ERROR_SUCCESS) {
               //
               // the global IsPortable flag isn't there, and the hardware did
               // not detect this as being a portable machine, so by default, it
               // is not.
               //
               pInfo->bPortable = FALSE;
           }
       }
       
       RegCloseKey(hKey);
   }

   SendMessage(hList, WM_SETREDRAW, (WPARAM)TRUE, 0);
   SendMessage(hList, LB_SETCURSEL, ulCurrentIndex, 0);

   return TRUE;

} // FillProfileList



/**-------------------------------------------------------------------------**/
BOOL
IsProfileNameInUse(
      HWND     hDlg,
      LPTSTR   pszFriendlyName
      )
{
   ULONG          ulBufferIndex=0;
   PHWPROF_INFO   pInfo=NULL;


   //
   // retrieve the profile buffer
   //
   pInfo = (PHWPROF_INFO)GetWindowLongPtr(hDlg, DWLP_USER);

   //
   // check each friendly name (that hasn't been deleted) for a
   // match (case-insensitive)
   //
   while (ulBufferIndex < pInfo->ulNumProfiles) {

      if (!(pInfo->pHwProfValues[ulBufferIndex].ulAction & HWP_DELETE)) {

         if (lstrcmpi(pInfo->pHwProfValues[ulBufferIndex].szFriendlyName,
                  pszFriendlyName) == 0) {
            return TRUE;      // match, name is in use
         }
      }
      ulBufferIndex++;
   }

   return FALSE;  // no match found, name not in use

} // IsProfileNameInUse



/**-------------------------------------------------------------------------**/
VOID
UnicodeToUL(LPWSTR pString, PULONG pulValue)
{
    ULONG i;

    *pulValue = 0;
    for (i = 0; i < 4; i++) {
        *pulValue *= 10;
        *pulValue += (pString[i] <= '9') ?
                     (pString[i] - '0') :
                     (pString[i]-'A'+10);
    }
} // UnicodeToUL



/**-------------------------------------------------------------------------**/
BOOL
CopyHardwareProfile(
      HWND   hDlg,
      ULONG_PTR ulIndex,
      ULONG  ulProfile,
      LPTSTR szNewFriendlyName
      )
{
   HWND           hList;
   ULONG          ulBufferIndex=0, ulNewBufferIndex=0;
   ULONG          ulNewProfile=0;
   ULONG_PTR      ulNewIndex=0;
   PHWPROF_INFO   pInfo=NULL;
   HLOCAL         hMem=NULL;
   WCHAR          szTemp[MAX_PATH];
   HKEY           hKey;
   LONG           RegStatus;


   //
   // retrieve the profile buffer
   //
   pInfo = (PHWPROF_INFO)GetWindowLongPtr(hDlg, DWLP_USER);

   //
   // retrieve a handle to the listbox window
   //
   hList = GetDlgItem(hDlg, IDD_HWP_PROFILES);

   //
   // find which entry in the buffer list matches this profile
   //
   while (ulBufferIndex < pInfo->ulNumProfiles) {
      if (pInfo->pHwProfValues[ulBufferIndex].ulProfile == ulProfile) {
         break;
      }
      ulBufferIndex++;
   }

   //
   // reallocate the profile buffer to hold another entry
   //
   pInfo->ulActiveProfiles++;
   pInfo->ulNumProfiles++;

   LocalUnlock(LocalHandle(pInfo->pHwProfValues));

   hMem = (PHWPROF_VALUES)LocalReAlloc(
               LocalHandle(pInfo->pHwProfValues),
               pInfo->ulNumProfiles * sizeof(HWPROF_VALUES),
               LMEM_MOVEABLE | LMEM_ZEROINIT);

   if (hMem == NULL) {
      DisplaySystemMessage(hDlg, ERROR_NOT_ENOUGH_MEMORY);
      return FALSE;
   }

   pInfo->pHwProfValues = (PHWPROF_VALUES)LocalLock(hMem);
   ulNewBufferIndex = pInfo->ulNumProfiles-1;

   //
   // find a free profile id to use
   //
   if (!GetFreeProfileID(pInfo, &ulNewProfile)) {
      return FALSE;
   }

   pInfo->pHwProfValues[ulNewBufferIndex].ulProfile = ulNewProfile;

   //
   // save the friendly name retrieved from the copy dialog box
   //
   lstrcpy(pInfo->pHwProfValues[ulNewBufferIndex].szFriendlyName,
            szNewFriendlyName);

   //
   // assume it's the last in the preference order (zero-based)
   //
   pInfo->pHwProfValues[ulNewBufferIndex].ulPreferenceOrder =
               pInfo->ulActiveProfiles - 1;

   //
   // copy the profile info from the selected profile to the new profile
   //
   pInfo->pHwProfValues[ulNewBufferIndex].ulDockState =
               pInfo->pHwProfValues[ulBufferIndex].ulDockState;

   //
   // new hardware profiles are (by default) not aliasable, unless no alias can
   // be copied for this profile.
   //
   pInfo->pHwProfValues[ulNewBufferIndex].bAliasable = FALSE;

   //
   // copied profiles are not clones of the pristine profile.
   //
   pInfo->pHwProfValues[ulNewBufferIndex].bCloned = FALSE;

   //pInfo->pHwProfValues[ulNewBufferIndex].bPortable =
   //            pInfo->pHwProfValues[ulBufferIndex].bPortable;

   lstrcpy(pInfo->pHwProfValues[ulNewBufferIndex].szDockID,
           pInfo->pHwProfValues[ulBufferIndex].szDockID);

   lstrcpy(pInfo->pHwProfValues[ulNewBufferIndex].szSerialNumber,
           pInfo->pHwProfValues[ulBufferIndex].szSerialNumber);

   //
   // save the original profile id this was copied from
   //
   pInfo->pHwProfValues[ulNewBufferIndex].ulCreatedFrom =
               GetOriginalProfile(pInfo, ulProfile, ulBufferIndex);

   //
   // increment the appropriate name-generating counter
   // (note that the counter is NOT symmetrically decreased whenever a profile
   // is deleted -- it increases over the lifetime of the system as new profiles
   // are created; this prevents us from assigning names that incrementally less
   // than names we have already assigned.)
   //
   AdjustProfileTypeCounter(pInfo,
                            pInfo->pHwProfValues[ulNewBufferIndex].ulDockState, 
                            TRUE);

   //
   // set the new profile in the listbox (at the end)
   //
   ulNewIndex = SendMessage(hList, LB_ADDSTRING, 0,
               (LPARAM)(LPTSTR)szNewFriendlyName);

   SendMessage(hList, LB_SETITEMDATA,
               (WPARAM)ulNewIndex,
               pInfo->pHwProfValues[ulNewBufferIndex].ulProfile);

   //
   // select the new profile
   //
   SendMessage(hList, LB_SETCURSEL, ulNewIndex, 0);

   //
   // mark the change
   //
   pInfo->pHwProfValues[ulNewBufferIndex].ulAction |= HWP_CREATE;

   //
   // disable copy if we're now at the max number of profiles
   //
   if ((pInfo->ulNumProfiles+1) >= MAX_PROFILES) {
      EnableWindow(GetDlgItem(hDlg, IDD_HWP_COPY), FALSE);
   }
   
   //
   // reenable delete since by definition the selection is not on the
   // current profile (whether it was before or not)
   //
   EnableWindow(GetDlgItem(hDlg, IDD_HWP_DELETE), TRUE);

   return TRUE;

} // CopyHardwareProfile



/**-------------------------------------------------------------------------**/
BOOL
RenameHardwareProfile(
      HWND   hDlg,
      ULONG_PTR ulIndex,
      ULONG  ulProfile,
      LPTSTR szNewFriendlyName
      )
{
   HWND           hList;
   ULONG          ulBufferIndex=0, ulCurrentProfile=0;
   PHWPROF_INFO   pInfo=NULL;
   WCHAR          szName[MAX_PATH];


   //
   // retrieve the profile buffer
   //
   pInfo = (PHWPROF_INFO)GetWindowLongPtr(hDlg, DWLP_USER);

   //
   // retrieve a handle to the listbox window
   //
   hList = GetDlgItem(hDlg, IDD_HWP_PROFILES);

   //
   // find the profile entry in the buffer that matches the selection
   //
   while (ulBufferIndex < pInfo->ulNumProfiles) {
      if (pInfo->pHwProfValues[ulBufferIndex].ulProfile == ulProfile) {
         break;
      }
      ulBufferIndex++;
   }

   //
   // set the new friendly name in the listbox
   //
   GetCurrentProfile(&ulCurrentProfile);
   AppendCurrentTag(szName, szNewFriendlyName, ulProfile, ulCurrentProfile);

   SendMessage(hList, LB_DELETESTRING, ulIndex, 0);
   SendMessage(hList, LB_INSERTSTRING, ulIndex, (LPARAM)(LPTSTR)szName);
   SendMessage(hList, LB_SETITEMDATA, ulIndex,
               pInfo->pHwProfValues[ulIndex].ulProfile);

   //
   // re-select the index (is this necessary?)
   //
   SendMessage(hList, LB_SETCURSEL, ulIndex, 0);

   //
   // mark the change
   //
   pInfo->pHwProfValues[ulBufferIndex].ulAction |= HWP_RENAME;
   lstrcpy(pInfo->pHwProfValues[ulBufferIndex].szFriendlyName,
            szNewFriendlyName);

   return TRUE;

} // RenameHardwareProfile



/**-------------------------------------------------------------------------**/
BOOL
DeleteHardwareProfile(
      HWND   hDlg,
      ULONG_PTR ulIndex
      )
{
   HWND           hList;
   ULONG          ulBufferIndex=0, ulProfile=0, ulCurrentProfile=0;
   PHWPROF_INFO   pInfo=NULL;


   //
   // retrieve the profile buffer
   //
   pInfo = (PHWPROF_INFO)GetWindowLongPtr(hDlg, DWLP_USER);

   //
   // retrieve a handle to the listbox window
   //
   hList = GetDlgItem(hDlg, IDD_HWP_PROFILES);

   //
   // find the profile entry in the buffer that matches the selection
   //
   ulProfile = (ULONG)SendMessage(hList, LB_GETITEMDATA, ulIndex, 0);

   while (ulBufferIndex < pInfo->ulNumProfiles) {
      if (pInfo->pHwProfValues[ulBufferIndex].ulProfile == ulProfile) {
         break;
      }
      ulBufferIndex++;
   }

   //
   // readjust all the rankings to be consecutive
   //
   DeleteRank(pInfo, pInfo->pHwProfValues[ulBufferIndex].ulPreferenceOrder);

   //
   // decrement the count of active profiles
   //
   pInfo->ulActiveProfiles--;

   //
   // delete the friendly name in the listbox
   //
   SendMessage(hList, LB_DELETESTRING, ulIndex, 0);

   //
   // re-select the following index (same position)
   //
   if (ulIndex >= pInfo->ulActiveProfiles) {
      ulIndex = pInfo->ulActiveProfiles-1;
   }

   SendMessage(hList, LB_SETCURSEL, ulIndex, 0);

   //
   // mark the change
   //
   pInfo->pHwProfValues[ulBufferIndex].ulAction |= HWP_DELETE;

   //
   // enable copy if less than max number of profiles
   //
   if (pInfo->ulNumProfiles < MAX_PROFILES) {
      EnableWindow(GetDlgItem(hDlg, IDD_HWP_COPY), TRUE);
   }

   //
   // find the profile entry in the buffer that matches the new selection
   //
   ulProfile = (ULONG)SendMessage(hList, LB_GETITEMDATA, ulIndex, 0);
   ulBufferIndex = 0;

   while (ulBufferIndex < pInfo->ulNumProfiles) {
      if (pInfo->pHwProfValues[ulBufferIndex].ulProfile == ulProfile) {
         break;
      }
      ulBufferIndex++;
   }

   GetCurrentProfile(&ulCurrentProfile);

   //
   // if the newly selected entry is the current profile, disable delete
   //
   if (pInfo->pHwProfValues[ulBufferIndex].ulProfile == ulCurrentProfile) {
      EnableWindow(GetDlgItem(hDlg, IDD_HWP_DELETE), FALSE);
   }


   return TRUE;

} // DeleteHardwareProfiles



/**-------------------------------------------------------------------------**/
BOOL
GetUserWaitInterval(
      PULONG   pulWait
      )
{
   ULONG    ulSize;
   HKEY     hKey;


   //
   // open the IDConfigDB key
   //
   if(RegOpenKeyEx(
            HKEY_LOCAL_MACHINE, pszRegIDConfigDB, 0,
            KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) {

      DisplaySystemMessage(NULL, ERROR_REGISTRY_CORRUPT);
      return FALSE;
   }

   //
   // retrieve the UserWaitInterval value
   //
   ulSize = sizeof(ULONG);
   if (RegQueryValueEx(
            hKey, pszRegUserWaitInterval, NULL, NULL,
            (LPBYTE)pulWait, &ulSize) != ERROR_SUCCESS) {
      *pulWait = DEFAULT_USER_WAIT;
   }

   RegCloseKey(hKey);
   return TRUE;

} // GetUserWaitInterval



/**-------------------------------------------------------------------------**/
BOOL
SetUserWaitInterval(
      ULONG   ulWait
      )
{
   HKEY     hKey;


   if (bAdmin) {
       //
       // open the IDConfigDB key
       //
       if(RegOpenKeyEx(
                HKEY_LOCAL_MACHINE, pszRegIDConfigDB, 0,
                KEY_SET_VALUE, &hKey) != ERROR_SUCCESS) {

          DisplaySystemMessage(NULL, ERROR_REGISTRY_CORRUPT);
          return FALSE;
       }

       //
       // set the UserWaitInterval value
       //
       if (RegSetValueEx(
                hKey, pszRegUserWaitInterval, 0, REG_DWORD,
                (LPBYTE)&ulWait, sizeof(ULONG)) != ERROR_SUCCESS) {
          RegCloseKey(hKey);
          return FALSE;
       }

       RegCloseKey(hKey);
   }

   return TRUE;

} // SetUserWaitInterval


/**-------------------------------------------------------------------------**/
BOOL
GetFreeProfileID(
      PHWPROF_INFO   pInfo,
      PULONG         pulProfile
      )

{
   ULONG    ulProfileID = 0, ulBufferIndex = 0;
   BOOL     bHit;


   //
   // find a profile id that isn't used
   //
   while (ulProfileID < MAX_PROFILES) {

      ulBufferIndex = 0;
      bHit = FALSE;

      while (ulBufferIndex < pInfo->ulNumProfiles) {

         if (ulProfileID == pInfo->pHwProfValues[ulBufferIndex].ulProfile) {
            bHit = TRUE;
            break;
         }

         ulBufferIndex++;
      }

      //
      // if I got all the way through the list without a hit, then this
      // profile id is free
      //
      if (!bHit) {
         *pulProfile = ulProfileID;
         return TRUE;
      }

      ulProfileID++;
   }

   *pulProfile = 0xFFFFFFFF;
   return FALSE;

} // GetFreeProfileID


/**-------------------------------------------------------------------------**/
ULONG
GetOriginalProfile(
      PHWPROF_INFO  pInfo,
      ULONG         ulProfile,
      ULONG         ulBufferIndex
      )
{
    ULONG   ulIndex, ulIndexCreatedFrom;

    //
    // if the specified profile is a newly created profile, then it is
    // by definition the first in the copy chain
    //
    if (!(pInfo->pHwProfValues[ulBufferIndex].ulAction & HWP_CREATE)) {
        return ulProfile;
    }

    ulIndex = ulBufferIndex;

    while (pInfo->pHwProfValues[ulIndex].ulAction & HWP_CREATE) {
        //
        // find which entry in the buffer list matches the "CopiedFrom" profile
        //
        ulIndexCreatedFrom = 0;

        while (ulIndexCreatedFrom < pInfo->ulNumProfiles) {
           if (pInfo->pHwProfValues[ulIndexCreatedFrom].ulProfile ==
               pInfo->pHwProfValues[ulIndex].ulCreatedFrom) {
              break;
           }
           ulIndexCreatedFrom++;
        }
        ulIndex = ulIndexCreatedFrom;
    }

    return pInfo->pHwProfValues[ulIndex].ulProfile;

} // GetOriginalProfile


/**-------------------------------------------------------------------------**/
BOOL
InsertRank(
      PHWPROF_INFO   pInfo,
      ULONG          ulRank
      )

{
   ULONG ulIndex;

   //
   // for inserting a rank and readjusting the other ranks, just
   // scan through the list and for any rank that is greater than
   // or equal to the inserted rank, increment the rank value
   //
   for (ulIndex = 0; ulIndex < pInfo->ulNumProfiles; ulIndex++) {
      //
      // if it's marked for delete, don't bother with it
      //
      if (!(pInfo->pHwProfValues[ulIndex].ulAction & HWP_DELETE)) {

         if (pInfo->pHwProfValues[ulIndex].ulPreferenceOrder >= ulRank) {
            pInfo->pHwProfValues[ulIndex].ulPreferenceOrder++;
            pInfo->pHwProfValues[ulIndex].ulAction |= HWP_REORDER;
         }
      }

   }

   return TRUE;

} // InsertRank



/**-------------------------------------------------------------------------**/
BOOL
DeleteRank(
      PHWPROF_INFO   pInfo,
      ULONG          ulRank
      )

{
   ULONG ulIndex;

   //
   // for deleting a rank and readjusting the other ranks, just
   // scan through the list and for any rank that is greater than
   // the deleted rank, subtract one from the rank value
   //
   for (ulIndex = 0; ulIndex < pInfo->ulNumProfiles; ulIndex++) {
      //
      // if it's marked for delete, don't bother with it
      //
      if (!(pInfo->pHwProfValues[ulIndex].ulAction & HWP_DELETE)) {

         if (pInfo->pHwProfValues[ulIndex].ulPreferenceOrder > ulRank) {
            pInfo->pHwProfValues[ulIndex].ulPreferenceOrder--;
            pInfo->pHwProfValues[ulIndex].ulAction |= HWP_REORDER;
         }
      }

   }

   return TRUE;

} // DeleteRank



/**-------------------------------------------------------------------------**/
BOOL
FlushProfileChanges(
      HWND hDlg,
      HWND hList
      )
{
   ULONG    ulIndex=0;
   HKEY     hKey = NULL, hDestKey = NULL, hSrcKey = NULL, hHwProf = NULL;
   WCHAR    RegStr[MAX_PATH];
   PHWPROF_INFO   pInfo=NULL;


   //
   // retrieve the profile buffer
   //
   pInfo = (PHWPROF_INFO)GetWindowLongPtr(hDlg, DWLP_USER);


   //
   // First pass, process the changes for each profile (except delete)
   //
   while (ulIndex < pInfo->ulNumProfiles) {

      //
      // were any changes made to this profile?
      //
      if (pInfo->pHwProfValues[ulIndex].ulAction == HWP_NO_ACTION) {
         goto NextProfile;
      }

      //
      // save deleting for the second pass
      //
      if (pInfo->pHwProfValues[ulIndex].ulAction & HWP_DELETE) {
         goto NextProfile;
      }

      //
      // commit the changes for this profile
      //
      WriteProfileInfo(&pInfo->pHwProfValues[ulIndex]);

      NextProfile:
         ulIndex++;
   }



   //
   // Second pass, process the delete requests
   //
   ulIndex = 0;
   while (ulIndex < pInfo->ulNumProfiles) {

      if (pInfo->pHwProfValues[ulIndex].ulAction & HWP_DELETE) {
         //
         // we only need to delete the key if it exists (if this
         // isn't a delete of a profile that was also just created)
         //
         if (!(pInfo->pHwProfValues[ulIndex].ulAction & HWP_CREATE)) {

            wsprintf(RegStr, TEXT("%s\\%s"),
                     pszRegIDConfigDB,
                     pszRegKnownDockingStates);

            if (RegOpenKeyEx(
                     HKEY_LOCAL_MACHINE, RegStr, 0, KEY_ALL_ACCESS,
                     &hKey) != ERROR_SUCCESS) {

               DisplaySystemMessage(hDlg, ERROR_REGISTRY_CORRUPT);
               return FALSE;
            }

            wsprintf(RegStr, TEXT("%04u"),
                     pInfo->pHwProfValues[ulIndex].ulProfile);

            RegDeleteKey(hKey, RegStr);
            RegCloseKey(hKey);

            //
            // also delete the profile specific enum tree
            //
            DeleteProfileDependentTree(pInfo->pHwProfValues[ulIndex].ulProfile);

            //
            // also delete the aliases to this profile
            //
            DeleteAliasEntries(pInfo->pHwProfValues[ulIndex].ulProfile);
            
         } else {
             //
             // decrement the name-generating counter for profiles we are
             // deleting that we created this session (only if registry keys
             // were never created for this new profile)
             //
             AdjustProfileTypeCounter(pInfo,
                                      pInfo->pHwProfValues[ulIndex].ulDockState, 
                                      FALSE);
         }
         
      }
      ulIndex++;
   }

   //
   // commit global settings
   //
   if(RegOpenKeyEx(
            HKEY_LOCAL_MACHINE, pszRegIDConfigDB, 0,
            KEY_SET_VALUE, &hKey) == ERROR_SUCCESS) {

       RegSetValueEx(hKey, pszRegIsPortable, 0, REG_DWORD,
                (LPBYTE)&pInfo->bPortable, sizeof(DWORD));

       if (RegOpenKeyEx(
                 hKey, pszRegKnownDockingStates, 0,
                 KEY_SET_VALUE, &hHwProf) == ERROR_SUCCESS) {

           if (pInfo->ulUndockedProfileNameCount > 0) {
               RegSetValueEx(hHwProf, pszRegUndocked, 0, REG_DWORD,
                             (LPBYTE)&pInfo->ulUndockedProfileNameCount, sizeof(ULONG));
           } else {
               RegDeleteValue(hHwProf, pszRegUndocked);
           }
           
           if (pInfo->ulDockedProfileNameCount > 0) {
               RegSetValueEx(hHwProf, pszRegDocked, 0, REG_DWORD,
                             (LPBYTE)&pInfo->ulDockedProfileNameCount, sizeof(ULONG));
           } else {
               RegDeleteValue(hHwProf, pszRegDocked);
           }
           
           if (pInfo->ulUnknownProfileNameCount > 0) {
               RegSetValueEx(hHwProf, pszRegUnknown, 0, REG_DWORD,
                             (LPBYTE)&pInfo->ulUnknownProfileNameCount, sizeof(ULONG));
           } else {
               RegDeleteValue(hHwProf, pszRegUnknown);
           }
           
           RegCloseKey(hHwProf);
       }
       RegCloseKey(hKey);
   }

   return TRUE;

} // FlushProfileChanges



/**-------------------------------------------------------------------------**/
BOOL
WriteProfileInfo(
   PHWPROF_VALUES pProfValues
   )
{
   HKEY     hKey = NULL, hDestKey = NULL, hSrcKey = NULL;
   WCHAR    RegStr[MAX_PATH];
   UUID     NewGuid;
   LPTSTR   UuidString;
   TCHAR    szGuid[MAX_GUID_STRING_LEN];

   if (pProfValues->ulAction & HWP_DELETE) {
      return TRUE;      // skip it
   }

   //
   // form the registry key string
   //
   wsprintf(RegStr, TEXT("%s\\%s\\%04u"),
            pszRegIDConfigDB,
            pszRegKnownDockingStates,
            pProfValues->ulProfile);

   //
   // create the profile key if it's a new profile. Don't
   // worry about security because the profile subkey always
   // inherits the security of the parent Hardware Profiles key.
   //
   if (pProfValues->ulAction & HWP_CREATE) {

      if (RegCreateKeyEx(
               HKEY_LOCAL_MACHINE, RegStr, 0, NULL,
               REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
               NULL, &hKey, NULL) != ERROR_SUCCESS) {
         return FALSE;
      }

      //
      // create a HwProfileGuid if its a new profile
      //
      if ((UuidCreate(&NewGuid) == RPC_S_OK) &&    
          (UuidToString(&NewGuid, &UuidString) == RPC_S_OK)) {                    

          //
          // put curly braces around the guid
          //
          lstrcpy(szGuid, TEXT("{"));
          lstrcat(szGuid, UuidString);
          lstrcat(szGuid, TEXT("}"));
          RpcStringFree(&UuidString);
          
          //
          // save the new HwProfileGuid in the registry
          //
          RegSetValueEx(
              hKey, pszRegHwProfileGuid, 0, REG_SZ,
              (LPBYTE)szGuid,
              (lstrlen(szGuid)+1) * sizeof(TCHAR) );         
      }
      
   } else {
      //
      // if not a create, just open the existing key
      //
      if (RegOpenKeyEx(
               HKEY_LOCAL_MACHINE, RegStr, 0, KEY_SET_VALUE,
               &hKey) != ERROR_SUCCESS) {
         return FALSE;
      }
   }

   //
   // update preference order if modified
   //
   if ((pProfValues->ulAction & HWP_REORDER) ||
            (pProfValues->ulAction & HWP_CREATE)) {

      RegSetValueEx(
            hKey, pszRegPreferenceOrder, 0, REG_DWORD,
            (LPBYTE)&pProfValues->ulPreferenceOrder, sizeof(ULONG));

      pProfValues->ulAction &= ~HWP_REORDER;    // clear action
   }

   //
   // update friendly name if modified
   //
   if ((pProfValues->ulAction & HWP_RENAME) ||
            (pProfValues->ulAction & HWP_CREATE)) {

      RegSetValueEx(
            hKey, pszRegFriendlyName, 0, REG_SZ,
            (LPBYTE)pProfValues->szFriendlyName,
            (lstrlen(pProfValues->szFriendlyName)+1) * sizeof(TCHAR));

      pProfValues->ulAction &= ~HWP_RENAME;     // clear action
   }

   //
   // update property values if modified
   //
   if ((pProfValues->ulAction & HWP_PROPERTIES) ||
            (pProfValues->ulAction & HWP_CREATE)) {

      RegSetValueEx(
            hKey, pszRegDockState, 0, REG_DWORD,
            (LPBYTE)&pProfValues->ulDockState, sizeof(ULONG));

      RegSetValueEx(
            hKey, pszRegAliasable, 0, REG_DWORD,
            (LPBYTE)&pProfValues->bAliasable, sizeof(DWORD));

      pProfValues->ulAction &= ~HWP_PROPERTIES;    // clear action
   }


   if (pProfValues->ulAction & HWP_CREATE) {
      //
      // copy the profile enum info. Don't worry about security on
      // this createkey because the profile key always inherits the
      // security of the parent Hardware Profiles key.
      //
      wsprintf(RegStr, TEXT("%s\\%04u"),
            pszRegHwProfiles,
            pProfValues->ulProfile);

      RegCreateKeyEx(
            HKEY_LOCAL_MACHINE, RegStr, 0, NULL, REG_OPTION_NON_VOLATILE,
            KEY_ALL_ACCESS, NULL, &hDestKey, NULL);

      wsprintf(RegStr, TEXT("%s\\%04u"),
            pszRegHwProfiles,
            pProfValues->ulCreatedFrom);

      RegOpenKeyEx(
            HKEY_LOCAL_MACHINE, RegStr, 0, KEY_ALL_ACCESS, &hSrcKey);

      CopyRegistryNode(hSrcKey, hDestKey);

      //
      // copy all aliases for the source profile to the destination as well.
      //
      if (CopyAliasEntries(pProfValues->ulCreatedFrom, 
                           pProfValues->ulProfile) == FALSE) {

          //
          // We didn't actually copy any aliases, so make sure this profile gets
          // marked as "Aliasable" so that it shows up as a profile option at
          // boot time.
          //
          pProfValues->bAliasable = TRUE;
          RegSetValueEx(
              hKey, pszRegAliasable, 0, REG_DWORD,
              (LPBYTE)&pProfValues->bAliasable, sizeof(DWORD));
      }

      if (hDestKey != NULL) RegCloseKey(hDestKey);
      if (hSrcKey != NULL) RegCloseKey(hSrcKey);

      pProfValues->ulAction &= ~HWP_CREATE;     // clear action
      pProfValues->ulAction |= HWP_NEWPROFILE;  // created during this session
   }

   RegCloseKey(hKey);

   return TRUE;

} // WriteProfileInfo


/**-------------------------------------------------------------------------**/
BOOL
RemoveNewProfiles(
      PHWPROF_INFO   pInfo
      )
{
   ULONG    ulIndex=0;
   HKEY     hKey = NULL;
   WCHAR    RegStr[MAX_PATH];


   //
   // check each profile for any HWP_NEWPROFILE flags
   //
   while (ulIndex < pInfo->ulNumProfiles) {

      if (pInfo->pHwProfValues[ulIndex].ulAction & HWP_NEWPROFILE) {

         wsprintf(RegStr, TEXT("%s\\%s"),
                  pszRegIDConfigDB,
                  pszRegKnownDockingStates);

         if (RegOpenKeyEx(
                  HKEY_LOCAL_MACHINE, RegStr, 0, KEY_ALL_ACCESS,
                  &hKey) == ERROR_SUCCESS) {

            wsprintf(RegStr, TEXT("%04u"),
                     pInfo->pHwProfValues[ulIndex].ulProfile);

            RegDeleteKey(hKey, RegStr);
            RegCloseKey(hKey);
         }

         //
         // also delete the profile specific enum tree
         //
         DeleteProfileDependentTree(pInfo->pHwProfValues[ulIndex].ulProfile);

         //
         // also delete aliases to this profile
         //
         DeleteAliasEntries(pInfo->pHwProfValues[ulIndex].ulProfile);

         //
         // decrement the appropriate name-generating counter
         // (note that the counter is only deleted for new profiles that are
         // never fully commited; we don't make an attempt to decerement the
         // counter when any profile is deleted, else we may get into a case
         // where we assign a name to a new profile that in incrementally less
         // than a name we had previously assigned.)
         //
         AdjustProfileTypeCounter(pInfo,
                                  pInfo->pHwProfValues[ulIndex].ulDockState, 
                                  FALSE);
      }
      ulIndex++;
   }

   return TRUE;

} // RemoveNewProfiles



/**-------------------------------------------------------------------------**/
BOOL
SwapPreferenceOrder(
      HWND  hDlg,
      HWND  hList,
      ULONG_PTR ulIndex1,
      ULONG_PTR ulIndex2
      )

{
   ULONG    ulProfile1=0, ulProfile2=0;
   ULONG    ulBufferIndex1=0, ulBufferIndex2=0;
   WCHAR    szFriendlyName1[MAX_FRIENDLYNAME_LEN];
   WCHAR    szFriendlyName2[MAX_FRIENDLYNAME_LEN];
   ULONG    ulTemp=0;
   PHWPROF_INFO   pInfo=NULL;


   //
   // retrieve the profile buffer
   //
   pInfo = (PHWPROF_INFO)GetWindowLongPtr(hDlg, DWLP_USER);

   //
   // retrieve the profile id for the two selected profile entries
   //
   ulProfile1 = (ULONG)SendMessage(hList, LB_GETITEMDATA, ulIndex1, 0);
   ulProfile2 = (ULONG)SendMessage(hList, LB_GETITEMDATA, ulIndex2, 0);

   //
   // find the profile entry in the buffer that matches these selections
   //
   while (ulBufferIndex1 < pInfo->ulNumProfiles) {
      if (pInfo->pHwProfValues[ulBufferIndex1].ulProfile == ulProfile1) {
         break;
      }
      ulBufferIndex1++;
   }

   while (ulBufferIndex2 < pInfo->ulNumProfiles) {
      if (pInfo->pHwProfValues[ulBufferIndex2].ulProfile == ulProfile2) {
         break;
      }
      ulBufferIndex2++;
   }

   //
   // swap the order values of the profiles in the in-memory buffer
   //
   ulTemp = pInfo->pHwProfValues[ulBufferIndex1].ulPreferenceOrder;
   pInfo->pHwProfValues[ulBufferIndex1].ulPreferenceOrder =
            pInfo->pHwProfValues[ulBufferIndex2].ulPreferenceOrder;
   pInfo->pHwProfValues[ulBufferIndex2].ulPreferenceOrder = ulTemp;

   //
   // mark both profiles as having been reordered
   //
   pInfo->pHwProfValues[ulBufferIndex1].ulAction |= HWP_REORDER;
   pInfo->pHwProfValues[ulBufferIndex2].ulAction |= HWP_REORDER;

   //
   // swap the positions in the list box
   //
   SendMessage(hList, LB_GETTEXT, ulIndex1, (LPARAM)(LPTSTR)szFriendlyName1);
   SendMessage(hList, LB_GETTEXT, ulIndex2, (LPARAM)(LPTSTR)szFriendlyName2);

   SendMessage(hList, LB_DELETESTRING, ulIndex1, 0);
   SendMessage(hList, LB_INSERTSTRING, ulIndex1,
         (LPARAM)(LPTSTR)szFriendlyName2);

   SendMessage(hList, LB_DELETESTRING, ulIndex2, 0);
   SendMessage(hList, LB_INSERTSTRING, ulIndex2,
         (LPARAM)(LPTSTR)szFriendlyName1);

   SendMessage(hList, LB_SETITEMDATA, ulIndex1, ulProfile2);
   SendMessage(hList, LB_SETITEMDATA, ulIndex2, ulProfile1);

   //
   // finally, select the second index (the second index is the rank
   // position we're moving to)
   //
   SendMessage(hList, LB_SETCURSEL, ulIndex2, 0);

   return TRUE;

} // SwapPreferenceOrder


/**-------------------------------------------------------------------------**/
BOOL
DeleteProfileDependentTree(
      ULONG ulProfile
      )
{
   TCHAR szProfile[5], szKey[MAX_PATH];
   LONG  RegStatus = ERROR_SUCCESS;
   HKEY  hHwProfKey, hCfgKey;
   ULONG ulIndex = 0, ulSize = 0;


   //
   // form the registry key string
   //
   if (RegOpenKeyEx(
            HKEY_LOCAL_MACHINE, pszRegHwProfiles, 0, KEY_ALL_ACCESS,
            &hHwProfKey) != ERROR_SUCCESS) {

      DisplaySystemMessage(NULL, ERROR_REGISTRY_CORRUPT);
      return FALSE;
   }

   wsprintf(szProfile, TEXT("%04u"),
            ulProfile);

   DeleteRegistryNode(hHwProfKey, szProfile);
   RegCloseKey(hHwProfKey);

   return TRUE;

} // DeleteProfileDependentTree

/**-------------------------------------------------------------------------**/

BOOL
CopyAliasEntries(
    ULONG ulSrcProfile,
    ULONG ulDestProfile
    )
{
   BOOL bNewAliasCreated = FALSE;

   bNewAliasCreated |= CopyAliasEntryType(ulSrcProfile, ulDestProfile, pszRegAlias);
   bNewAliasCreated |= CopyAliasEntryType(ulSrcProfile, ulDestProfile, pszRegAcpiAlias);    

   return bNewAliasCreated;

} // CopyAliasEntries

/**-------------------------------------------------------------------------**/

BOOL
DeleteAliasEntries(
    ULONG ulProfile    
    )
{
   BOOL bResult = TRUE;

   bResult |= DeleteAliasEntryType(ulProfile, pszRegAlias);
   bResult |= DeleteAliasEntryType(ulProfile, pszRegAcpiAlias);    

   return bResult;

} // CopyAliasEntries

/**-------------------------------------------------------------------------**/

BOOL
CopyAliasEntryType(
    ULONG  ulSrcProfile,
    ULONG  ulDestProfile,
    LPWSTR szSubKeyName
    )
{

   LONG   RegStatus = ERROR_SUCCESS;
   HKEY   hAliasRoot, hSrcKey, hDestKey;
   WCHAR  RegStr[MAX_PATH];
   WCHAR  szString[MAX_PATH];
   ULONG  ulAliasProfileNumber, ulNewAlias, ulSize, i;
   BOOL   bNewAliasCreated=FALSE;

   //
   // check all aliases under the "Alias" key
   //
   wsprintf(RegStr, TEXT("%s\\%s"),
            pszRegIDConfigDB,
            szSubKeyName);

   RegStatus = RegOpenKeyEx(
            HKEY_LOCAL_MACHINE, RegStr, 0, KEY_ALL_ACCESS,
            &hAliasRoot);
   if (RegStatus == ERROR_FILE_NOT_FOUND) {
      //
      // No Alias subkey, this could be ok if we have the other type.
      //
      RegStatus = ERROR_SUCCESS;
      return bNewAliasCreated;
   } else if (RegStatus != ERROR_SUCCESS) {
      //
      // Some other registry error occurred.
      //
      DisplaySystemMessage(NULL, ERROR_REGISTRY_CORRUPT);
      return FALSE;
   } else {
       //
       // enumerate all Alias subkeys
       //
       for (i=0; RegStatus == ERROR_SUCCESS; i++) {

           ulSize = MAX_PATH;
           
           RegStatus = RegEnumKey(hAliasRoot, i, szString, ulSize);

           if (RegStatus == ERROR_SUCCESS) {

               if (RegOpenKey(hAliasRoot, szString, &hSrcKey) == ERROR_SUCCESS) {
             
                   ulSize = sizeof(ULONG);
                   
                   if (RegQueryValueEx(
                       hSrcKey, pszRegProfileNumber, NULL, NULL,
                       (LPBYTE)&ulAliasProfileNumber, 
                       &ulSize) == ERROR_SUCCESS) {
                 
                       //
                       // Check if we need to copy this one
                       //
                       if (ulSrcProfile == ulAliasProfileNumber) {

                           //
                           // Find an unused Alias subkey name
                           //          
                           ulNewAlias = 0;
                           while (ulNewAlias < MAX_ALIASES) {
                               ulNewAlias++;
                               wsprintf(RegStr, TEXT("%s\\%s\\%04u"),
                                        pszRegIDConfigDB,
                                        szSubKeyName,
                                        ulNewAlias);              
                               RegStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
                                                        RegStr, 0, KEY_ALL_ACCESS, 
                                                        &hDestKey);
                               
                               if (RegStatus == ERROR_SUCCESS) {
                                   RegCloseKey(hDestKey);
                                   continue;
                               } else if (RegStatus == ERROR_FILE_NOT_FOUND) {
                                   RegStatus = ERROR_SUCCESS;
                                   break;
                               } else {
                                   break;
                               }
                           }
                     
                           if ((RegStatus != ERROR_SUCCESS) || (ulNewAlias >= MAX_ALIASES)) {
                               RegCloseKey(hSrcKey);
                               break;
                           }

                           wsprintf(RegStr, TEXT("%s\\%s\\%04u"),
                                    pszRegIDConfigDB,
                                    szSubKeyName,
                                    ulNewAlias);
                           
                           RegCreateKeyEx(
                               HKEY_LOCAL_MACHINE, RegStr, 0, NULL, REG_OPTION_NON_VOLATILE,
                               KEY_ALL_ACCESS, NULL, &hDestKey, NULL);
                        
                           //
                           // copy this alias entry 
                           //   
                           CopyRegistryNode(hSrcKey, hDestKey);
                           
                           RegSetValueEx(
                               hDestKey, pszRegProfileNumber, 0, REG_DWORD,
                               (LPBYTE)&ulDestProfile, sizeof(ULONG));
                           
                           bNewAliasCreated = TRUE;
                           if (ulNewAlias < (ULONG)_wtoi(szString)) {
                               // kick the enumeration index up one, else we'll
                               // find the key we just copied again.
                               i++;
                           }
                           
                           if (hDestKey != NULL) {
                               RegCloseKey(hDestKey);
                               hDestKey = NULL;
                           }                           
                       }
                   }
                   if (hSrcKey != NULL) {
                       RegCloseKey(hSrcKey);
                       hSrcKey = NULL;
                   }
               }
           }
       }
       RegCloseKey(hAliasRoot);
       hAliasRoot = NULL;
   }

   return bNewAliasCreated;

} // CopyAliasEntryType

/**-------------------------------------------------------------------------**/

BOOL
DeleteAliasEntryType(
      ULONG  ulProfile,
      LPWSTR szSubKeyName
      )

{

   LONG   RegStatus = ERROR_SUCCESS;
   HKEY   hAliasRoot;
   HKEY   hAliasSubKey;
   WCHAR  RegStr[MAX_PATH];
   WCHAR  szString[MAX_PATH];
   ULONG  ulAliasProfileNumber, ulSize, i;
   BOOL   bDeleted = FALSE;

   //
   // check all aliases under the "Alias" key
   //
   wsprintf(RegStr, TEXT("%s\\%s"),
            pszRegIDConfigDB,
            szSubKeyName);

   RegStatus = RegOpenKeyEx(
            HKEY_LOCAL_MACHINE, RegStr, 0, KEY_ALL_ACCESS,
            &hAliasRoot);
   if (RegStatus == ERROR_FILE_NOT_FOUND) {
       return TRUE;
   } else if (RegStatus != ERROR_SUCCESS) {
      DisplaySystemMessage(NULL, ERROR_REGISTRY_CORRUPT);
      return FALSE;
   }
   
   //
   // enumerate all subkeys
   //
   RegStatus = ERROR_SUCCESS;

   for (i=0; RegStatus == ERROR_SUCCESS; ) {

      ulSize = MAX_PATH;

      RegStatus = RegEnumKey(hAliasRoot, i, szString, ulSize);
      bDeleted = FALSE;

      if (RegStatus == ERROR_SUCCESS) {
         if (RegOpenKey(hAliasRoot, szString, &hAliasSubKey) == ERROR_SUCCESS) {
             
             ulSize = sizeof(ULONG);
             if (RegQueryValueEx(
                 hAliasSubKey, pszRegProfileNumber, NULL, NULL,
                 (LPBYTE)&ulAliasProfileNumber, 
                 &ulSize) == ERROR_SUCCESS) {
                 
                 if (ulProfile == ulAliasProfileNumber) {
                     //
                     // delete this alias entry 
                     //
                     RegCloseKey(hAliasSubKey);
                     hAliasSubKey = NULL;
                     RegDeleteKey(hAliasRoot, szString);
                     bDeleted = TRUE;
                 }
             }
             if (hAliasSubKey) RegCloseKey(hAliasSubKey);
         }
      }
      if (!bDeleted) i++;
   }   
   RegCloseKey(hAliasRoot);
      
   return TRUE;

} // DeleteAliasEntryType

/**-------------------------------------------------------------------------**/

BOOL
CopyRegistryNode(
   HKEY     hSrcKey,
   HKEY     hDestKey
   )
{

   LONG  RegStatus = ERROR_SUCCESS;
   HKEY  hSrcSubKey, hDestSubKey;
   WCHAR szString[MAX_PATH];
   ULONG ulDataSize, ulLength, ulType, i;
   BYTE  Data[MAX_PATH * 2];          // biggest value data??
   PSECURITY_DESCRIPTOR pSecDesc;

   // copy all values for this key
   for (i=0; RegStatus == ERROR_SUCCESS; i++)
   {
      ulLength = MAX_PATH;
      ulDataSize = sizeof(Data);

      RegStatus = RegEnumValue(hSrcKey, i, szString, &ulLength, NULL,
            &ulType, Data, &ulDataSize);

        if (RegStatus == ERROR_SUCCESS)
        {
           RegSetValueEx(hDestKey, szString, 0, ulType, Data, ulDataSize);
        }
    }

    // recursively call CopyRegistryNode to copy all subkeys
    RegStatus = ERROR_SUCCESS;
    for (i=0; RegStatus == ERROR_SUCCESS; i++)
    {

      ulLength = MAX_PATH;

      RegStatus = RegEnumKey(hSrcKey, i, szString, ulLength);

      if (RegStatus == ERROR_SUCCESS)
      {
         if (RegOpenKey(hSrcKey, szString, &hSrcSubKey) == ERROR_SUCCESS)
         {
            if (RegCreateKey(hDestKey, szString, &hDestSubKey) == ERROR_SUCCESS)
            {
               RegGetKeySecurity(hSrcSubKey, DACL_SECURITY_INFORMATION, NULL, &ulDataSize);
               pSecDesc = LocalAlloc(LPTR, ulDataSize);
               RegGetKeySecurity(hSrcSubKey, DACL_SECURITY_INFORMATION, pSecDesc, &ulDataSize);

               CopyRegistryNode(hSrcSubKey, hDestSubKey);

               RegSetKeySecurity(hDestSubKey, DACL_SECURITY_INFORMATION, pSecDesc);
               LocalFree(pSecDesc);
               RegCloseKey(hDestSubKey);
            }
            RegCloseKey(hSrcSubKey);
         }
      }
   }

   return TRUE;

} // CopyRegistryNode


/**-------------------------------------------------------------------------**/
BOOL
DeleteRegistryNode(
   HKEY     hParentKey,
   LPTSTR   szKey
   )
{
   ULONG ulSize = 0;
   LONG  RegStatus = ERROR_SUCCESS;
   HKEY  hKey = NULL;
   WCHAR szSubKey[MAX_PATH];


   //
   // attempt to delete the key
   //
   if (RegDeleteKey(hParentKey, szKey) != ERROR_SUCCESS) {

      if (RegOpenKeyEx(
               hParentKey, szKey, 0, KEY_ENUMERATE_SUB_KEYS | KEY_WRITE,
               &hKey) != ERROR_SUCCESS) {
         return FALSE;
      }

      //
      // enumerate subkeys and delete those nodes
      //
      while (RegStatus == ERROR_SUCCESS) {
         //
         // enumerate the first level children under the profile key
         //
         ulSize = MAX_PATH;
         RegStatus = RegEnumKeyEx(
                  hKey, 0, szSubKey, &ulSize,
                  NULL, NULL, NULL, NULL);

         if (RegStatus == ERROR_SUCCESS) {

            if (!DeleteRegistryNode(hKey, szSubKey)) {
               RegCloseKey(hKey);
               return FALSE;
            }
         }
      }

      //
      // subkeys have been deleted, try deleting this key again
      //
      RegCloseKey(hKey);
      RegDeleteKey(hParentKey, szKey);
   }

   return TRUE;

}  // DeleteRegistryNode


/**-------------------------------------------------------------------------**/
VOID
AdjustProfileTypeCounter(
    PHWPROF_INFO   pInfo,
    ULONG          ulDockState,
    BOOL           bIncrement
    )
{
    PULONG pCounter;

    //
    // use the counter corresponding to the given DockState.
    //
    if ((ulDockState & DOCKINFO_DOCKED) &&
        (ulDockState & DOCKINFO_UNDOCKED)) {       
        pCounter = &pInfo->ulUnknownProfileNameCount;
    } else if (ulDockState & DOCKINFO_DOCKED) {
        pCounter = &pInfo->ulDockedProfileNameCount;
    } else if (ulDockState & DOCKINFO_UNDOCKED) {
        pCounter = &pInfo->ulUndockedProfileNameCount;
    } else {
        pCounter = &pInfo->ulUnknownProfileNameCount;
    }

    //
    // increment or decrement the counter, as requested.
    //
    if (bIncrement) {
        *pCounter += 1;
    } else if (!bIncrement && (*pCounter > 0)) {
        *pCounter -= 1;        
    } else {
        *pCounter = 0;
    }
}

/**-------------------------------------------------------------------------**/
BOOL
StripCurrentTag(
   LPTSTR   szFriendlyName,
   ULONG    ulProfile,
   ULONG    ulCurrentProfile
   )
{
   ULONG ulTagLen, ulNameLen;


   if (ulProfile == ulCurrentProfile) {

      ulTagLen = lstrlen(pszCurrentTag);
      ulNameLen = lstrlen(szFriendlyName);

      if (ulNameLen < ulTagLen) {
         return TRUE;   // nothing to do
      }

      if (lstrcmpi(&szFriendlyName[ulNameLen - ulTagLen], pszCurrentTag) == 0) {
         //
         // truncate the string before the current tag
         //
         szFriendlyName[ulNameLen - ulTagLen - 1] = '\0';
      }
   }

   return TRUE;

} // StripCurrentTag


/**-------------------------------------------------------------------------**/
BOOL
AppendCurrentTag(
   LPTSTR   szTaggedName,
   LPCTSTR  szOriginalName,
   ULONG    ulProfile,
   ULONG    ulCurrentProfile
   )
{

   lstrcpy(szTaggedName, szOriginalName);

   //
   // if the profile is the current profile, then append the tag
   // (let's user easily identify it as current)
   //
   if (ulProfile == ulCurrentProfile) {
      lstrcat(szTaggedName, TEXT(" "));
      lstrcat(szTaggedName, pszCurrentTag);
   }

   return TRUE;

} // AppendCurrentTag


/**-------------------------------------------------------------------------**/
VOID
CreateHwProfileFriendlyName(
   IN  HWND       hDlg,
   IN  ULONG      ulDockState,
   OUT LPTSTR     szFriendlyName
   )
{

   PHWPROF_INFO   pInfo;
   LPTSTR         szPrefix;
   PULONG         pulIndex;
   BOOL           bUnknown=FALSE, bUniqueFriendlyName=FALSE;

   //
   // retrieve the profile buffer
   //
   pInfo = (PHWPROF_INFO)GetWindowLongPtr(hDlg, DWLP_USER);

   //
   // based on the DockState, determine the appropriate label to use and get a
   // pointer to its counter index
   //
   if ((ulDockState & DOCKINFO_DOCKED) &&
       (ulDockState & DOCKINFO_UNDOCKED)) {
       szPrefix = pszUnknown;
       pulIndex = &pInfo->ulUnknownProfileNameCount;
       bUnknown = TRUE;
   }
   else if (ulDockState & DOCKINFO_DOCKED) {
       szPrefix = pszDocked;
       pulIndex = &pInfo->ulDockedProfileNameCount;
   }
   else if (ulDockState & DOCKINFO_UNDOCKED) {
       szPrefix = pszUndocked;
       pulIndex = &pInfo->ulUndockedProfileNameCount;
   }
   else {
       szPrefix = pszUnknown;
       pulIndex = &pInfo->ulUnknownProfileNameCount;
       bUnknown = TRUE;
   }

   while ((!bUniqueFriendlyName) && (*pulIndex < MAX_PROFILES)) {
       //
       // as long as we don't have a unique name, build a FriendlyName based on
       // the DockState and counter index
       //
       if (bUnknown || (*pulIndex > 0)) {
           wsprintf(szFriendlyName, TEXT("%s %u"),
                    szPrefix,
                    *pulIndex + 1);
       } else {
           lstrcpy(szFriendlyName, szPrefix);
       }

       if (IsProfileNameInUse(hDlg,szFriendlyName)) {
           //
           // if this friendly name is in use, increment the counter to reflect this
           //
           *pulIndex += 1;
       } else {
           //
           // a unique friendly name has been generated
           //
           bUniqueFriendlyName = TRUE;
       }
   }

   if (!bUniqueFriendlyName) {
       //
       // if unable to generate a unique friendly name, just use some default.
       // the user may have to deal with any errors arising from duplicate names
       // is this name is already taken
       //
       lstrcpy(szFriendlyName, pszRegDefaultFriendlyName);
   }

   return;

} // CreateHwProfileFriendlyName


/**-------------------------------------------------------------------------**/
VOID
DisplayPrivateMessage(
      HWND  hWnd,
      UINT  uiPrivateError
      )
{
   WCHAR szMessage[MAX_PATH];


   LoadString(hInstance, uiPrivateError, szMessage, MAX_PATH);
   MessageBox(hWnd, szMessage, pszErrorCaption, MB_OK | MB_ICONSTOP);

   return;
}


/**-------------------------------------------------------------------------**/
VOID
DisplaySystemMessage(
      HWND  hWnd,
      UINT  uiSystemError
      )
{
   WCHAR szMessage[MAX_PATH];

   //
   // retrieve the string matching the Win32 system error
   //
   FormatMessage(
            FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
            NULL,
            uiSystemError,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
            szMessage,
            MAX_PATH,
            NULL);

   //
   // display a message box with this error
   //
   MessageBox(
            hWnd,
            szMessage,
            pszErrorCaption,
            MB_OK | MB_ICONSTOP);

   return;

} // DisplaySystemMessage


/**--------------------------------------------------------------------------**/
BOOL
UpdateOrderButtonState(
   HWND  hDlg
   )
{
   PHWPROF_INFO   pInfo;
   ULONG_PTR      ulIndex = 0;
   HWND           hList;


   pInfo = (PHWPROF_INFO)GetWindowLongPtr(hDlg, DWLP_USER);

   hList = GetDlgItem(hDlg, IDD_HWP_PROFILES);

   if ((ulIndex = SendMessage(hList,
        LB_GETCURSEL, 0, 0)) == LB_ERR) {
      return FALSE;
   }

   if (ulIndex == 0) {
      //
      // if focus currently on the button we're about to disable,
      // change focus first or focus will be lost.
      //
      if (GetFocus() == GetDlgItem(hDlg, IDD_HWP_ORDERUP)) {
          SendMessage(hDlg, DM_SETDEFID, IDD_HWP_ORDERDOWN, 0L);
          SetFocus(GetDlgItem(hDlg, IDD_HWP_ORDERDOWN));
      }
      EnableWindow(GetDlgItem(hDlg, IDD_HWP_ORDERUP), FALSE);
   } else {
      EnableWindow(GetDlgItem(hDlg, IDD_HWP_ORDERUP), TRUE);
   }

   if (ulIndex < pInfo->ulActiveProfiles-1) {
      EnableWindow(GetDlgItem(hDlg, IDD_HWP_ORDERDOWN), TRUE);
   } else {
      //
      // if focus currently on the button we're about to disable,
      // change focus first or focus will be lost.
      //
      if (GetFocus() == GetDlgItem(hDlg, IDD_HWP_ORDERDOWN)) {
          SendMessage(hDlg, DM_SETDEFID, IDD_HWP_PROPERTIES, 0L);
          SetFocus(GetDlgItem(hDlg, IDD_HWP_PROPERTIES));
      }
      EnableWindow(GetDlgItem(hDlg, IDD_HWP_ORDERDOWN), FALSE);
   }




   return TRUE;
}


/**-------------------------------------------------------------------------**/
BOOL CALLBACK AddPropSheetPageProc(
    HPROPSHEETPAGE  hpage,
    LPARAM  lParam
   )
{
    hPages[ulNumPages] = hpage;

    return TRUE;

} // AddPropSheetPageProc


/**--------------------------------------------------------------------------**/
BOOL
DisplayProperties(
    IN HWND           hOwnerDlg,
    IN PHWPROF_INFO   pInfo
    )
{
    BOOL              bStatus;
    LPTSTR            pszProviderList = NULL, pszProvider = NULL;
    PROPSHEETPAGE     PropPage;
    PROPSHEETHEADER   PropHeader;
    FARPROC           lpProc;
    ULONG             i, ulSize = 0;
    HKEY              hKey = NULL;


    //
    // create the first page (General)
    //
    ulNumPages = 0;

    PropPage.dwSize        = sizeof(PROPSHEETPAGE);
    PropPage.dwFlags       = PSP_DEFAULT;
    PropPage.hInstance     = hInstance;
    PropPage.pszTemplate   = MAKEINTRESOURCE(DLG_HWP_GENERAL);
    PropPage.pszIcon       = NULL;
    PropPage.pszTitle      = NULL;
    PropPage.pfnDlgProc    = GeneralProfileDlg;
    PropPage.lParam        = (LPARAM)pInfo;
    PropPage.pfnCallback   = NULL;

    hPages[0] = CreatePropertySheetPage(&PropPage);
    if (hPages[0] == NULL) {
        return FALSE;
    }

    ulNumPages++;

    //
    // open the IDConfigDB key
    //
    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, pszRegIDConfigDB, 0,
                     KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) {
        return FALSE;
    }


    //---------------------------------------------------------------
    // Are there any other property pages?
    //---------------------------------------------------------------

    if (RegQueryValueEx(hKey, pszRegPropertyProviders, NULL, NULL,
                        NULL, &ulSize) == ERROR_SUCCESS) {

        pszProviderList = LocalAlloc(LPTR, ulSize);

        if (pszProviderList != NULL) {
            //
            // read list of providers
            //
            if (RegQueryValueEx(hKey, pszRegPropertyProviders, NULL, NULL,
                    (LPBYTE)pszProviderList, &ulSize) == ERROR_SUCCESS) {
                //
                // Ask each provider to create and register it's property page
                //
                for (pszProvider = pszProviderList;
                     *pszProvider;
                     pszProvider += lstrlen(pszProvider) + 1) {

                    if (ulNumPages >= MAX_EXTENSION_PROVIDERS) {
                        break;      // stop at max number of pages
                    }

                    //
                    // load the provider DLL
                    //
                    hLibs[ulNumPages] = LoadLibrary(pszProvider);
                    if (hLibs[ulNumPages] != NULL) {

                        lpProc = GetProcAddress(hLibs[ulNumPages],
                                                "ExtensionPropSheetPageProc");
                        if (lpProc != NULL) {
                            //
                            // pass the profile ID to the provider as the lParam value
                            //
                            if ((lpProc)(NULL,
                                         &AddPropSheetPageProc,
                                         pInfo->ulSelectedProfile)) {
                                ulNumPages++;
                            }
                        }
                    }
                }
            }
            LocalFree(pszProviderList);
        }
    }

    RegCloseKey(hKey);


    //
    // create the property sheet
    //
    PropHeader.dwSize      = sizeof(PROPSHEETHEADER);
    PropHeader.dwFlags     = PSH_PROPTITLE | PSH_NOAPPLYNOW;
    PropHeader.hwndParent  = hOwnerDlg;
    PropHeader.hInstance   = hInstance;
    PropHeader.pszIcon     = NULL;   //MAKEINTRESOURCE(DOCK_ICON);
    PropHeader.pszCaption  =
            pInfo->pHwProfValues[pInfo->ulSelectedProfileIndex].szFriendlyName;
    PropHeader.nPages      = ulNumPages;
    PropHeader.phpage      = hPages;
    PropHeader.nStartPage  = 0;
    PropHeader.pfnCallback = NULL;

    if (PropertySheet(&PropHeader) == 1) {
        bStatus = FALSE;
    } else {
        bStatus = TRUE;
    }

    //
    // cleanup extension page info
    //
    for (i = 1; i < ulNumPages; i++) {
        FreeLibrary(hLibs[i]);
    }

    return bStatus;

} // DisplayProperties



/**-------------------------------------------------------------------------**/
INT_PTR
APIENTRY
GeneralProfileDlg(
      HWND    hDlg,
      UINT    uMessage,
      WPARAM  wParam,
      LPARAM  lParam
      )

{
   PHWPROF_INFO      pInfo = NULL;
   PHWPROF_VALUES    pProfInfo = NULL;
   ULONG             ulReturn, ulIndex;

   switch (uMessage)
   {
      case WM_INITDIALOG:

         if (!lParam) {
            break;
         }

         //
         // on WM_INITDIALOG call, lParam points to the property sheet page.
         // The lParam field in the property sheet page struct is set by,
         // caller. When I created the property sheet, I passed in a pointer
         // to a HWPROF_INFO struct. Save this in the user window long so I
         // can access it on later messages.
         //
         pInfo = (PHWPROF_INFO)((LPPROPSHEETPAGE)lParam)->lParam;
         SetWindowLongPtr(hDlg, DWLP_USER, (LPARAM)pInfo);

         pProfInfo = (PHWPROF_VALUES)(&(pInfo->pHwProfValues[pInfo->ulSelectedProfileIndex]));

         SetDlgItemText(hDlg, IDD_HWP_ST_PROFILE, pProfInfo->szFriendlyName);

         //
         // for pre-beta hwprofile code, the dockstate might have originally
         // been set to zero which is invalid, use 0x111 instead
         //
         if (pProfInfo->ulDockState == 0) {
            pProfInfo->ulDockState =
                     DOCKINFO_USER_SUPPLIED | DOCKINFO_DOCKED | DOCKINFO_UNDOCKED;
         }

         //
         // initialize the dock state radio buttons
         //
         if ((pProfInfo->ulDockState & DOCKINFO_DOCKED) &&
             (pProfInfo->ulDockState & DOCKINFO_UNDOCKED)) {

            CheckRadioButton(hDlg, IDD_HWP_UNKNOWN, IDD_HWP_UNDOCKED, IDD_HWP_UNKNOWN);
         }
         else if (pProfInfo->ulDockState & DOCKINFO_DOCKED) {
            CheckRadioButton(hDlg, IDD_HWP_UNKNOWN, IDD_HWP_UNDOCKED, IDD_HWP_DOCKED);
         }
         else if (pProfInfo->ulDockState & DOCKINFO_UNDOCKED) {
            CheckRadioButton(hDlg, IDD_HWP_UNKNOWN, IDD_HWP_UNDOCKED, IDD_HWP_UNDOCKED);
         }
         else {
            CheckRadioButton(hDlg, IDD_HWP_UNKNOWN, IDD_HWP_UNDOCKED, IDD_HWP_UNKNOWN);
         }

         //
         // if the user-specified bit is not set then the dock state
         // was determined from the hardware so don't allow changing it
         //
         if (pProfInfo->ulDockState & DOCKINFO_USER_SUPPLIED) {
         }
         else {
            EnableWindow(GetDlgItem(hDlg, IDD_HWP_PORTABLE), FALSE);
            EnableWindow(GetDlgItem(hDlg, IDD_HWP_DOCKED), FALSE);
            EnableWindow(GetDlgItem(hDlg, IDD_HWP_UNDOCKED), FALSE);
            EnableWindow(GetDlgItem(hDlg, IDD_HWP_UNKNOWN), FALSE);
         }

         //
         // initialize the dock id and serial # static control
         //
         if (pProfInfo->szSerialNumber[0] &&
             (pProfInfo->szSerialNumber[0] != TEXT('0'))) {
            SetDlgItemText(hDlg, IDD_HWP_SERIALNUM, pProfInfo->szSerialNumber);
         }
         else {
            SetDlgItemText(hDlg, IDD_HWP_SERIALNUM, pszUnavailable);
         }

         if (pProfInfo->szDockID[0] &&
             (pProfInfo->szDockID[0] != TEXT('0'))) {
            SetDlgItemText(hDlg, IDD_HWP_DOCKID, pProfInfo->szDockID);
            //
            // if dock id is available then docking state is known
            // and cannot be over-ridden (this is a redundant check,
            // the dock state should be accurate)
            //
            EnableWindow(GetDlgItem(hDlg, IDD_HWP_PORTABLE), FALSE);
            EnableWindow(GetDlgItem(hDlg, IDD_HWP_DOCKED), FALSE);
            EnableWindow(GetDlgItem(hDlg, IDD_HWP_UNDOCKED), FALSE);
            EnableWindow(GetDlgItem(hDlg, IDD_HWP_UNKNOWN), FALSE);
         }
         else {
            SetDlgItemText(hDlg, IDD_HWP_DOCKID, pszUnavailable);
         }

         //
         // initialize the portable checkbox-groupbox
         //
         if (pInfo->bPortable) {
            CheckDlgButton(hDlg, IDD_HWP_PORTABLE, BST_CHECKED);
         }
         else {
            EnableWindow(GetDlgItem(hDlg, IDD_HWP_DOCKED), FALSE);
            EnableWindow(GetDlgItem(hDlg, IDD_HWP_UNDOCKED), FALSE);
            EnableWindow(GetDlgItem(hDlg, IDD_HWP_UNKNOWN), FALSE);
         }

         //
         // Don't allow changing the global IsPortable Flag if it was determined
         // from the hardware
         //
         if (pInfo->bHwDetectedPortable) {
             EnableWindow(GetDlgItem(hDlg, IDD_HWP_PORTABLE), FALSE);
         }

         //
         // initialize the aliasable checkbox
         //
         if (pProfInfo->bAliasable) {
            CheckDlgButton(hDlg, IDD_HWP_ALIASABLE, BST_CHECKED);
         }
         else {
             CheckDlgButton(hDlg, IDD_HWP_ALIASABLE, BST_UNCHECKED);
         }

         SetFocus(GetDlgItem(hDlg, IDD_HWP_PORTABLE));
         return FALSE;

      case WM_DEVICECHANGE:
         //
         // If a hardware profile change event takes place while the dialog is
         // up, just dismiss the dialog because things have changed.
         //
         if (wParam == DBT_CONFIGCHANGED) {
            PropSheet_PressButton(GetParent(hDlg), PSBTN_CANCEL);
            return TRUE;
         }
         break;

      case WM_NOTIFY:

         if (!lParam) {
            break;
         }

         switch (((NMHDR *)lParam)->code) {

             case PSN_APPLY:

               pInfo = (PHWPROF_INFO)GetWindowLongPtr(hDlg, DWLP_USER);
               pProfInfo = (PHWPROF_VALUES)(&(pInfo->pHwProfValues[pInfo->ulSelectedProfileIndex]));

               //
               // unchecked --> checked case
               //
               if (!pInfo->bPortable && IsDlgButtonChecked(hDlg, IDD_HWP_PORTABLE)) {
                  pInfo->bPortable = TRUE;
               }
               //
               // checked --> unchecked case
               //
               else if (pInfo->bPortable && !IsDlgButtonChecked(hDlg, IDD_HWP_PORTABLE)) {

                   TCHAR szCaption[MAX_PATH];
                   TCHAR szMsg[MAX_PATH];

                   LoadString(hInstance, HWP_ERROR_CAPTION, szCaption, MAX_PATH);
                   LoadString(hInstance, HWP_CONFIRM_NOT_PORTABLE, szMsg, MAX_PATH);

                   //
                   // confirm with user that other profiles will be set to unknown
                   //
                   if (MessageBox(hDlg, szMsg, szCaption,
                                  MB_OKCANCEL | MB_ICONQUESTION) == IDCANCEL) {

                       SetWindowLongPtr(hDlg, DWLP_MSGRESULT, TRUE);   // don't apply
                       return TRUE;
                   }

                   for (ulIndex = 0; ulIndex < pInfo->ulNumProfiles; ulIndex++) {

                       if (pInfo->pHwProfValues[ulIndex].ulDockState & 
                           DOCKINFO_USER_SUPPLIED) {
                           //
                           // only user-specified dock states will be changed
                           //
                           pInfo->pHwProfValues[ulIndex].ulDockState =
                               DOCKINFO_USER_SUPPLIED | DOCKINFO_DOCKED | DOCKINFO_UNDOCKED;
                           pInfo->pHwProfValues[ulIndex].ulAction = HWP_PROPERTIES;                           
                       }

                   }
                   pInfo->bPortable = FALSE;
               }

               //
               // if user-specified dock state, then update the profile values
               // with current ui settings
               //
               if (pProfInfo->ulDockState & DOCKINFO_USER_SUPPLIED) {

                  if (IsDlgButtonChecked(hDlg, IDD_HWP_DOCKED)) {
                     pProfInfo->ulDockState |= DOCKINFO_DOCKED;
                     pProfInfo->ulDockState &= ~DOCKINFO_UNDOCKED;
                  }
                  else if (IsDlgButtonChecked(hDlg, IDD_HWP_UNDOCKED)) {
                     pProfInfo->ulDockState |= DOCKINFO_UNDOCKED;
                     pProfInfo->ulDockState &= ~DOCKINFO_DOCKED;
                  }
                  else {
                     pProfInfo->ulDockState |= (DOCKINFO_UNDOCKED | DOCKINFO_DOCKED);
                  }
               }

               //
               // aliasable unchecked --> checked case
               //
               if (!pProfInfo->bAliasable && IsDlgButtonChecked(hDlg, IDD_HWP_ALIASABLE)) {
                  pProfInfo->bAliasable = TRUE;
               } 
               //
               // aliasable checked --> unchecked case
               //
               else if (pProfInfo->bAliasable && !IsDlgButtonChecked(hDlg, IDD_HWP_ALIASABLE)) {
                  pProfInfo->bAliasable = FALSE;
               } 

               //
               // commit the changes for this profile
               //
               pProfInfo->ulAction |= HWP_PROPERTIES;
               WriteProfileInfo(pProfInfo);

               SetWindowLongPtr(hDlg, DWLP_MSGRESULT, FALSE);   // TRUE if error
               break;

            case PSN_RESET:
               //
               // user canceled the property sheet
               //
               break;
         }
         break;


      case WM_HELP:
         WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, HELP_FILE,
               HELP_WM_HELP, (DWORD_PTR)(LPTSTR)HwProfileHelpIds);
         break;

      case WM_CONTEXTMENU:
         WinHelp((HWND)wParam, HELP_FILE, HELP_CONTEXTMENU,
                  (DWORD_PTR)(LPTSTR)HwProfileHelpIds);
         break;


      case WM_COMMAND:
      {
         switch (LOWORD(wParam))
         {
            case IDD_HWP_PORTABLE:
               //
               // if user chooses portable
               //
               if (!IsDlgButtonChecked(hDlg, IDD_HWP_PORTABLE)) {
                  CheckRadioButton(hDlg, IDD_HWP_UNKNOWN, IDD_HWP_UNDOCKED, IDD_HWP_UNKNOWN);
                  EnableWindow(GetDlgItem(hDlg, IDD_HWP_DOCKED), FALSE);
                  EnableWindow(GetDlgItem(hDlg, IDD_HWP_UNDOCKED), FALSE);
                  EnableWindow(GetDlgItem(hDlg, IDD_HWP_UNKNOWN), FALSE);
               }
               else {
                  EnableWindow(GetDlgItem(hDlg, IDD_HWP_DOCKED), TRUE);
                  EnableWindow(GetDlgItem(hDlg, IDD_HWP_UNDOCKED), TRUE);
                  EnableWindow(GetDlgItem(hDlg, IDD_HWP_UNKNOWN), TRUE);
               }
               break;

            default:
               return FALSE;
          }
          break;

       } // case WM_COMMAND...

       default:
          return FALSE;
          break;
    }

    return TRUE;

} // GeneralProfileDlg
