/*  DRIVERS.C
**
**  Copyright (C) Microsoft, 1990, All Rights Reserved.
**
**  Multimedia Control Panel Applet for installing/configuring installable
**  device drivers.  See the ispec doc DRIVERS.DOC for more information.
**
**  History:
**
**      Tue Jul 31 1990 -by- MichaelE
**          Created.
**
**      Thu Oct 25 1990 -by- MichaelE
**          Added restart, horz. scroll, added SKIPDESC reading desc. strings.
**
**      Sat Oct 27 1990 -by- MichaelE
**          Added FileCopy.  Uses SULIB.LIB and LZCOPY.LIB. Finished stuff
**          for case of installing a driver with more than one type.
**
**      May 1991 -by- JohnYG
**          Added and replaced too many things to list.  Better management
**          of removed drivers, correct usage of DRV_INSTALL/DRV_REMOVE,
**          installing VxD's, replaced "Unknown" dialog with an OEMSETUP.INF
**          method, proper "Cancel" method, fixed many potential UAE's.
*/

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntseapi.h>
#include <windows.h>
#include <mmsystem.h>
#include <memory.h>
#include <string.h>
#include <stdlib.h>
#include <cpl.h>
#include <cphelp.h>
#include <commctrl.h>
#include <mmcpl.h>
#include <mmddkp.h>
#include <mmreg.h>
#include <msacm.h>
#include <msacmdrv.h>
#include <regstr.h>

#include "drivers.h"
#include "sulib.h"
#include "utils.h"
#include "medhelp.h"
#include "midi.h"
#ifdef FIX_BUG_15451
#include "trayvol.h"
#endif // FIX_BUG_15451

#ifndef cchLENGTH
#define cchLENGTH(_sz)  (sizeof(_sz) / sizeof((_sz)[0]))
#endif

#ifndef TreeView_GetGrandParent
#define TreeView_GetGrandParent(_htree,_hti) \
        TreeView_GetParent(_htree,TreeView_GetParent(_htree,_hti))
#endif

/*
 * Enable the definition below to cause MCI devices to be listed by their
 * internal descriptions in the tree, rather than their descriptions as
 * read from Drivers.Desc.
 *
 */
// #define GET_MCI_DEVICE_DESCRIPTIONS_FROM_THEIR_DEVICES

typedef struct
{
        int idIcon;
        int idName;
        int idInfo;
        BOOL bEnabled;
        DWORD dwContext;
        LPTSTR pszHelp;
} APPLET_INFO;

#define NUM_APPLETS     1
#define OBJECT_SIZE     1024

BOOL     bBadOemSetup;
BOOL     bRestart = FALSE;
int      iRestartMessage = 0;
BOOL     bInstallBootLine = FALSE;
BOOL     bCopyVxD;
BOOL     bFindOEM = FALSE;
BOOL     bRelated = FALSE;
BOOL     bCopyEvenIfOlder = FALSE;
BOOL     bDriversAppInUse;
BOOL     bCopyingRelated;
BOOL     bDescFileValid;
HANDLE   myInstance;
HWND     hAdvDlgTree;
UINT     wHelpMessage;
DWORD    dwContext;
PINF     pinfOldDefault = NULL;
TCHAR     szDriversHlp[24];
TCHAR     szLastQuery[20];
TCHAR     szSetupInf[18];
TCHAR     szKnown[250];
TCHAR     szRestartDrv[80];
TCHAR     szUnlisted[150];
TCHAR     szRelatedDesc[30];
TCHAR     szAppName[26];
TCHAR     szDrivers[12];
TCHAR     szRemove[12];
TCHAR     szControlIni[20];
TCHAR     szSysIni[20];
TCHAR     szMCI[6];
TCHAR     szOutOfRemoveSpace[54];
TCHAR     szDriversDesc[38];
TCHAR     szUserDrivers[38];

// Where the source of files to copy is - user updates

TCHAR     szDirOfSrc[MAX_PATH];
TCHAR     szAddDriver[36];
TCHAR     szNoDesc[36];
TCHAR     szError[20];
TCHAR     szRemoveOrNot[250];
TCHAR     szRemoveOrNotStrict[250];
TCHAR     szStringBuf[128];
TCHAR     szMDrivers[38];
TCHAR     szMDrivers32[38];
TCHAR     szFullPath[MAXFILESPECLEN];
TCHAR     szSystem[MAX_PATH];
TCHAR     szOemInf[MAX_PATH];
TCHAR     aszClose[16];
TCHAR     szFileError[50];

#ifdef FIX_BUG_15451
TCHAR    szDriverWhichNeedsSettings[MAX_PATH]; // See MMCPL.C
#endif // FIX_BUG_15451

static   HANDLE   hIList;
static   HANDLE   hWndMain;

/*
 *  Global flag telling us if we're allowed to write to ini files
 */

 BOOL IniFileReadAllowed;
 BOOL IniFileWriteAllowed;


/*
 *** Stuff for keeping track of the TreeView window
 *
 */

#define GetString(_psz,_id) LoadString(myInstance,(_id),(_psz),sizeof((_psz))/sizeof(TCHAR))

static struct   // aDriverKeyword
   {
   LPTSTR       psz;    // text found as alias for driver
   DriverClass  dc; // DriverClass inferred from keyword
   }
aDriverKeyword[] =  // (used by GuessDriverClass())
   {
      { TEXT("waveaudio"),   dcMCI      },    // (sort in inverse alphabetical;
      { TEXT("wavemap"),     dcWAVE     },    //  in particular, longer names first)
      { TEXT("wave"),        dcWAVE     },
      { TEXT("vids"),        dcVCODEC   },
      { TEXT("vidc"),        dcVCODEC   },
      { TEXT("sequencer"),   dcMCI      },
      { TEXT("msvideo"),     dcVIDCAP   },
      { TEXT("msacm"),       dcACODEC   },
      { TEXT("mpegvideo"),   dcMCI      },
      { TEXT("mixer"),       dcMIXER    },
      { TEXT("midimapper"),  dcMIDI     },
      { TEXT("midi"),        dcMIDI     },
      { TEXT("mci"),         dcMCI      },
      { TEXT("icm"),         dcVCODEC   },
      { TEXT("cdaudio"),     dcMCI      },
      { TEXT("avivideo"),    dcMCI      },
      { TEXT("aux"),         dcAUX      },
      { TEXT("acm"),         dcACODEC   },
      { TEXT("joy"),         dcJOY      }
   };

#define nDriverKEYWORDS ((int)(sizeof(aDriverKeyword) / \
                               sizeof(aDriverKeyword[0])))

static struct   // aKeywordDesc
   {
   DriverClass  dc; // DriverClass
   LPTSTR       psz;    // alias which best describes class
   }
aKeywordDesc[] =    // (used by DriverClassToClassNode())
   {
      { dcWAVE,    TEXT("wave")     },
      { dcMIXER,   TEXT("mixer")    },
      { dcVIDCAP,  TEXT("msvideo")  },
      { dcVCODEC,  TEXT("icm")      },
      { dcAUX,     TEXT("aux")      },
      { dcACODEC,  TEXT("acm")      },
      { dcMIDI,    TEXT("midi")     },
      { dcJOY,     TEXT("joystick") }
   };

#define nKeywordDESCS   ((int)(sizeof(aKeywordDesc) / \
                               sizeof(aKeywordDesc[0])))

static struct   // aDriverRoot
   {
   DriverClass  dc; // corresponding driver classification
   BOOL         fAlwaysMake;    // TRUE if should exist even w/o child
   int          idIcon; // icon for items under this tree
   int          idDesc; // description string for parent
   int          idEnable;   // string to describe enabling action
   int          idDisable;  // string to describe disabling action
   HTREEITEM    hti;    // item within tree
   DWORD        dwBit;  // bit mask representing this node
   }
aDriverRoot[] = // (order will define order in display)
   {
      { dcINVALID, TRUE,  IDI_MMICON, IDS_MM_HEADER,
                                      0,
                                      0 },
      { dcWAVE,    TRUE,  IDI_WAVE,   IDS_WAVE_HEADER,
                                      IDS_ENABLEAUDIO,
                                      IDS_DISABLEAUDIO },
      { dcMIDI,    TRUE,  IDI_MIDI,   IDS_MIDI_HEADER,
                                      IDS_ENABLEMIDI,
                                      IDS_DISABLEMIDI },
      { dcMIXER,   TRUE,  IDI_MIXER,  IDS_MIXER_HEADER,
                                      IDS_ENABLEMIXER,
                                      IDS_DISABLEMIXER },
      { dcAUX,     TRUE,  IDI_AUX,    IDS_AUX_HEADER,
                                      IDS_ENABLEAUX,
                                      IDS_DISABLEAUX },
      { dcMCI,     TRUE,  IDI_MCI,    IDS_MCI_HEADER,
                                      IDS_ENABLEMCI,
                                      IDS_DISABLEMCI },
      { dcVCODEC,  TRUE,  IDI_ICM,    IDS_ICM_HEADER,
                                      IDS_ENABLEICM,
                                      IDS_DISABLEICM },
      { dcACODEC,  TRUE,  IDI_ACM,    IDS_ACM_HEADER,
                                      IDS_ENABLEACM,
                                      IDS_DISABLEACM },
      { dcVIDCAP,  TRUE,  IDI_VIDEO,  IDS_VIDCAP_HEADER,
                                      IDS_ENABLECAP,
                                      IDS_DISABLECAP },
      { dcJOY,     TRUE, IDI_JOYSTICK,IDS_JOYSTICK_HEADER,
                                      IDS_ENABLEJOY,
                                      IDS_DISABLEJOY },
      { dcOTHER,   FALSE, IDI_MMICON, IDS_OTHER_HEADER,
                                      IDS_ENABLEJOY,
                                      IDS_DISABLEJOY },
   };

#define nDriverROOTS ((int)(sizeof(aDriverRoot) / sizeof(aDriverRoot[0])))

static LPCTSTR aDriversToSKIP[] =
   {
   TEXT( "MMDRV.DLL" ),
   TEXT( "MIDIMAP.DLL" ),
   TEXT( "MSACM32.DRV" )
   };


static TCHAR cszMMDRVDLL[]    = TEXT("MMDRV.DLL");
static TCHAR cszAliasKERNEL[] = TEXT("KERNEL");
static TCHAR cszRegValueLOADTYPE[] = TEXT("Load Type");

#define nDriversToSKIP ((int)( sizeof(aDriversToSKIP)   \
                             / sizeof(aDriversToSKIP[0]) ))

static HIMAGELIST  hImageList = NULL;   // image list for treeview in advdlg
DriverClass g_dcFilterClass = dcINVALID;

short       DriverClassToRootIndex        (DriverClass);
DriverClass GuessDriverClass              (PIDRIVER);
#ifdef FIX_BUG_15451
DriverClass GuessDriverClassFromAlias     (LPTSTR);
#endif // FIX_BUG_15451
DriverClass GuessDriverClassFromTreeItem  (HTREEITEM hti);
BOOL        EnsureRootIndexExists         (HWND, short);
HTREEITEM   AdvDlgFindTopLevel            (void);
BOOL        InitAdvDlgTree                (HWND);
void        FreeAdvDlgTree                (HWND);
void        TreeContextMenu               (HWND, HWND);

int         lstrnicmp      (LPTSTR, LPTSTR, size_t);
LPTSTR      lstrchr        (LPTSTR, TCHAR);
void        lstrncpy       (LPTSTR, LPTSTR, size_t);

void        ShowDeviceProperties          (HWND, HTREEITEM);

PIDRIVER    FindIDriverByTreeItem         (HTREEITEM);

#ifdef FIX_BUG_15451
HTREEITEM   FindTreeItemByDriverName      (LPTSTR);
#endif // FIX_BUG_15451

// We want to run "control joy.cpl" when the joystick devices
// are highlight and the user clicks Add/Remove/Properties buttons.
BOOL RunJoyControlPanel(void);  //qzheng

/*
 ***
 *
 */


DWORD GetFileDateTime     (LPTSTR);
LPTSTR  GetProfile          (LPTSTR,LPTSTR, LPTSTR, LPTSTR, int);
void  AddIDrivers         (HWND, LPTSTR, LPTSTR);
HTREEITEM AddIDriver      (HWND, PIDRIVER, DriverClass);
BOOL  AddIDriverByName    (HWND, LPCWSTR, DriverClass);
PIDRIVER GetSelectedIDriver (HWND);
BOOL  FillTreeFromWinMM   (HWND);
BOOL  FillTreeFromMSACM   (HWND);
BOOL  FillTreeFromMCI     (HWND);
BOOL  FillTreeFromMIDI    (HWND);
BOOL  FillTreeFromRemaining (HWND);
void  FillTreeFromRemainingBySection (HWND, long ii, LPCTSTR, DriverClass);
BOOL CALLBACK FillTreeFromMSACMQueryCallback (HACMDRIVERID, DWORD_PTR, DWORD);
int __cdecl FillTreeFromMSACMSortCallback (const void *p1, const void *p2);
BOOL  InitAvailable       (HWND, int);
void  RemoveAvailable     (HWND);
BOOL  UserInstalled       (LPTSTR);
INT_PTR  RestartDlg          (HWND, unsigned, WPARAM, LPARAM);
INT_PTR  AddUnlistedDlg      (HWND, unsigned, WPARAM, LPARAM);
INT_PTR   AvailableDriversDlg (HWND, unsigned, WPARAM, LPARAM);
INT_PTR  AdvDlg              (HWND, unsigned, WPARAM, LPARAM);
void  ReBoot              (HWND);
BOOL  GetMappable         (PIDRIVER);
BOOL  SetMappable         (PIRESOURCE, BOOL);

#define REGSTR_PATH_WAVEMAPPER  TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Wave Mapper")
#define REGSTR_VALUE_MAPPABLE   TEXT("Mappable")

#define REGSTR_PATH_MCI         TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\MCI")
#define REGSTR_PATH_MCI32       TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\MCI32")
#define REGSTR_PATH_DRIVERS     TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Drivers")
#define REGSTR_PATH_DRIVERS32   TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Drivers32")


/*
 * REALLOC - Allows expansion of GlobalAlloc()'d block while retaining contents
 *
 *    Newly-allocated portions of memory are initialized to zero, while
 *    the contents of reallocated portions of memory are retained.
 *
 * Parameters:
 *    LPVOID   _pData....allocated array
 *    mysize_t _cOld.....current count of elements in allocated array
 *    mysize_t _cNew.....minimum number of elements requested
 *    mysize_t _cDelta...granularity of increase
 *
 * Example:
 *    {
 *    mysize_t   cElements = 0; // Number of elements allocated so far
 *    DataType  *aElements = NULL;  // Allocated array of DataType
 *
 *    // At this point, cElements == 0 (obviously)
 *
 *    REALLOC (aElements, cElements, 10, 16)
 *
 *    // The line above requested 10 elements, and indicated that elements
 *    // should be allocated in increments of 16.  So cElements is 16 at this
 *    // point (thus, sizeof(aElements) == sizeof(DataType)*16).
 *
 *    REALLOC (aElements, cElements, 12, 16)
 *
 *    // The line above requested 12 elements.  Since cElements is already 16,
 *    // REALLOC knows that 12 elements are already available--and does nothing.
 *
 *    REALLOC (aElements, cElements, 17, 16)
 *
 *    // The line above requested 17 elements, in increments of 16.  aElements
 *    // has been reallocated to contain 32 elements, and cElements is
 *    // therefore 32.
 *
 *    GlobalFree ((HGLOBAL)aElements);  // All done!
 *    aElements = NULL;
 *    cElements = 0;
 *    }
 *
 *
 */

typedef signed long mysize_t;

#ifdef REALLOC
#undef REALLOC
#endif
#define REALLOC(_pData,_cOld,_cNew,_cDelta) \
        ReallocFn (sizeof(*_pData), (void **)&_pData, &_cOld, _cNew, _cDelta)

#ifdef DivRoundUp
#undef DivRoundUp
#endif
#define DivRoundUp(_a,_b) ( (LONG)(((_a) + (_b) -1) / (_b)) )

#ifdef RoundUp
#undef RoundUp
#endif
#define RoundUp(_a,_b) ( DivRoundUp(_a,_b) * (LONG)_b )

BOOL ReallocFn (mysize_t cbElement,
                LPVOID *ppOld, mysize_t *pcOld, mysize_t cNew, mysize_t cDelta)
{
   LPVOID     pNew;
   mysize_t   cbOld, cbNew;

            // First check if we actually need to reallocate or not.
            // It's possible that {ppOld} was already allocated with
            // enough space.
            //
   if ( ((*ppOld) != NULL)  && (cNew <= (*pcOld)) )
      return TRUE;

            // Oh well.  Determine how much space we need, and how much
            // is allocated now.
            //
   cNew  = RoundUp (cNew, cDelta);
   cbNew = cbElement * cNew;
   cbOld = (ppOld == NULL) ? 0 : (cbElement * (*pcOld));

            // Allocate the space and copy over the original contents.
            // Zero-fill the remaining space.
            //
   if ((pNew = (LPVOID)GlobalAlloc (GMEM_FIXED, cbNew)) == NULL)
      return FALSE;

   if (cbOld != 0)
      {
      memcpy (pNew, *ppOld, cbOld);
      GlobalFree ((HGLOBAL)(*ppOld));
      }

   memset (&((char*)pNew)[ cbOld ], 0x00, cbNew -cbOld);

            // Finally, update the passed-in pointers and we're done.
            //
   *pcOld = cNew;
   *ppOld = pNew;

   return TRUE;
}


/*
 * PIDRIVER ARRAY
 *
 * The AddIDrivers() routine LocalAlloc()'s a single IDRIVER structure
 * for each installed device driver.  Pointers to these structures are
 * retained in the InstalledDriver array, and indices into this array
 * are stored as the LPARAM values for each tree item.  Each element
 * in the array stores not only a pointer to the driver's IDRIVER structure,
 * but also a DWORD which, as a combination of aDriverRoot[].dwBit values,
 * reflects the tree root items under which the driver has tree items.
 *
 */

typedef struct  // InstalledDriver
   {
   PIDRIVER  pIDriver;     // Pointer to AddIDrivers()'s PIDRIVER structure
   DWORD     dwBits;       // Combination of aDriverRoot[].dwBit flags
   } InstalledDriver;

InstalledDriver  *aInstalledDrivers = NULL;
mysize_t          cInstalledDrivers = 0;

#define NOPIDRIVER  ((LPARAM)-1)



/*
 *  CheckSectionAccess()
 *
 *  See if we can read/write to a given section
 */


 BOOL CheckSectionAccess(TCHAR *szIniFile, TCHAR *SectionName)
 {
     static TCHAR TestKey[] = TEXT("TestKey!!!");
     static TCHAR TestData[] = TEXT("TestData");
     static TCHAR ReturnData[50];

    /*
     *   Check we can write, read back and delete our key
     */

     return WritePrivateProfileString(SectionName,
                                      TestKey,
                                      TestData,
                                      szIniFile) &&

            GetPrivateProfileString(SectionName,
                                    TestKey,
                                    TEXT(""),
                                    ReturnData,
                                    sizeof(ReturnData) / sizeof(TCHAR),
                                    szIniFile) == (DWORD)wcslen(TestData) &&

            WritePrivateProfileString(SectionName,
                                      TestKey,
                                      NULL,
                                      szIniFile);
 }


/*
 *  CheckIniAccess()
 *
 *  Checks access to our 2 .ini file sections - DRIVERS_SECTION and
 *  MCI_SECTION by just writing and reading some junk
 *
 *  Basically if we don't have access to these sections we're not
 *  going to allow Add and Remove.  The individual MCI drivers must
 *  take care not to put their data into non-writeable storage although
 *  this completely messes up the default parameters thing so we're going
 *  to put these into a well-known key in the win.ini file (ie per user).
 *
 */

 BOOL CheckIniAccess(void)
 {
     return CheckSectionAccess(szSysIni, szDrivers) &&
            CheckSectionAccess(szSysIni, szMCI) &&
            CheckSectionAccess(szControlIni, szUserDrivers) &&
            CheckSectionAccess(szControlIni, szDriversDesc) &&
            CheckSectionAccess(szControlIni, szRelatedDesc);
 }

/*
 *  QueryRemoveDrivers()
 *
 *  Ask the user if they're sure.  If the Driver is one required by the
 *  system (ie not listed in [Userinstallable.drivers] in control.ini)
 *  warn the user of that too.
 */

 BOOL QueryRemoveDrivers(HWND hDlg, LPTSTR szKey, LPTSTR szDesc)
 {
     TCHAR bufout[MAXSTR];

     if (UserInstalled(szKey))
          wsprintf(bufout, szRemoveOrNot, (LPTSTR)szDesc);
     else
          wsprintf(bufout, szRemoveOrNotStrict, (LPTSTR)szDesc);

     return (MessageBox(hDlg, bufout, szRemove,
                    MB_ICONEXCLAMATION | MB_TASKMODAL | MB_YESNO) == IDYES );
 }

/*
 *  GetProfile()
 *
 *  Get private profile strings.
 */

 LPTSTR GetProfile(LPTSTR pstrAppName, LPTSTR pstrKeyName, LPTSTR pstrIniFile,
                 LPTSTR pstrRet, int cbSize)
 {
     TCHAR szNULL[2];

     szNULL[0] = TEXT('\0');
     GetPrivateProfileString(pstrAppName, (pstrKeyName==NULL) ? NULL :
         (LPTSTR)pstrKeyName, szNULL, pstrRet, cbSize/sizeof(TCHAR), pstrIniFile);
     return(pstrRet);
 }

/*********************************************************************
 *
 *  AddIDrivers()
 *
 *  Add drivers in the passed key strings list to the InstalledDrivers array
 *
 *********************************************************************/

void AddIDrivers(HWND hWnd, LPTSTR pstrKeys, LPTSTR pstrSection)
{
    PIDRIVER    pIDriver;
    LPTSTR        pstrKey;
    LPTSTR        pstrDesc;

    pstrKey = pstrKeys;
    pstrDesc = (LPTSTR)LocalAlloc(LPTR, MAXSTR);

	if (!pstrDesc) return;

   /*
    *  parse key strings for profile, and make IDRIVER structs
    */

    while ( *pstrKey )
    {
        pIDriver = (PIDRIVER)LocalAlloc(LPTR, sizeof(IDRIVER));
        if ( pIDriver )
        {
            LPTSTR        pstr;

            if (*GetProfile(pstrSection, pstrKey, szSysIni, pIDriver->szFile,
                sizeof(pIDriver->szFile)) == TEXT('\0'))
            {
                LocalFree((HANDLE)pIDriver);
                goto nextkey;
            }

            for ( pstr=pIDriver->szFile; *pstr && (*pstr!=COMMA) &&
                (*pstr!=SPACE); pstr++ )
                    ;
            *pstr = TEXT('\0');

#ifdef TRASHDRIVERDESC
            if (bDescFileValid)
#endif
              /*
               *  try to load the cached description
               */

               GetProfile(szDriversDesc,
                          pIDriver->szFile,
                          szControlIni,
                          pIDriver->szDesc,
                          sizeof(pIDriver->szDesc));

           /*
            *  if we failed, then try to get the information from
            *  mmdriver.inf or the exehdr
            */

            if (pIDriver->szDesc[0] == TEXT('\0'))
            {
               if (LoadDescFromFile(pIDriver, pstrKey, pstrDesc) != DESC_NOFILE)
               {
                   if (!*pstrDesc)
                   {
                       /*
                        *  failed to load a description.
                        *  The file isn't in setup.inf
                        *  and doesn't have exehdr information
                        */

                        lstrcpy(pIDriver->szDesc, pIDriver->szFile);
                        lstrcat(pIDriver->szDesc, szNoDesc);
                   }
                   else
                        lstrcpy(pIDriver->szDesc, pstrDesc);

                   WritePrivateProfileString(szDriversDesc, pIDriver->szFile,
                               pIDriver->szDesc, szControlIni);
               } else {
                    LocalFree((HANDLE)pIDriver);
                    goto nextkey;
               }
            }

            wcsncpy(pIDriver->szAlias, pstrKey, sizeof(pIDriver->szAlias)/sizeof(TCHAR));
            pIDriver->szAlias[sizeof(pIDriver->szAlias)/sizeof(TCHAR) - 1] = TEXT('\0');
            wcscpy(pIDriver->wszAlias, pIDriver->szAlias);

            wcsncpy(pIDriver->szSection, pstrSection,sizeof(pIDriver->szSection)/sizeof(TCHAR));
            pIDriver->szSection[sizeof(pIDriver->szSection)/sizeof(TCHAR) - 1] = TEXT('\0');
            wcscpy(pIDriver->wszSection, pIDriver->szSection);

            pIDriver->KernelDriver = IsFileKernelDriver(pIDriver->szFile);
            pIDriver->fQueryable = pIDriver->KernelDriver ? 0 : -1;

            pIDriver->lp = 0L;

            if (!AddIDriverToArray (pIDriver))
               LocalFree((HANDLE)pIDriver);
        }
        else
           break;  //ERROR Low Memory

nextkey: while (*pstrKey++);
    }
    LocalFree((HANDLE)pstrDesc);
}


/*
 * AddIDriverToArray - Adds the given PIDRIVER to the InstalledDrivers array
 *
 */

BOOL AddIDriverToArray (PIDRIVER pIDriver)
{
    mysize_t  ii;

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

            // Don't create duplicate entries in this array; one PIDRIVER
            // per driver-file is sufficient.
            //
    for (ii = 0; ii < cInstalledDrivers; ++ii)
    {
        if (aInstalledDrivers[ ii ].pIDriver != NULL)
        {
            if (!lstrcmpi (aInstalledDrivers[ ii ].pIDriver->szFile,
                           pIDriver->szFile))
            {
                return FALSE;
            }
        }
    }

            // To reduce repetitive calls to GlobalAlloc(), we'll allocate
            // space for an additional 50 InstalledDriver entries within
            // the aInstalledDrivers array each time we run out of space.
            //
#define nDriverEntriesToAllocAtONCE   50

    for (ii = 0; ii < cInstalledDrivers; ++ii)
    {
        if (aInstalledDrivers[ ii ].pIDriver == NULL)
            break;
    }

    if (ii >= cInstalledDrivers)
    {
        if (!REALLOC (aInstalledDrivers,   // Array
                      cInstalledDrivers,   // Current size of array
                      1+ii,                // Requested size of array
                      nDriverEntriesToAllocAtONCE))
        {
            return FALSE;
        }
    }

    aInstalledDrivers[ ii ].pIDriver = pIDriver;
    aInstalledDrivers[ ii ].dwBits = 0L;

    return TRUE;
}


/*********************************************************************
 *
 *  FindInstallableDriversSection()
 *
 *********************************************************************/

PINF FindInstallableDriversSection(PINF pinf)
{
    PINF pinfFound;

    pinfFound = infFindSection(pinf, szMDrivers32);

    if (pinfFound == NULL) {
        pinfFound = infFindSection(pinf, szMDrivers);
    }

    return pinfFound;
}

//NOTE: Returns nSize as a count of bytes, not characters (later calls expect this)
int GetINISectionSize(LPCTSTR pstrSection, LPCTSTR pstrFile)
{
    int ncbSize = 0;
    int ncbMaxSize = 0;

    while (ncbSize >= ncbMaxSize)
    {
        TCHAR szNULL[2];
        LPTSTR pStr = NULL;

        szNULL[0] = TEXT('\0');

        ncbMaxSize += SECTION; //allocate another 512 bytes

        pStr = (LPTSTR)LocalAlloc(LPTR, ncbMaxSize);

        if (!pStr)
        {
            //we're trying to allocate too much memory ...
            //drop out and use the last smaller size that worked
            break;
        }

        ncbSize = GetPrivateProfileString(pstrSection, NULL, szNULL, pStr, ncbMaxSize/sizeof(TCHAR), pstrFile);
        ncbSize = (ncbSize+2) * sizeof(TCHAR);  //convert to byte count, adding two chars
                                                //to account for terminating null and API's truncation

        LocalFree(pStr);
    }

    return (ncbSize);
}


/*********************************************************************
 *
 *  InitInstalled()
 *
 *  Add the drivers installed in [DRIVERS] and [MCI] to the Installed
 *  Drivers list box.
 *
 *********************************************************************/

BOOL InitInstalled(HWND hWnd, LPTSTR pstrSection)
{
    BOOL    bSuccess=FALSE;
    LPTSTR    pstr;
    int nSize = SECTION;

#ifdef TRASHDRIVERDESC
    UINT    wTime;
    BOOL    fForce;
    TCHAR    szOut[10];

    wTime = LOWORD(GetFileDateTime(szControlIni)) >> 1;
    if (fForce = (GetPrivateProfileInt((LPTSTR)szUserDrivers,
                   (LPTSTR)szLastQuery,  0, (LPTSTR)szControlIni) != wTime))
    {
        wsprintf(szOut, TEXT("%d"), wTime);
        WritePrivateProfileString((LPTSTR)szUserDrivers, (LPTSTR)szLastQuery,
                                        szOut, (LPTSTR)szControlIni);
        WritePrivateProfileString((LPTSTR)szDriversDesc, NULL, NULL,
                                                (LPTSTR)szControlIni);
        bDescFileValid = FALSE;
    }
    else
        bDescFileValid = TRUE;
#endif

    nSize = GetINISectionSize(pstrSection, szSysIni);

    pstr = (LPTSTR)LocalAlloc(LPTR, nSize);
    if ( pstr )
    {
        if (*GetProfile(pstrSection, NULL, szSysIni, pstr, nSize ))
        {
            AddIDrivers(hWnd,pstr,pstrSection);
            bSuccess = TRUE;
        }

        LocalFree((HANDLE)pstr);
    }

    return(bSuccess);
}


/*
 * RefreshAdvDlgTree - Clears the Devices tree, and fills it back in
 *
 */

void RefreshAdvDlgTree (void)
{
   if (hAdvDlgTree != NULL)
      {
      SendMessage (hAdvDlgTree, WM_SETREDRAW, FALSE, 0L);

      FreeAdvDlgTree (hAdvDlgTree);
      InitAdvDlgTree (hAdvDlgTree);
      InitInstalled (GetParent (hAdvDlgTree), szDrivers);
      InitInstalled (GetParent (hAdvDlgTree), szMCI);
      FillTreeInAdvDlg (hAdvDlgTree, NULL);

      SendMessage (hAdvDlgTree, WM_SETREDRAW, TRUE, 0L);
      }
}


/*
 * FillTreeInAdvDlg - Adds TreeItems for each entry in aInstalledDrivers
 *
 * If pIDriver is specified, the first treeitem to mention that driver
 * will be highlighted.
 *
 */

BOOL FillTreeInAdvDlg (HWND hTree, PIDRIVER pIDriver)
{
   if (!FillTreeFromWinMM (hTree))
      return FALSE;

   if (!FillTreeFromMSACM (hTree))
      return FALSE;

   if (!FillTreeFromMCI (hTree))
      return FALSE;

   if (!FillTreeFromMIDI (hTree))
      return FALSE;

   if (!FillTreeFromRemaining (hTree))
      return FALSE;

   if (pIDriver != NULL)    // Do we have to highlight a pIDriver?
      {
      short idr;

      for (idr = 0; idr < nDriverROOTS; idr++)
         {
         HTREEITEM  hti;

         if ((hti = aDriverRoot[ idr ].hti) == NULL)
            continue;

         for (hti = TreeView_GetChild (hTree, hti);
              hti != NULL;
              hti = TreeView_GetNextSibling (hTree, hti))
            {
            if (pIDriver == FindIDriverByTreeItem (hti))
               {
               TreeView_SelectItem (hTree, hti);
               break;
               }
            }

         if (hti != NULL)   // Found and selected a TreeItem?
            break;  // Then we're done!
         }
      }

   return TRUE;
}


/*
 * FillTreeFromWinMM - Adds tree items for all WinMM-controlled MM devices
 *
 * This routine adds tree items under the following DriverClasses:
 *    dcWAVE    - waveOut*
 *    dcMIXER   - mixer*
 *    dcAUX     - aux*
 *
 */

BOOL FillTreeFromWinMM (HWND hTree)
{
   UINT     iDevice;
   UINT     cDevices;
   WCHAR    szDriver[ cchRESOURCE ];

            // Add entries for each waveOut device
            //
   cDevices = waveOutGetNumDevs ();
   for (iDevice = 0; iDevice < cDevices; ++iDevice)
      {
      if (waveOutMessage (HWAVEOUT_INDEX(iDevice),
                          DRV_QUERYFILENAME,
                          (DWORD_PTR)szDriver,
                          (DWORD_PTR)cchRESOURCE) == MMSYSERR_NOERROR)
         {
         AddIDriverByName (hTree, szDriver, dcWAVE);
         }
      }

            // Add entries for each mixer device
            //
   cDevices = mixerGetNumDevs ();
   for (iDevice = 0; iDevice < cDevices; ++iDevice)
      {
      if (mixerMessage (HMIXER_INDEX(iDevice),
                        DRV_QUERYFILENAME,
                        (DWORD_PTR)szDriver,
                        (DWORD_PTR)cchRESOURCE) == MMSYSERR_NOERROR)
         {
         AddIDriverByName (hTree, szDriver, dcMIXER);
         }
      }

            // Add entries for each aux device
            //
   cDevices = auxGetNumDevs ();
   for (iDevice = 0; iDevice < cDevices; ++iDevice)
      {
      if (auxOutMessage (iDevice,
                         DRV_QUERYFILENAME,
                         (DWORD_PTR)szDriver,
                         (DWORD_PTR)cchRESOURCE) == MMSYSERR_NOERROR)
         {
         AddIDriverByName (hTree, szDriver, dcAUX);
         }
      }

   return TRUE;
}


/*
 * FillTreeFromMSACM - Adds tree items for all MSACM-controlled MM devices
 *
 * This routine adds tree items under the following DriverClasses:
 *    dcACODEC  - acmDriverEnum()
 *
 * Note that, since audio codecs are supposed to be sorted in the tree,
 * all audio codec treeitems are first deleted from the tree (if there
 * are any at this point) then all audio codecs are added in their
 * sorted order.
 *
 */

typedef struct
   {
   DWORD     dwPriority;    // priority of this audio codec
   PIDRIVER  pIDriver;      // matching driver file (or NULL)
   WORD      wMid;          // manufacturer ID
   WORD      wPid;          // product ID
   TCHAR      szDesc[ ACMDRIVERDETAILS_LONGNAME_CHARS ];
   } AudioCodec;

AudioCodec  *pCodecs;
mysize_t     cCodecs;

extern BOOL gfLoadedACM;   // From MSACMCPL.C

BOOL FillTreeFromMSACM (HWND hTree)
{
   MMRESULT  mmr;
   short     idr;
   mysize_t  ii;

   if (!gfLoadedACM)
      {
      if (LoadACM())
         gfLoadedACM = TRUE;
      }
   if (!gfLoadedACM)
      return FALSE;

            // Step one: get rid of any audio codecs listed in the tree
            //
   if ((idr = DriverClassToRootIndex (dcACODEC)) != -1)
      {
      if (aDriverRoot[ idr ].hti != NULL)
         {
         HTREEITEM hti;

         while ((hti = TreeView_GetChild (hTree, aDriverRoot[ idr ].hti)) != 0)
            {
            TreeView_DeleteItem (hTree, hti);

            if (hti == TreeView_GetChild (hTree, aDriverRoot[ idr ].hti))
               break;  // if it didn't delete, make sure we don't loop forever!
            }
         }

      for (ii = 0; ii < cInstalledDrivers; ++ii)
         {
         aInstalledDrivers[ ii ].dwBits &= ~aDriverRoot[ idr ].dwBit;
         }
      }

            // Step two: query ACM to obtain the list of codecs
            //
   pCodecs = NULL;
   cCodecs = 0;

   mmr = (MMRESULT)acmDriverEnum (FillTreeFromMSACMQueryCallback,
                        0,
                        ACM_DRIVERENUMF_NOLOCAL | ACM_DRIVERENUMF_DISABLED);

               // Step three: sort the list of codecs and add each to the tree
               //
   if ((mmr == MMSYSERR_NOERROR) && (pCodecs != NULL))
      {
      mysize_t  iiDr;

      qsort (pCodecs, (size_t)cCodecs, sizeof(AudioCodec),
             FillTreeFromMSACMSortCallback);

                  // Assign lp=wMid+wPid for each audio codec we find
                  //
      for (iiDr = 0; iiDr < cInstalledDrivers; ++iiDr)
         {
         if (aInstalledDrivers[ iiDr ].pIDriver == NULL)
            continue;
         if (aInstalledDrivers[ iiDr ].pIDriver->lp != 0L)  // already did this
            continue;

         if (GuessDriverClass (aInstalledDrivers[ iiDr ].pIDriver) == dcACODEC)
            {
            HANDLE hDriver;

            hDriver = OpenDriver (aInstalledDrivers[iiDr].pIDriver->wszAlias,
                                  aInstalledDrivers[iiDr].pIDriver->wszSection,
                                  0L);
            if (hDriver != NULL)
               {
               ACMDRIVERDETAILSW add;
               memset ((TCHAR *)&add, 0x00, sizeof(add));
               add.cbStruct = sizeof(add);
               SendDriverMessage (hDriver, ACMDM_DRIVER_DETAILS, (LONG_PTR)&add, 0);
               CloseDriver (hDriver, 0L, 0L);

               aInstalledDrivers[ iiDr ].pIDriver->lp = MAKELONG( add.wMid,
                                                                  add.wPid );
               }
            }
         }

                  // Search for installed drivers with matching lp=wMid+wPid's
                  //
      for (iiDr = 0; iiDr < cInstalledDrivers; ++iiDr)
         {
         if (aInstalledDrivers[ iiDr ].pIDriver == NULL)
            continue;

         if ((aInstalledDrivers[ iiDr ].pIDriver->szAlias[0] == TEXT('\0')) ||
             (GuessDriverClass (aInstalledDrivers[iiDr].pIDriver) == dcACODEC))
            {
            for (ii = 0; ii < cCodecs; ++ii)
               {
               if (pCodecs[ ii ].dwPriority == 0)
                  continue;

               if ( (pCodecs[ ii ].wMid ==
                     LOWORD( aInstalledDrivers[ iiDr ].pIDriver->lp )) &&
                    (pCodecs[ ii ].wPid ==
                     HIWORD( aInstalledDrivers[ iiDr ].pIDriver->lp )) )
                  {
                  pCodecs[ ii ].pIDriver = aInstalledDrivers[ iiDr ].pIDriver;
                  break;
                  }
               }
            }
         }

                  // Add each in-use entry in pCodecs to the treeview
                  //
      for (ii = 0; ii < cCodecs; ++ii)
         {
         if (pCodecs[ ii ].dwPriority == 0)
            continue;

                     // The PCM converter, for instance, won't have a matching
                     // PID.  So create a bogus one--the lack of an szAlias
                     // will let us know it's bogus--and insert it into the
                     // aInstalledDrivers array.
                     //
         if (pCodecs[ ii ].pIDriver == NULL)
            {
            PIDRIVER  pid = (PIDRIVER)LocalAlloc(LPTR, sizeof(IDRIVER));

            if (pid != NULL)
               {
               memset (pid, 0x00, sizeof(IDRIVER));
               pid->lp = MAKELONG( pCodecs[ ii ].wMid, pCodecs[ ii ].wPid );
               lstrcpy (pid->szDesc, pCodecs[ ii ].szDesc);

               if (!AddIDriverToArray (pid))
                  LocalFree ((HLOCAL)pid);
               else
                  {
                  pCodecs[ ii ].pIDriver = pid;
                  }
               }
            }

         if (pCodecs[ ii ].pIDriver != NULL)
            {
            AddIDriver (hTree, pCodecs[ ii ].pIDriver, dcACODEC);
            }
         }
      }

               // Cleanup
               //
   if (pCodecs != NULL)
      {
      GlobalFree ((HGLOBAL)pCodecs);
      pCodecs = NULL;
      cCodecs = 0;
      }

   return (mmr == MMSYSERR_NOERROR) ? TRUE : FALSE;
}


/*
 * FillTreeFromMCI - Adds tree items for all MCI devices
 *
 * This routine adds tree items under the following DriverClasses:
 *    dcMCI     - mciSendCommand
 *
 */

BOOL FillTreeFromMCI (HWND hTree)
{
   MCI_SYSINFO_PARMS mciSysInfo;
   TCHAR szAlias[ cchRESOURCE ];

            // How many MCI devices does WinMM know about?
            //
   memset ((TCHAR *)&mciSysInfo, 0x00, sizeof(mciSysInfo));
   mciSysInfo.lpstrReturn = szAlias;
   mciSysInfo.dwRetSize = cchLENGTH(szAlias);
   mciSysInfo.wDeviceType = MCI_ALL_DEVICE_ID;

   if (mciSendCommand (MCI_ALL_DEVICE_ID,
                       MCI_SYSINFO,
                       MCI_SYSINFO_QUANTITY,
                       (DWORD_PTR)&mciSysInfo) == 0)
      {
      DWORD    iDevice;
      DWORD    cDevices;

      cDevices = *((DWORD *)(mciSysInfo.lpstrReturn));

               // Get the name of each MCI device in turn.
               //
      for (iDevice = 0; iDevice < cDevices; ++iDevice)
         {
         mysize_t ii;

         memset ((TCHAR *)&mciSysInfo, 0x00, sizeof(mciSysInfo));
         mciSysInfo.lpstrReturn = szAlias;
         mciSysInfo.dwRetSize = cchLENGTH(szAlias);
         mciSysInfo.wDeviceType = MCI_ALL_DEVICE_ID;
         mciSysInfo.dwNumber = 1+iDevice;  // note: 1-based, not 0-based!

         if (mciSendCommand (MCI_ALL_DEVICE_ID,
                             MCI_SYSINFO,
                             MCI_SYSINFO_NAME,
                             (DWORD_PTR)&mciSysInfo) != 0)
            {
            continue;
            }

                  // Got an alias--search the InstalledDrivers array
                  // and try to find a matching PIDRIVER.
                  //
         for (ii = 0; ii < cInstalledDrivers; ++ii)
            {
            if (aInstalledDrivers[ ii ].pIDriver == NULL)
               continue;
            if (!lstrcmpi (aInstalledDrivers[ ii ].pIDriver->szAlias, szAlias))
               {
#ifdef GET_MCI_DEVICE_DESCRIPTIONS_FROM_THEIR_DEVICES
               MCI_OPEN_PARMS  mciOpen;
               MCI_INFO_PARMS  mciInfo;
               MCIERROR        rc;

                        // It's an installed, functioning, happy MCI device.
                        // Open it up and see what it calls itself; update the
                        // description in the PIDRIVER (what's in there was
                        // obtained from the registry, thus from MMDRIVER.INF,
                        // and we instead want what Media Player lists in its
                        // Device menu).
                        //
               memset ((TCHAR *)&mciOpen, 0x00, sizeof(mciOpen));
               mciOpen.lpstrDeviceType = szAlias;

               rc = mciSendCommand (0,MCI_OPEN,MCI_OPEN_TYPE,(DWORD)&mciOpen);
               if (rc == MCIERR_MUST_USE_SHAREABLE)
                  {
                  rc = mciSendCommand (0, MCI_OPEN,
                                       MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE,
                                       (DWORD)&mciOpen);
                  }
               if (rc == 0)
                  {
                  TCHAR szDesc[ cchRESOURCE ];
                  szDesc[0] = 0;

                  mciInfo.lpstrReturn = szDesc;
                  mciInfo.dwRetSize = cchLENGTH(szDesc);

                  if (mciSendCommand (mciOpen.wDeviceID,
                                      MCI_INFO,
                                      MCI_INFO_PRODUCT,
                                      (DWORD)&mciInfo) == 0)
                     {
                     lstrcpy (aInstalledDrivers[ ii ].pIDriver->szDesc, szDesc);
                     }

                  mciSendCommand (mciOpen.wDeviceID, MCI_CLOSE, 0L, 0);
                  }
#endif // GET_MCI_DEVICE_DESCRIPTIONS_FROM_THEIR_DEVICES

               AddIDriver (hTree, aInstalledDrivers[ ii ].pIDriver, dcMCI);
               break;
               }
            }
         }
      }

   return TRUE;
}


/*
 * FillTreeFromMIDI - Adds tree items for all MIDI devices and instruments
 *
 * This routine adds tree items under the following DriverClasses:
 *    dcMIDI    - LoadInstruments() provides necessary data
 *
 */

BOOL FillTreeFromMIDI (HWND hTree)
{
   MCMIDI mcm;
   UINT   iiRoot;
   int    idrMIDI;

   if ((idrMIDI = DriverClassToRootIndex (dcMIDI)) == -1)
   return FALSE;

            // First load in all relevant information regarding MIDI
            // instruments.  Fortunately, all that work is encapsulated
            // nicely within one routine.
            //
   memset (&mcm, 0x00, sizeof(mcm));
   LoadInstruments (&mcm, FALSE);

            // Each entry in mcm's api array is one of three things:
            //  - a parent (say, a sound card)
            //  - a child (say, an external instrument)
            //  - the "(none)" thing that we want to skip
            //
            // Add each parent to the tree, and when we find a parent,
            // add all its children.
            //
   for (iiRoot = 0; iiRoot < mcm.nInstr; ++iiRoot)
      {
      TCHAR    szName[ MAXSTR ];
      LPTSTR   pch;
      PIDRIVER pid;

      if (mcm.api[ iiRoot ] == NULL)
         continue;
      if (mcm.api[ iiRoot ]->piParent != NULL)
         continue;
      if (mcm.api[ iiRoot ]->szKey[0] == TEXT('\0'))
         continue;

               // Found a parent!  If we can match it to an installed driver,
               // add it to the tree.  Note that mcm.api[]->szKey will
               // be of the form "MMDRV.DLL<0000>"--we need to strip off
               // the "<0000>" before we can match this thing to a PIDRIVER.
               //
      lstrcpy (szName, mcm.api[ iiRoot ]->szKey);
      if ((pch = lstrchr (szName, TEXT('<'))) != NULL)
         *pch = TEXT('\0');

      if ((pid = FindIDriverByName (szName)) != NULL)
         {
         HTREEITEM hti;
         UINT      ii;
         TV_ITEM   tvi;

         if ((hti = AddIDriver (hTree, pid, dcMIDI)) == NULL)
            continue;

#if 0
         tvi.mask = TVIF_TEXT;
         tvi.hItem = hti;
         tvi.pszText = mcm.api[ iiRoot ]->szFriendly;
         TreeView_SetItem(hTree, &tvi);
#endif

                  // We've added this parent.  See if it has any children,
                  // and if so, stick 'em in the tree.
                  //
         for (ii = 0; ii < mcm.nInstr; ++ii)
            {
            PINSTRUM lp;
            TV_INSERTSTRUCT ti;

            if (mcm.api[ ii ] == NULL)
               continue;
            if (mcm.api[ ii ]->piParent != mcm.api[ iiRoot ])
               continue;

                     // Yep--it's got a parent.  Allocate a second copy
                     // of this PINSTRUM; that copy will be our LPARAM value.
                     //
            if ((lp = (PINSTRUM)LocalAlloc(LPTR,sizeof (INSTRUM))) == NULL)
               continue;
            memcpy ((TCHAR *)lp, (TCHAR *)mcm.api[ ii ], sizeof (INSTRUM));

                     // Now add a treeitem for this instrument.
                     //
            ti.hParent = hti;
            ti.hInsertAfter = TVI_LAST;
            ti.item.mask = TVIF_TEXT | TVIF_PARAM |
                           TVIF_IMAGE | TVIF_SELECTEDIMAGE;
            ti.item.iImage = (int)idrMIDI;
            ti.item.iSelectedImage = (int)idrMIDI;
            ti.item.pszText = mcm.api[ ii ]->szFriendly;
            ti.item.lParam = (LPARAM)lp;

            if (!TreeView_InsertItem (hTree, &ti))
               break;
            }
         }
      }

            // Done--cleanup and we're out of here.
            //
   FreeInstruments (&mcm);

   if (mcm.hkMidi)
      RegCloseKey (mcm.hkMidi);

   return TRUE;
}


            // To reduce repetitive calls to GlobalAlloc(), we'll allocate
            // space for an additional 10 AudioCodec entries within
            // the pCodecs array each time we run out of space.
            //
#define nAudioCodecEntriesToAllocAtONCE   10

BOOL CALLBACK FillTreeFromMSACMQueryCallback (HACMDRIVERID hadid,
                                              DWORD_PTR dwUser,
                                              DWORD fdwSupport)
{
   short       ii;
   AudioCodec *pac;
   ACMDRIVERDETAILS add;

            // Find or create a place in which to store information
            // about this codec
            //
   for (ii = 0; ii < cCodecs; ++ii)
      {
      if (pCodecs[ ii ].dwPriority == 0)
         break;
      }
   if (ii >= cCodecs)
      {
        if (!REALLOC (pCodecs, cCodecs, 1+ii, nAudioCodecEntriesToAllocAtONCE))
            return FALSE;
      }
   pac = &pCodecs[ ii ];    // for shorthand

            // Find out about this codec
            //
   memset ((TCHAR *)&add, 0x00, sizeof(add));
   add.cbStruct = sizeof(add);
   if (acmDriverDetails (hadid, &add, 0) == MMSYSERR_NOERROR)
      {
      acmMetrics ((HACMOBJ)hadid,ACM_METRIC_DRIVER_PRIORITY,&pac->dwPriority);

      lstrcpy (pac->szDesc, add.szLongName);

      pac->wMid = add.wMid;
      pac->wPid = add.wPid;

      pac->pIDriver = NULL;
      }

   return TRUE; // keep counting
}


int __cdecl FillTreeFromMSACMSortCallback (const void *p1, const void *p2)
{
   if (((AudioCodec *)p1)->dwPriority == 0)
      return 1;
   if (((AudioCodec *)p2)->dwPriority == 0)
      return -1;
   return ((AudioCodec *)p1)->dwPriority - ((AudioCodec *)p2)->dwPriority;
}



/*
 * FillTreeFromRemaining - Adds tree items for all remaining MM devices
 *
 * This routine adds a single tree item for each entry in the aInstalledDrivers
 * array which is not already represented somewhere in the tree.  The
 * classification is based on the driver's alias--if that fails, it is lumped
 * under dcOTHER.
 *
 */

BOOL FillTreeFromRemaining (HWND hTree)
{
   mysize_t ii;

   for (ii = 0; ii < cInstalledDrivers; ++ii)
      {
      UINT iiSkipCheck;

      if (aInstalledDrivers[ ii ].pIDriver == NULL)
         continue;
      if (aInstalledDrivers[ ii ].pIDriver->szAlias[0] == TEXT('\0'))
         continue;

            // (don't do this for any not-to-be-displayed drivers)
            //
      for (iiSkipCheck = 0; iiSkipCheck < nDriversToSKIP; iiSkipCheck++)
         {
         if (!FileNameCmp ((LPTSTR)aDriversToSKIP[ iiSkipCheck ],
                           (LPTSTR)aInstalledDrivers[ ii ].pIDriver->szFile))
            break;
         }
      if (iiSkipCheck < nDriversToSKIP)
         continue;

            // Zip through the {drivers,drivers32,mci,mci32} sections, to
            // try to classify this driver. If we find a classification
            // for which we haven't already added an entry in the tree,
            // add another.
            //
      FillTreeFromRemainingBySection (hTree,
                                      ii,
                                      REGSTR_PATH_DRIVERS,
                                      dcINVALID);

      FillTreeFromRemainingBySection (hTree,
                                      ii,
                                      REGSTR_PATH_DRIVERS32,
                                      dcINVALID);

      FillTreeFromRemainingBySection (hTree,
                                      ii,
                                      REGSTR_PATH_MCI,
                                      dcMCI);

      FillTreeFromRemainingBySection (hTree,
                                      ii,
                                      REGSTR_PATH_MCI32,
                                      dcMCI);

            // If the dwBits element is zero, then this driver hasn't
            // already been assigned a treeitem elsewhere.  In that event,
            // call AddIDriver() with dcOTHER--to tell it to lump this
            // driver under "Other Drivers".
            //
      if (aInstalledDrivers[ ii ].dwBits == 0)
         {
         AddIDriver (hTree, aInstalledDrivers[ ii ].pIDriver, dcOTHER);
         }
      }

   return TRUE;
}


void FillTreeFromRemainingBySection (HWND hTree,
                                     long iiDriver,
                                     LPCTSTR pszSection,
                                     DriverClass dcSection)
{
   HKEY hk;
   UINT ii;

   if (RegOpenKey (HKEY_LOCAL_MACHINE, pszSection, &hk))
      return;

   for (ii = 0; ; ++ii)
      {
      TCHAR  szLHS[ cchRESOURCE ];
      TCHAR  szRHS[ cchRESOURCE ];
      DWORD  dw1;
      DWORD  dw2;
      DWORD  dw3;

      dw1 = cchRESOURCE;
      dw3 = cchRESOURCE;
      if (RegEnumValue (hk, ii,  szLHS, &dw1,
                        0, &dw2, (LPBYTE)szRHS, &dw3) != ERROR_SUCCESS)
         {
         break;
         }

      if (!FileNameCmp (szRHS, aInstalledDrivers[ iiDriver ].pIDriver->szFile))
         {
         DriverClass dc;

         if ((dc = dcSection) == dcINVALID)
            dc = GuessDriverClassFromAlias (szLHS);

         if ((dc == dcINVALID) || (dc == dcOTHER))
            continue;

         (void)AddIDriver (hTree, aInstalledDrivers[ iiDriver ].pIDriver, dc);
         }
      }

   RegCloseKey (hk);
}


#ifdef FIX_BUG_15451
HWND MakeThisCPLLookLikeTheOldCPL (HWND hWndCPL)
{
   TCHAR  szTitle[ cchRESOURCE ];
   HWND   hWndOldCPL = NULL;

   GetWindowText (hWndCPL, szTitle, cchRESOURCE);

   for (hWndOldCPL = GetWindow (hWndCPL, GW_HWNDFIRST);
        hWndOldCPL != NULL;
        hWndOldCPL = GetWindow (hWndOldCPL, GW_HWNDNEXT))
      {
      TCHAR  szTitleTest[ cchRESOURCE ];
      GetWindowText (hWndOldCPL, szTitleTest, cchRESOURCE);
      if ( (!lstrcmpi (szTitle, szTitleTest)) && (hWndCPL != hWndOldCPL) )
         {
         RECT  rOld;
         GetWindowRect (hWndOldCPL, &rOld);
         SetWindowPos (hWndCPL, hWndOldCPL, rOld.left, rOld.top,
                       0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
         SetWindowPos (hWndOldCPL, hWndCPL, 0, 0, 0, 0,
                       SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
         }
      }

    return hWndOldCPL;
}


HWND MakeThisDialogLookLikeTheOldDialog (HWND hDlg)
{
   TCHAR  szTitle[ cchRESOURCE ];
   RECT   rOld;
   POINT  pt;
   HWND   hWndOldDlg;

   GetWindowText (hDlg, szTitle, cchRESOURCE);

   for (hWndOldDlg = GetWindow (hDlg, GW_HWNDFIRST);
        hWndOldDlg != NULL;
        hWndOldDlg = GetWindow (hWndOldDlg, GW_HWNDNEXT))
      {
      TCHAR  szTitleTest[ cchRESOURCE ];
      GetWindowText (hWndOldDlg, szTitleTest, cchRESOURCE);
      if ( (!lstrcmpi (szTitle, szTitleTest)) && (hDlg != hWndOldDlg) )
         {
         GetWindowRect (hWndOldDlg, &rOld);

         SetWindowPos (hDlg, NULL, rOld.left, rOld.top, 0, 0,
                       SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

         return hWndOldDlg;
         }
      }

   return NULL;
}



BOOL WaitForNewCPLWindow (HWND hWndMyDlg)
{
   TCHAR  szTitle[ cchRESOURCE ];
   HWND   hWnd;
   DWORD  tickStart;

#define msecMAXWAIT   5000

   hWndMyDlg = GetParent (hWndMyDlg); // (hWndMyDlg was a property sheet)

   GetWindowText (hWndMyDlg, szTitle, cchRESOURCE);

   for (tickStart = GetTickCount();
        GetTickCount() - tickStart < msecMAXWAIT;)
      {
      MSG msg;

      for (hWnd = GetWindow (hWndMyDlg, GW_HWNDFIRST);
           hWnd != NULL;
           hWnd = GetWindow (hWnd, GW_HWNDNEXT))
         {
         TCHAR  szTitleTest[ cchRESOURCE ];
         if (!IsWindowVisible (hWnd))
            continue;
         GetWindowText (hWnd, szTitleTest, cchRESOURCE);
         if ( (!lstrcmpi (szTitle, szTitleTest)) && (hWnd != hWndMyDlg) )
            {
            PropSheet_PressButton (hWndMyDlg, PSBTN_CANCEL);
            hWnd = GetParent (GetParent (hAdvDlgTree));
            PropSheet_PressButton (hWnd, PSBTN_CANCEL);
            return TRUE;
            }
         }

      if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
         {
         if (GetMessage (&msg, NULL, 0, 0))
            {
            TranslateMessage (&msg);
            DispatchMessage (&msg);
            }
         }
      }

   return FALSE;
}
#endif // FIX_BUG_15451


        // Prevent any more REMOVE button presses
        // Otherwise one can get stacked up and cause trouble,
        // particularly if it is assocated with a driver that
        // is automatically removed.  We have to use a static
        // as any focus changes cause the button to change state.
        //
static long fWorking = 0;




/********************************************************************
 *
 *  AdvDlg ()
 *
 *  Display list of installed installable drivers.  Return TRUE/FALSE
 *  indicating if should restart windows.
 *
 ********************************************************************/

const static DWORD aAdvDlgHelpIds[] = {  // Context Help IDs
    IDC_ADV_TREE,    IDH_GENERIC_DEVICES,
    ID_ADV_PROP,     IDH_ADV_PROPERTIES,
    ID_ADV_REMOVE,   IDH_MMCPL_DEVPROP_REMOVE,
    0, 0
};

void MapDriverClass(DWORD_PTR dwSetupClass)
{
    g_dcFilterClass = dcINVALID;

    switch (dwSetupClass)
    {
        case IS_MS_MMMCI :
        {
            g_dcFilterClass = dcMCI;
        }
        break;

        case IS_MS_MMVID :
        {
            g_dcFilterClass = dcVCODEC;
        }
        break;

        case IS_MS_MMACM :
        {
            g_dcFilterClass = dcACODEC;
        }
        break;

        case IS_MS_MMVCD :
        {
            g_dcFilterClass = dcVIDCAP;
        }
        break;

        case IS_MS_MMDRV :
        {
            g_dcFilterClass = dcLEGACY;
        }
        break;
    }
}

INT_PTR AdvDlg (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    HANDLE          hWndI, hWnd;
    PIDRIVER        pIDriver;
    DWORD_PTR       dwType = 0;

    switch ( uMsg )
    {
        case WM_INITDIALOG:
#ifdef FIX_BUG_15451
        if (szDriverWhichNeedsSettings[0] != TEXT('\0'))
        {
            MakeThisCPLLookLikeTheOldCPL (GetParent(hDlg));
        }
#endif // FIX_BUG_15451

            wsStartWait();

            if (lParam)
            {
                dwType = ((LPPROPSHEETPAGE) lParam)->lParam;
            }

            MapDriverClass(dwType);

            hWndI = GetDlgItem(hDlg, IDC_ADV_TREE);
            SendMessage(hWndI,WM_SETREDRAW, FALSE, 0L);

            InitAdvDlgTree (hWndI); // initialize the treeview display

           /*
            *  Handle the fact that we may not be able to update our .ini
            *  sections
            *
            */

            IniFileWriteAllowed = CheckIniAccess();

                  // Note nasty sneaky hack: using (A|B) instead of (A&&B)
                  // makes both functions evaluate in either success or
                  // failure cases.
                  //
            IniFileReadAllowed = ( InitInstalled (hDlg, szDrivers) |
                                   InitInstalled (hDlg, szMCI) );

            FillTreeInAdvDlg (GetDlgItem (hDlg, IDC_ADV_TREE), NULL);

            wsEndWait();

            if ((!IniFileReadAllowed) || (!IniFileWriteAllowed))
            {
                TCHAR szCantAdd[120];
                EnableWindow(GetDlgItem(hDlg, ID_ADV_ADD),FALSE);
                EnableWindow(GetDlgItem(hDlg, ID_ADV_REMOVE),FALSE);
                LoadString(myInstance,IDS_CANTADD,szCantAdd,sizeof(szCantAdd)/sizeof(TCHAR));
                MessageBox(hDlg, szCantAdd, szError,
                                MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
            }


            SendMessage (hWndI, WM_SETREDRAW, TRUE, 0L);

            break;

        case WM_COMMAND:
            hWndI = GetDlgItem(hDlg, IDC_ADV_TREE);
            hWndMain = hDlg;

            pIDriver = GetSelectedIDriver (hWndI);

            switch ( LOWORD(wParam ))
            {
                case ID_ADV_PROP:
                    {
                HTREEITEM    htiCur = TreeView_GetSelection (hWndI);
                DriverClass  dc = GuessDriverClassFromTreeItem (htiCur);

                        if (fWorking)
                            break;

                        ++fWorking; // Just starting an operation

                        if( dc == dcJOY )
                            // We want to run "control joy.cpl" when the joystick devices
                            // are highlight and the user clicks Properties buttons.
                            RunJoyControlPanel();
            else
                        ShowDeviceProperties (hDlg, TreeView_GetSelection(hWndI));

                    --fWorking; // Finished with this operation
                    }
                    break;

                case ID_WHATSTHIS:
                    {
                    WinHelp((HWND)GetDlgItem (hDlg, IDC_ADV_TREE),
                            gszWindowsHlp, HELP_WM_HELP,
                           (UINT_PTR)(LPTSTR)aAdvDlgHelpIds);
                    }
                    break;

                case ID_ADV_REMOVE:
                    {
                    HWND         hTree = GetDlgItem (hDlg, IDC_ADV_TREE);
                    HTREEITEM    htiCur = TreeView_GetSelection (hTree);
                    DriverClass  dc = GuessDriverClassFromTreeItem (htiCur);
                    PIDRIVER     pid;
                    LONG_PTR         Status;

                    if ((!IniFileReadAllowed) || (!IniFileWriteAllowed))
                       break;   // (button should be disabled)

                    if( dc == dcJOY ) {
                         RunJoyControlPanel();
                break;
            }

                    if (TreeView_GetParent (hAdvDlgTree, htiCur) &&
                        TreeView_GetGrandParent (hAdvDlgTree, htiCur) &&
                        (GuessDriverClassFromTreeItem (
                                 TreeView_GetGrandParent (hAdvDlgTree, htiCur)
                                                      ) == dcMIDI))
                       {
                       TV_ITEM tvi;
                       PINSTRUM pin;

                       tvi.mask = TVIF_PARAM;
                       tvi.hItem = htiCur;
                       tvi.lParam = 0;
                       TreeView_GetItem(hAdvDlgTree, &tvi);

                       if ((pin = (PINSTRUM)tvi.lParam) != NULL)
                          {
                          RemoveInstrumentByKeyName (pin->szKey);
                          RefreshAdvDlgTree ();
                          KickMapper (hDlg);
                          }

                       break;
                       }

                    if ((pid = FindIDriverByTreeItem (htiCur)) == NULL)
                       break;

                    if (dc == dcLEGACY)
                    {
                        dc = GuessDriverClass(pid);
                    }

                    if (pid->szAlias[0] == TEXT('\0'))
                       {
                       TCHAR szCantRemove[ cchRESOURCE ];
                       GetString(szCantRemove, IDS_ACMREMOVEFAIL);
                       MessageBox(hDlg, szCantRemove, szError,
                                  MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
                       break;
                       }

                    if (fWorking)
                       break;
                    ++fWorking; // Just starting an operation

                    if (QueryRemoveDrivers (hDlg, pid->szAlias, pid->szDesc))
                       {
                       if ((Status = PostRemove (pid, TRUE)) != DRVCNF_CANCEL)
                          {
                          switch (dc)
                             {
                             case dcMIDI:
                                   break;
                             case dcACODEC:
                                   acmDeleteCodec (LOWORD(pid->lp),
                                                   HIWORD(pid->lp));
                                   break;
                             default:
                                   break;
                             }

                          iRestartMessage = IDS_RESTART_REM;

                          if (Status == DRVCNF_RESTART)
                             {
                             DialogBox (myInstance,
                                        MAKEINTRESOURCE(DLG_RESTART),
                                        hDlg,
                                        RestartDlg);
                             }
                          }
                       }

                    --fWorking; // Finished with this operation
                    }
                    break;

                case ID_ADV_ADD:
                    {
                HTREEITEM    htiCur = TreeView_GetSelection (hWndI);
                DriverClass  dc = GuessDriverClassFromTreeItem (htiCur);

                        if ((!IniFileReadAllowed) || (!IniFileWriteAllowed))
                            break;  // (button should be disabled)

                        if( dc == dcJOY ) {
                            RunJoyControlPanel();
                break;
            }

                    if (fWorking)
                       break;
                    ++fWorking; // Just starting an operation

                    bCopyEvenIfOlder = FALSE;

                    DialogBox(myInstance, MAKEINTRESOURCE(DLG_KNOWN), hDlg,
                              AvailableDriversDlg);

                    bCopyEvenIfOlder = FALSE;

                    --fWorking; // Finished with this operation
                    }
                    break;


                case ID_ADV_TSHOOT:
                    {
                        TCHAR szCommand[ MAX_PATH ];
                        STARTUPINFO si;
                        PROCESS_INFORMATION pi;
                        LoadString(myInstance,IDS_TSHOOT, szCommand, sizeof(szCommand)/sizeof(TCHAR));
                        ZeroMemory(&si, sizeof(si));
                        si.cb = sizeof(si);
                        si.dwFlags = STARTF_USESHOWWINDOW;
                        si.wShowWindow = SW_NORMAL;
                        if (CreateProcess(NULL, szCommand, NULL, NULL, FALSE, 0, 0, NULL, &si, &pi)) {
                            CloseHandle(pi.hThread);
                            CloseHandle(pi.hProcess);
                        }
                    }
                    break;

#ifdef FIX_BUG_15451
        case ID_INIT:
            if (szDriverWhichNeedsSettings[0] != TEXT('\0'))
               {
               HTREEITEM hti;

               if ((hti = FindTreeItemByDriverName (
                        szDriverWhichNeedsSettings)) != 0)
               {
               TreeView_Expand (hAdvDlgTree,
                        TreeView_GetParent(hAdvDlgTree,hti),
                        TVE_EXPAND);
               TreeView_SelectItem(hAdvDlgTree,hti);
               FORWARD_WM_COMMAND(hDlg,ID_ADV_PROP,0,0,PostMessage);
               }
               else
               {
               szDriverWhichNeedsSettings[0] = 0;
               }
               }
                    break;
#endif // FIX_BUG_15451

                default:
                    return(FALSE);
            }
            break;

      case WM_NOTIFY:
            {
            NMHDR         *lpnm   = (NMHDR *)lParam;
            LPNM_TREEVIEW  lpnmtv = (LPNM_TREEVIEW)lParam;

            switch (lpnm->code)
               {
               case PSN_KILLACTIVE:
                     FORWARD_WM_COMMAND (hDlg, IDOK, 0, 0, SendMessage);
                    break;

               case PSN_APPLY:
                     FORWARD_WM_COMMAND (hDlg, ID_APPLY, 0, 0, SendMessage);
                    break;

               case PSN_SETACTIVE:
                     FORWARD_WM_COMMAND (hDlg, ID_INIT, 0, 0, SendMessage);
                    break;

               case PSN_RESET:
                     FORWARD_WM_COMMAND (hDlg, IDCANCEL, 0, 0, SendMessage);
                    break;

               case NM_DBLCLK:
                              // show properties or expand/collapse tree node.
                              //
                     if (lpnm->idFrom == (UINT)IDC_ADV_TREE)
                        {
                        HWND           hTree =  GetDlgItem (hDlg, IDC_ADV_TREE);
                        HTREEITEM      htiCur = TreeView_GetSelection (hTree);
                        TV_HITTESTINFO tvht;

                        if (!htiCur)
                           break;

                        GetCursorPos (&tvht.pt);
                        ScreenToClient (hTree, &tvht.pt);
                        TreeView_HitTest (hTree, &tvht);

                        if ( (tvht.flags & TVHT_ONITEM) &&
                             (TreeView_GetChild (hTree, htiCur) == NULL) &&
                             (IsWindowEnabled (GetDlgItem(hDlg,ID_ADV_PROP))) )
                           {
                           FORWARD_WM_COMMAND(hDlg,ID_ADV_PROP,0,0,PostMessage);
                           }
                        }
                    break;

               case NM_RCLICK:
                     TreeContextMenu (hDlg, GetDlgItem (hDlg, IDC_ADV_TREE));
                     return TRUE;
                    break;
               }
            }
           break;


        // The TreeView has its own right-click handling, and presents a
        // "What's This?" automatically--so don't handle WM_CONTEXTMENU
        // for that control.
        //
        case WM_CONTEXTMENU:
            if (wParam != (WPARAM)GetDlgItem (hDlg, IDC_ADV_TREE))
            {
                WinHelp((HWND)wParam, gszWindowsHlp, HELP_CONTEXTMENU,
                       (UINT_PTR)(LPTSTR)aAdvDlgHelpIds);
            }
            break;

        case WM_HELP:
            WinHelp(((LPHELPINFO)lParam)->hItemHandle, gszWindowsHlp,
                  HELP_WM_HELP, (UINT_PTR)(LPTSTR)aAdvDlgHelpIds);
            break;

        case WM_DESTROY:
            FreeAdvDlgTree (GetDlgItem (hDlg, IDC_ADV_TREE));
            return FALSE;
            break;

        default:
            return FALSE;
            break;
    }
    return(TRUE);
}


/*
 *** TreeContextMenu
 *
 * This function displays the context menu that pops up when the
 * user right clicks on any of the tree view items.
 *
 */
void TreeContextMenu (HWND hWnd, HWND hKeyTreeWnd)
{
   DWORD MessagePos;
   POINT MessagePoint;
   TV_HITTESTINFO TVHitTestInfo;
   HMENU hContextMenu;
   HMENU hContextPopupMenu;
   TV_ITEM TVItem;
   int MenuCommand;
   TCHAR szCollapse[32];

            // dont bring up a menu unless click is on the item.
            //
   MessagePos = GetMessagePos();
   MessagePoint.x = GET_X_LPARAM(MessagePos);
   MessagePoint.y = GET_Y_LPARAM(MessagePos);

   TVHitTestInfo.pt = MessagePoint;
   ScreenToClient(hKeyTreeWnd, &TVHitTestInfo.pt);
   TVItem.hItem = TreeView_HitTest(hKeyTreeWnd, &TVHitTestInfo);

   if (TVItem.hItem == NULL)
      return;

   hContextMenu = LoadMenu(ghInstance, MAKEINTRESOURCE(POPUP_TREE_CONTEXT));
   if (hContextMenu == NULL)
      return;

   hContextPopupMenu = GetSubMenu (hContextMenu, 0);

   TVItem.mask = TVIF_STATE | TVIF_HANDLE | TVIF_CHILDREN | TVIF_PARAM;
   TreeView_GetItem(hKeyTreeWnd, &TVItem);

               // show collapse item because we are expanded?
               //
   if (TVItem.state & TVIS_EXPANDED)
      {
      LoadString(ghInstance, IDS_COLLAPSE, szCollapse, sizeof(szCollapse)/sizeof(TCHAR));
      ModifyMenu(hContextPopupMenu, ID_TOGGLE, MF_BYCOMMAND | MF_STRING,
                 ID_TOGGLE, szCollapse);
      }
   SetMenuDefaultItem (hContextPopupMenu, ID_TOGGLE, MF_BYCOMMAND);

   if (TVItem.cChildren == 0) //gray expand/collaps if no children
      {
      SetMenuDefaultItem(hContextPopupMenu, ID_ADV_PROP, MF_BYCOMMAND);
      EnableMenuItem(hContextPopupMenu, ID_TOGGLE, MF_GRAYED |MF_BYCOMMAND);
      }

   TreeView_SelectItem (hKeyTreeWnd, TVItem.hItem);
   MenuCommand = TrackPopupMenuEx (hContextPopupMenu,
                                   TPM_RETURNCMD | TPM_RIGHTBUTTON |
                                   TPM_LEFTALIGN | TPM_TOPALIGN,
                                   MessagePoint.x, MessagePoint.y,
                                   hWnd, NULL);

   DestroyMenu (hContextMenu);
   FORWARD_WM_COMMAND(hWnd, MenuCommand, 0, 0, SendMessage);
}



/*--------------------------------------------------------------------------*
 *                                                                          *
 *                                                                          *
 *  LB_AVAILABLE Dialog Routines                                            *
 *                                                                          *
 *                                                                          *
 *--------------------------------------------------------------------------*/

/*
 *  DLG: LB_AVAILABLE
 *
 *  InitAvailable()
 *
 *  Add the available drivers from mmdriver.inf to the passed list box.
 *  The format of [Installable.drivers] in setup.inf is:
 *  profile=disk#:driverfile,"type1,type2","Installable driver Description","vxd1.386,vxd2.386","opt1,2,3"
 *
 *  for example:
 *
 *  driver1=6:sndblst.drv,"midi,wave","SoundBlaster MIDI and Waveform drivers","vdmad.386,vadmad.386","3,260"
 */

BOOL InitAvailable(HWND hWnd, int iLine)
{
    PINF    pinf;
    BOOL    bInitd=FALSE;
    LPTSTR    pstrKey;
    int     iIndex;
    TCHAR    szDesc[MAX_INF_LINE_LEN];

    SendMessage(hWnd,WM_SETREDRAW, FALSE, 0L);

   /*
    *  Parse the list of keywords and load their strings
    */

    for (pinf = FindInstallableDriversSection(NULL); pinf; pinf = infNextLine(pinf))
    {
        //
        // found at least one keyname!
        //
        bInitd = TRUE;
        if ( (pstrKey = (LPTSTR)LocalAlloc(LPTR, MAX_SYS_INF_LEN)) != NULL )
                infParseField(pinf, 0, pstrKey);
        else
            break;
       /*
        *  add the installable driver's description to listbox, and filename as data
        */

        infParseField(pinf, 3, szDesc);

        if ( (iIndex = (int)SendMessage(hWnd, LB_ADDSTRING, 0, (LONG_PTR)(LPTSTR)szDesc)) != LB_ERR )

            SendMessage(hWnd, LB_SETITEMDATA, iIndex, (LONG_PTR)pstrKey);

    }

    if (iLine == UNLIST_LINE)
    {
        //
        // Add the "Install unlisted..." choice to the top of the list
        // box.
        LoadString(myInstance, IDS_UPDATED, szDesc, sizeof(szDesc)/sizeof(TCHAR));
        if ((iIndex = (int)(LONG)SendMessage(hWnd, LB_INSERTSTRING, 0, (LPARAM)(LPTSTR)szDesc)) != LB_ERR)
            SendMessage(hWnd, LB_SETITEMDATA, (WPARAM)iIndex, (LPARAM)0);
     }
     if (bInitd)

         SendMessage(hWnd, LB_SETCURSEL, 0, 0L );


     SendMessage(hWnd,WM_SETREDRAW, TRUE, 0L);
     return(bInitd);
}


/*
 *  DLG: LB_AVAILABLE
 *
 *  RemoveAvailable()
 *
 *  Remove all drivers from the listbox and free all storage associated with
 *  the keyname
 */

void RemoveAvailable(HWND hWnd)
{
    int iIndex;
    HWND hWndA;
    LPTSTR pstrKey;

    hWndA = GetDlgItem(hWnd, LB_AVAILABLE);
    iIndex = (int)SendMessage(hWndA, LB_GETCOUNT, 0, 0L);
    while ( iIndex-- > 0)
    {
        if (( (pstrKey = (LPTSTR)SendMessage(hWndA, LB_GETITEMDATA, iIndex,
            0L)) != (LPTSTR)LB_ERR ) && pstrKey)
            LocalFree((HLOCAL)pstrKey);
    }
}


/*
 *  DLG: LB_AVAILABLE
 *
 *  AvailableDriversDlg()
 *
 *  List the available installable drivers or return FALSE if there are none.
 */

const static DWORD aAvailDlgHelpIds[] = {  // Context Help IDs
    LB_AVAILABLE,    IDH_ADD_DRIVER_LIST,
    ID_DRVSTRING,    IDH_ADD_DRIVER_LIST,

    0, 0
};

INT_PTR AvailableDriversDlg(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LPTSTR    pstrKey;    //-jyg- added

    HWND    hWndA;
    int     iIndex;

    switch ( uMsg )
    {
        case WM_INITDIALOG:
            ShowWindow(hWnd, TRUE);
            wsStartWait();
            if (pinfOldDefault)
            {
                infSetDefault(pinfOldDefault);
                pinfOldDefault = NULL;
            }

            if ( !InitAvailable(hWndA = GetDlgItem(hWnd, LB_AVAILABLE), UNLIST_LINE))
            {
               /*
                *  We weren't able to find the [installable.drivers] section
                *  of the
                *  mmdriver.inf OR it was corrupt.  Go ahead and query the
                *  user to find an oemsetup.inf to make our default.  This
                *  is a bad state.
                */
                EndDialog(hWnd, FALSE);
                bFindOEM = TRUE;
                wcscpy(szDrv, szOemInf);
                if (DialogBox(myInstance, MAKEINTRESOURCE(DLG_INSERTDISK),
                        hWnd,  AddDriversDlg) == TRUE)
                    PostMessage(hWnd, WM_INITDIALOG, 0, 0L);
                else
                    pinfOldDefault = infSetDefault(pinfOldDefault);

                bFindOEM = FALSE;
            }
            wsEndWait();
            break;

        case WM_COMMAND:

            switch ( LOWORD(wParam ))
            {
                case LB_AVAILABLE:

                    // Hm... We've picked it.

                    if ( HIWORD(wParam) == LBN_DBLCLK )
                        SendMessage(hWnd, WM_COMMAND, IDOK, 0L);
                    break;

                case IDOK:

                   /*
                    *  We've made our selection
                    */

                    hWndA = GetDlgItem(hWnd, LB_AVAILABLE);

                    if ( (iIndex = (int)SendMessage(hWndA, LB_GETCURSEL, 0, 0L)) != LB_ERR)
                    {
                        if (!iIndex)
                        {
                           /*
                            *  The first entry is for OEMs
                            */

                            INT_PTR iFound;
                            bBadOemSetup = FALSE;

                            bCopyEvenIfOlder = TRUE;
                            bFindOEM = TRUE;
                            hMesgBoxParent = hWnd;
                            while ((iFound = DialogBox(myInstance,
                                    MAKEINTRESOURCE(DLG_INSERTDISK), hWnd,
                                            AddDriversDlg)) == 2);
                            if (iFound == 1)
                            {
                                    RemoveAvailable(hWnd);
                                    SendDlgItemMessage(hWnd, LB_AVAILABLE,
                                            LB_RESETCONTENT, 0, 0L);
                                    PostMessage(hWnd, WM_INITDIALOG, 0, 0L);
                            }
                            bFindOEM = FALSE;
                        }
                        else
                        {
                           /*
                            *  The user selected an entry from our .inf
                            */

                            wsStartWait();

                           /*
                            *  The  data associated with the list item is
                            *  the driver key name (field 0 in the inf file).
                            */

                            pstrKey = (LPTSTR)SendMessage(hWndA, LB_GETITEMDATA, iIndex, 0L);
                            bCopyingRelated = FALSE;
                            bQueryExist = TRUE;

                            if (InstallDrivers(hWndMain, hWnd, pstrKey))
                            {
                               RefreshAdvDlgTree ();
                               wsEndWait();


                              /*
                               *  If bRestart is true then the system must
                               *  be restarted to activate these changes
                               */

                               if (bRestart)
                               {
                                  iRestartMessage= IDS_RESTART_ADD;
                                  DialogBox(myInstance,
                                          MAKEINTRESOURCE(DLG_RESTART), hWnd,
                                              RestartDlg);
                               }
                            }
                            else
                               wsEndWait();

                            bRestart = FALSE;
                            bRelated = FALSE;
                        }
                    }
                    EndDialog(hWnd, FALSE);
                    break;

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

                default:
                    return(FALSE);
            }
            break;

        case WM_CONTEXTMENU:
            WinHelp((HWND)wParam, gszWindowsHlp, HELP_CONTEXTMENU,
                  (UINT_PTR)(LPTSTR)aAvailDlgHelpIds);
            break;

        case WM_HELP:
            WinHelp(((LPHELPINFO)lParam)->hItemHandle, gszWindowsHlp,
                  HELP_WM_HELP, (UINT_PTR)(LPTSTR)aAvailDlgHelpIds);
            break;

        case WM_DESTROY:
            //
            // free the strings added as DATAITEM to the avail list

            RemoveAvailable(hWnd);
            return(FALSE);

        default:
            return FALSE;
         break;
    }
    return(TRUE);
}


BOOL DriversDllInitialize( IN PVOID hInstance
                  , IN DWORD ulReason
                  , IN PCONTEXT pctx OPTIONAL
                  )
{
    if (ulReason != DLL_PROCESS_ATTACH)
        return TRUE;

    myInstance = hInstance;
    LoadString(myInstance, IDS_CLOSE,  aszClose, sizeof(aszClose)/sizeof(TCHAR));
    LoadString(myInstance, IDS_DRIVERDESC, szDriversDesc, sizeof(szDriversDesc)/sizeof(TCHAR));
    LoadString(myInstance, IDS_FILE_ERROR, szFileError, sizeof(szFileError)/sizeof(TCHAR));
    LoadString(myInstance, IDS_INSTALLDRIVERS, szMDrivers, sizeof(szMDrivers)/sizeof(TCHAR));
    LoadString(myInstance, IDS_INSTALLDRIVERS32, szMDrivers32, sizeof(szMDrivers)/sizeof(TCHAR));
    LoadString(myInstance, IDS_RELATEDDESC, szRelatedDesc, sizeof(szRelatedDesc)/sizeof(TCHAR));
    LoadString(myInstance, IDS_USERINSTALLDRIVERS, szUserDrivers, sizeof(szUserDrivers)/sizeof(TCHAR));
    LoadString(myInstance, IDS_UNLISTED, (LPTSTR)szUnlisted, sizeof(szUnlisted)/sizeof(TCHAR));
    LoadString(myInstance, IDS_KNOWN, szKnown, sizeof(szKnown)/sizeof(TCHAR));
    LoadString(myInstance, IDS_OEMSETUP, szOemInf, sizeof(szOemInf)/sizeof(TCHAR));
    LoadString(myInstance, IDS_SYSTEM, szSystem, sizeof(szSystem)/sizeof(TCHAR));
    LoadString(myInstance, IDS_OUT_OF_REMOVE_SPACE, szOutOfRemoveSpace, sizeof(szOutOfRemoveSpace)/sizeof(TCHAR));
    LoadString(myInstance, IDS_NO_DESCRIPTION, szNoDesc, sizeof(szNoDesc)/sizeof(TCHAR));
    LoadString(myInstance, IDS_ERRORBOX, szError, sizeof(szError)/sizeof(TCHAR));
    LoadString(myInstance, IDS_REMOVEORNOT, szRemoveOrNot, sizeof(szRemoveOrNot)/sizeof(TCHAR));
    LoadString(myInstance, IDS_REMOVEORNOTSTRICT, szRemoveOrNotStrict, sizeof(szRemoveOrNotStrict)/sizeof(TCHAR));
    LoadString(myInstance, IDS_SETUPINF, szSetupInf, sizeof(szSetupInf)/sizeof(TCHAR));
    LoadString(myInstance, IDS_APPNAME, szAppName, sizeof(szAppName)/sizeof(TCHAR));

    LoadString(myInstance, IDS_DRIVERS, szDrivers, sizeof(szDrivers)/sizeof(TCHAR));
    LoadString(myInstance, IDS_REMOVE, szRemove, sizeof(szRemove)/sizeof(TCHAR));
    LoadString(myInstance, IDS_CONTROLINI, szControlIni, sizeof(szControlIni)/sizeof(TCHAR));
    LoadString(myInstance, IDS_SYSINI, szSysIni, sizeof(szSysIni)/sizeof(TCHAR));
    LoadString(myInstance, IDS_MCI, szMCI, sizeof(szMCI)/sizeof(TCHAR));
    LoadString(myInstance, IDS_DEFDRIVE, szDirOfSrc, sizeof(szDirOfSrc)/sizeof(TCHAR));
    LoadString(myInstance, IDS_CONTROL_HLP_FILE, szDriversHlp, sizeof(szDriversHlp)/sizeof(TCHAR));
    LoadString(myInstance, IDS_LASTQUERY, szLastQuery, sizeof(szLastQuery)/sizeof(TCHAR));

    return TRUE;
}

void DeleteCPLCache(void)
{
    HKEY hKeyCache;

    if (ERROR_SUCCESS ==
        RegOpenKey(HKEY_CURRENT_USER,
                   TEXT("Control Panel\\Cache\\multimed.cpl"),
                   &hKeyCache)) {
        for ( ; ; ) {
            TCHAR Name[50];

            if (ERROR_SUCCESS ==
                RegEnumKey(hKeyCache,
                           0,
                           Name,
                           sizeof(Name)/sizeof(TCHAR))) {
                HKEY hSubKey;

                RegDeleteKey(hKeyCache, Name);
            } else {
                break;    // leave loop
            }
        }

        RegDeleteKey(hKeyCache, NULL);
        RegCloseKey(hKeyCache);
    }
}


/*
** RestartDlg()
**
** Offer user the choice to (not) restart windows.
*/
INT_PTR RestartDlg(HWND hDlg, unsigned uiMessage, WPARAM wParam, LPARAM lParam)
{
    switch (uiMessage)
    {
        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
               case IDCANCEL:
                    //
                    // don't restart windows
                    //
                    EndDialog(hDlg, FALSE);
                    break;

                case IDOK:
                    //
                    // do restart windows, *dont* dismiss dialog incase
                    // the user canceled it.
                    //
                    ReBoot(hDlg);
                    SetActiveWindow(hDlg);
                    //EndDialog(hDlg, TRUE);
                    break;

                default:
                    return FALSE;
            }
            return TRUE;

        case WM_INITDIALOG:
              /*
              **  Delete the control panel's cache so it will get it
              **  right!
              */

              DeleteCPLCache();


              if (iRestartMessage)
              {
                TCHAR szMesg1[300];
                TCHAR szMesg2[300];

                LoadString(myInstance, iRestartMessage, szMesg1, sizeof(szMesg1)/sizeof(TCHAR));
                wsprintf(szMesg2, szMesg1, (LPTSTR)szRestartDrv);
                SetDlgItemText(hDlg, IDS_RESTARTTEXT, (LPTSTR)szMesg2);

                if (iRestartMessage == IDS_RESTART_NOSOUND)
                {
                  PostMessage (hDlg, WM_NEXTDLGCTL,
                               (WPARAM)GetDlgItem(hDlg,IDOK), (LPARAM)TRUE);
                }
              }
              return TRUE;

        case WM_KEYUP:
            if (wParam == VK_F3)
                //
                // don't restart windows
                //
                EndDialog(hDlg, FALSE);
            break;

        default:
            break;
    }
    return FALSE;
}

/*
 * UserInstalled()
 *
 *
 */

BOOL UserInstalled(LPTSTR szKey)
{
        TCHAR buf[MAXSTR];
        LPTSTR lpstr = NULL;
        ZeroMemory (buf, sizeof (buf)); // make prefix happy.

        lpstr = GetProfile (szUserDrivers, (LPTSTR)szKey, szControlIni, buf, sizeof(buf));
        if (lpstr && *lpstr != TEXT('\0'))
            return(TRUE);
        else
            return(FALSE);
}

/*
 *   AddUnlistedDlg()
 *
 *   The following function processes requests by the user to install unlisted
 *   or updated drivers.
 *
 *   PARAMETERS:  The normal Dialog box parameters
 *   RETURN VALUE:  The usual Dialog box return value
 */

INT_PTR AddUnlistedDlg(HWND hDlg, unsigned nMsg, WPARAM wParam, LPARAM lParam)
{
  switch (nMsg)
  {
      case WM_INITDIALOG:
      {
          HWND hListDrivers;
          BOOL bFoundDrivers;

          wsStartWait();
          hListDrivers = GetDlgItem(hDlg, LB_UNLISTED);

          /* Search for drivers */
          bFoundDrivers = InitAvailable(hListDrivers, NO_UNLIST_LINE);
          if (!bFoundDrivers)
          {
                //
                // We weren't able to find the MMDRIVERS section of the
                // setup.inf OR it was corrupt.  Go ahead and query the
                // user to find an oemsetup.inf to make our default.  This
                // is a bad state.
                //

                INT_PTR iFound;

                bFindOEM = TRUE;
                bBadOemSetup = TRUE;
                while ((iFound = DialogBox(myInstance,
                        MAKEINTRESOURCE(DLG_INSERTDISK), hMesgBoxParent,
                                AddDriversDlg)) == 2);
                bFindOEM = FALSE;
                if (iFound == 1)
                {
                        SendDlgItemMessage(hDlg, LB_AVAILABLE,
                                LB_RESETCONTENT, 0, 0L);
                        PostMessage(hDlg, WM_INITDIALOG, 0, 0L);
                }
                EndDialog(hDlg, FALSE);
          }
          SendMessage(hListDrivers, LB_SETCURSEL, 0, 0L);
          wsEndWait();

          break;
        }

      case WM_COMMAND:
        switch (LOWORD(wParam))
        {
            case IDH_DLG_ADD_UNKNOWN:
              goto DoHelp;

            case LB_UNLISTED:
              if (HIWORD(wParam) != LBN_DBLCLK)
                  break;

              // else Fall through here
            case IDOK:
            {
             HWND hWndA;
             int iIndex;
             LPTSTR pstrKey;

             hWndA = GetDlgItem(hDlg, LB_UNLISTED);
             if ( (iIndex = (int)SendMessage(hWndA, LB_GETCURSEL, 0, 0L))
                                                             != LB_ERR)
             {
                wsStartWait();
                pstrKey = (LPTSTR)SendMessage(hWndA, LB_GETITEMDATA, iIndex, 0L);
                bCopyingRelated = FALSE;
                bQueryExist = TRUE;
                if (InstallDrivers(hWndMain, hDlg, pstrKey))
                {
                   RefreshAdvDlgTree ();
                   wsEndWait();

                   if (bRestart)
                   {
                      iRestartMessage= IDS_RESTART_ADD;
                      DialogBox(myInstance,   MAKEINTRESOURCE(DLG_RESTART),
                                                      hDlg, RestartDlg);
                   }
                 }
                 else
                   wsEndWait();
                 bRelated = FALSE;
                 bRestart = FALSE;
              }
              EndDialog(hDlg, FALSE);
            }
            break;

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

            default:
              return FALSE;
          }
        break;

      case WM_HELP:
DoHelp:
        WinHelp (hDlg, gszWindowsHlp, HELP_CONTEXT, IDH_MMCPL_DEVPROP_ENABLE);
        break;

      default:
        return FALSE;
   }
   return TRUE;
}
/*
 *  ReBoot()
 *
 *  Restart the system.  If this fails we put up a message box
 */

 void ReBoot(HWND hDlg)
 {
     DWORD Error;
     BOOLEAN WasEnabled;

    /*
     *  We must adjust our privilege level to be allowed to restart the
     *  system
     */

     RtlAdjustPrivilege( SE_SHUTDOWN_PRIVILEGE,
                         TRUE,
                         FALSE,
                         &WasEnabled
                       );
    /*
     *  Try to reboot the system
     */

     if (!ExitWindowsEx(EWX_REBOOT, 0xFFFFFFFF)) {

         Error = GetLastError();

        /*
         *  Put up a message box if we failed
         */

         if (Error != NO_ERROR) {
            TCHAR szCantRestart[80];
            LoadString(myInstance,
                       Error == ERROR_PRIVILEGE_NOT_HELD  ||
                       Error == ERROR_NOT_ALL_ASSIGNED  ||
                       Error == ERROR_ACCESS_DENIED ?
                           IDS_CANNOT_RESTART_PRIVILEGE :
                           IDS_CANNOT_RESTART_UNKNOWN,
                       szCantRestart,
                       sizeof(szCantRestart)/sizeof(TCHAR));

            MessageBox(hDlg, szCantRestart, szError,
                       MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
         }
     }
 }


void OpenDriverError(HWND hDlg, LPTSTR szDriver, LPTSTR szFile)
{
        TCHAR szMesg[MAXSTR];
        TCHAR szMesg2[MAXSTR];

        LoadString(myInstance, IDS_INSTALLING_DRIVERS, szMesg, sizeof(szMesg)/sizeof(TCHAR));
        wsprintf(szMesg2, szMesg, szDriver, szFile);
        MessageBox(hDlg, szMesg2, szError, MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);

}


/*
 *** AddIDriver - Adds a treeitem referencing the given PIDRIVER
 *
 * Note that the listed PIDRIVER should already have been added to the
 * aInstalledDrivers array (via AddIDriverToArray()) before calling this
 * routine.
 *
 */

HTREEITEM AddIDriver (HWND hTree, PIDRIVER pIDriver, DriverClass dc)
{
   short           idr;
   TV_INSERTSTRUCT ti;
   HTREEITEM       hti;
   short           ii;
   TCHAR           szFile[ _MAX_FNAME +1 +_MAX_EXT +1 ];
   TCHAR           szExt[ _MAX_EXT +1 ];
   TCHAR           szDesc[ cchRESOURCE ];

            // don't add an entry for one of the to-be-skipped drivers
            //
   lsplitpath (pIDriver->szFile, NULL, NULL, szFile, szExt);

   if (szExt[0] != TEXT('\0'))
      lstrcat (szFile, szExt);

    //check to see if we're trying to put a PNP driver into the legacy tree
    if (g_dcFilterClass == dcLEGACY)
    {
        if ((dc == dcWAVE) ||
            (dc == dcMIDI) ||
            (dc == dcMIXER) ||
            (dc == dcAUX))
        {
            if (IsPnPDriver(szFile))
            {
                return FALSE;
            }
        }
    }

   if (dc != dcMIDI)
      {
      for (ii = 0; ii < nDriversToSKIP; ii++)
         {
         if (!lstrcmpi (szFile, aDriversToSKIP[ ii ]))
            return FALSE;
         }
      }

            // If we were given a DriverClass, then the caller has
            // specified where we should create an entry--add the "Audio for"
            // (etc) tag before the description, and add it.
            //
            // Otherwise, determine where this driver belongs in the tree
            //
   if (dc != dcINVALID)
      {
      TCHAR   szTag[ cchRESOURCE ];

      switch (dc)
         {
         case dcWAVE:   GetString (szTag, IDS_AUDIOFOR);
                       break;
         case dcMIDI:   GetString (szTag, IDS_MIDIFOR);
                       break;
         case dcMIXER:  GetString (szTag, IDS_MIXERFOR);
                       break;
         case dcAUX:    GetString (szTag, IDS_AUXFOR);
                       break;
         default:       lstrcpy (szTag, TEXT("%s"));
                       break;
         }

      wsprintf (szDesc, szTag, pIDriver->szDesc);
      }
   else
      {
      if ((dc = GuessDriverClass (pIDriver)) == dcINVALID)
         return FALSE;

      lstrcpy (szDesc, pIDriver->szDesc);
      }

            // map that classification into an index within the
            // root entries of the tree (aDriverRoot[])
            //
   if ((idr = DriverClassToRootIndex (dc)) == -1)
      return FALSE;

            // if this driver already has an entry under this DriverClass,
            // then don't add another.
            //
   for (ii =0; ii < cInstalledDrivers; ++ii)
      {
      if (aInstalledDrivers[ ii ].pIDriver == pIDriver)
         break;
      }
   if (ii >= cInstalledDrivers)
      {
      ii = (short)NOPIDRIVER;
      }
   else if (aInstalledDrivers[ ii ].dwBits & aDriverRoot[ idr ].dwBit)
      {
      return FALSE; // Already have an entry here!
      }

            // since not all roots need exist all the time, make sure
            // this classification HAS a root in the tree
            //
   if (!EnsureRootIndexExists (hTree, idr))
      return FALSE;

            // finally, insert an item into the tree for this driver
            // note that for audio codecs to be sorted properly, they must
            // be added via this routine in their appropriate order--ie,
            // call this routine for the highest-priority codec first.
            //
   ti.hParent = aDriverRoot[ idr ].hti;
   ti.hInsertAfter = (dc == dcACODEC) ? TVI_LAST : TVI_SORT;
   ti.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
   ti.item.iImage = (int)idr;
   ti.item.iSelectedImage = (int)idr;
   ti.item.pszText = szDesc;
   ti.item.lParam = ii;

   if ((hti = TreeView_InsertItem (hTree, &ti)) == NULL)
      return FALSE;

   if (ii != NOPIDRIVER)
      aInstalledDrivers[ ii ].dwBits |= aDriverRoot[ idr ].dwBit;

   return hti;
}


BOOL AddIDriverByName (HWND hTree, LPCWSTR wszFile, DriverClass dc)
{
   LPTSTR pch;
   TCHAR tszFile[ max(cchRESOURCE, MAX_PATH) ];
   PIDRIVER pid;

#ifdef UNICODE
   lstrcpy (tszFile, wszFile);
#else
   wcstombs (tszFile, wszFile, cchRESOURCE);
#endif

            // Strip off any trailing whitespace
            //
   if (tszFile[0] == TEXT('\0'))
      return FALSE;

   for (pch = &tszFile[ lstrlen(tszFile)-1 ];
        pch >= tszFile && (*pch == TEXT('\t') || *pch == TEXT(' '));
        --pch)
      ;
   *(1+pch) = TEXT('\0');

            // If this is MMDRV.DLL, then it's possibly providing the
            // user-mode component for kernel-mode drivers.  Since it's
            // apparently impossible to determine the name of the .SYS
            // file which is providing a "\\.\WaveIn0" device (etc),
            // we'll use a hack: Check around for anyone registered
            // under the alias "Kernel", and use that.
            //
   if (!lstrcmpi (tszFile, cszMMDRVDLL))
      {
      mysize_t ii;
      for (ii = 0; ii < cInstalledDrivers; ++ii)
         {
         if (aInstalledDrivers[ ii ].pIDriver == NULL)
            continue;
         if (!lstrnicmp (aInstalledDrivers[ ii ].pIDriver->szAlias,
                         cszAliasKERNEL,
                         lstrlen(cszAliasKERNEL)))
            {
            lstrcpy (tszFile, aInstalledDrivers[ ii ].pIDriver->szFile);
            break;
            }
         }
      if (ii >= cInstalledDrivers)
         return FALSE;
      }

            // Find the driver in the aInstalledDriver array, and add
            // an entry for it in the tree.
            //
   if ((pid = FindIDriverByName (tszFile)) == NULL)
      return FALSE;

   if (AddIDriver (hTree, pid, dc) == NULL)
      return FALSE;

   return TRUE;
}


/*
 *** RemoveIDriver - Removes (and optionally frees) an IDRIVER from hAdvDlgTree
 *
 */

void RemoveIDriver (HWND hTree, PIDRIVER pIDriver, BOOL fFreeToo)
{
   mysize_t   ii;
   short      idr;
   HTREEITEM  hti;

            // Find each TreeItem which references this entry.
            //
   for (idr = 0; idr < nDriverROOTS; idr++)
      {
      if ((hti = aDriverRoot[ idr ].hti) == NULL)
         continue;

      hti = TreeView_GetChild (hTree, hti);
      while (hti != NULL)
         {
         if (pIDriver != FindIDriverByTreeItem (hti))
            {
            hti = TreeView_GetNextSibling (hTree, hti);
            continue;
            }

         // We found a tree item which uses this driver, so delete the
         // item.  Also note that this may cause the driver's parent
         // node to no longer be necessary.
         //

         TreeView_DeleteItem (hTree, hti);
         hti = TreeView_GetChild (hTree, aDriverRoot[ idr ].hti);

         if (!aDriverRoot[ idr ].fAlwaysMake)   // may no longer need parent?
            {
            if (hti == NULL)                    // parent now has no children?
               {
               TreeView_DeleteItem (hTree, aDriverRoot[ idr ].hti);
               aDriverRoot[ idr ].hti = NULL;
               }
            }
         }
      }

            // See if we can find the given pIDriver within the
            // aInstalledDriver array.
            //
   for (ii = 0; ii < cInstalledDrivers; ++ii)
      {
      if (aInstalledDrivers[ ii ].pIDriver == pIDriver)
         {
         aInstalledDrivers[ ii ].dwBits = 0L;   // no longer in tree at all

         if (fFreeToo)
            {
            LocalFree ((HANDLE)aInstalledDrivers[ ii ].pIDriver);
            aInstalledDrivers[ ii ].pIDriver = NULL;
            }

         break;   // There's only one entry in this array for each pIDriver
         }
      }
}


#ifdef FIX_BUG_15451
HTREEITEM FindTreeItemByDriverName (LPTSTR pszName)
{
   PIDRIVER   pid;
   short      idr;
   HTREEITEM  hti;

   if ((pid = FindIDriverByName (pszName)) == NULL)
      return (HTREEITEM)0;

   for (idr = 0; idr < nDriverROOTS; idr++)
      {
      if ((hti = aDriverRoot[ idr ].hti) == NULL)
         continue;

      for (hti = TreeView_GetChild (hAdvDlgTree, hti);
           hti != NULL;
           hti = TreeView_GetNextSibling (hAdvDlgTree, hti))
         {
         if (pid == FindIDriverByTreeItem (hti))
            {
            return hti;
            }
         }
     }

   return (HTREEITEM)0;
}
#endif // FIX_BUG_15451


PIDRIVER FindIDriverByTreeItem (HTREEITEM hti)
{
   TV_ITEM tvi;

   tvi.mask = TVIF_PARAM;
   tvi.hItem = hti;
   TreeView_GetItem (hAdvDlgTree, &tvi);

   if ( (tvi.lParam < 0) ||
        (tvi.lParam >= cInstalledDrivers) )
      {
      return NULL;
      }

   return aInstalledDrivers[ tvi.lParam ].pIDriver;
}


/*
 *** FindIDriverByName - Returns the first found IDRIVER structure with a name
 *
 */

PIDRIVER FindIDriverByName (LPTSTR szFile)
{
   mysize_t ii;

   for (ii = 0; ii < cInstalledDrivers; ++ii)
      {
      if (aInstalledDrivers[ ii ].pIDriver == NULL)
         continue;
      if (aInstalledDrivers[ ii ].pIDriver->szAlias[0] == TEXT('\0'))
         continue;

      if (!FileNameCmp (aInstalledDrivers[ ii ].pIDriver->szFile, szFile))
         return aInstalledDrivers[ ii ].pIDriver;
      }

    return NULL;
}


PIDRIVER FindIDriverByResource (PIRESOURCE pir)
{
   return FindIDriverByName (FileName( pir->szFile ));
}


/*
 *** GetSelectedIDriver - Returns the IDRIVER structure the user has selected
 *
 */

PIDRIVER GetSelectedIDriver (HWND hTree)
{
   HTREEITEM  htiCur = TreeView_GetSelection (hTree);

   if (htiCur == NULL)
      return NULL;

   return FindIDriverByTreeItem (htiCur);
}


/*
 *** DriverClassToRootIndex - obtain idr for which {aDriverRoot[idr].dc == dc}
 *
 * The array index for aDriverRoot[] is NOT a DriverClass--that is,
 * aDriverRoot[ PickAnyDC ].dc is not necessarily equal to PickAnyDC.
 * Given a DC, this routine finds the index into aDriverRoot which references
 * that DC.
 *
 */

short DriverClassToRootIndex (DriverClass dc)
{
   short  idr;

   for (idr = 0; idr < nDriverROOTS; idr++)
      {
      if (aDriverRoot[ idr ].dc == dc)
         return idr;
      }

   return -1;
}


/*
 *** GetDriverClass - guess a DriverClass based on an IDRIVER structure
 *
 * The registry has several different aliases which it uses in the LHS
 * of HKLM\software\microsoft\windowsnt\drivers,drivers32,etc to indicate
 * the classification of a particular driver.  These include:
 *
 * AUX, MIDI, MIDIMAPPER, MIXER, MSACM.*, VIDC.* WAVE, WAVEMAPPER
 *
 * as well as others--the full array of known entries is tracked within
 * aDriverKeywords[].  In addition, any of these may be followed by a
 * string of digits by which they are distinguished.  This routine parses
 * these keywords and returns a corresponding DriverClass enum.
 *
 */

DriverClass GuessDriverClass (PIDRIVER pid)
{
#ifdef FIX_BUG_15451
   return GuessDriverClassFromAlias (pid->szAlias);
}



DriverClass GuessDriverClassFromAlias (LPTSTR pszAlias)
{
#endif // FIX_BUG_15451
   TCHAR   szAlias[ cchRESOURCE ];
   TCHAR  *pch;
   short   ii;

#ifdef FIX_BUG_15451
   lstrcpy (szAlias, pszAlias); // Make a local copy so we can munge it
#else // FIX_BUG_15451
   lstrcpy (szAlias, pid->szAlias); // Make a local copy so we can munge it
#endif // FIX_BUG_15451

   if ((pch = lstrchr (szAlias, TEXT('.'))) != NULL)
      *pch = TEXT('0');

   for (ii = 0; ii < nDriverKEYWORDS; ii++)
      {
      if (!lstrnicmp (szAlias,
                      aDriverKeyword[ii].psz,
                      lstrlen (aDriverKeyword[ii].psz)))
         {
         return aDriverKeyword[ii].dc;
         }
      }

   return dcOTHER;
}


DriverClass GuessDriverClassFromTreeItem (HTREEITEM hti)
{
   short  idr;

   for (idr = 0; idr < nDriverROOTS; idr++)
      {
      if (hti == aDriverRoot[idr].hti)
         return aDriverRoot[idr].dc;
      }

   return (g_dcFilterClass);
}



/*
 *** EnsureRootIndexExists - makes sure a given parent exists in hAdvDlgTree
 *
 */

BOOL EnsureRootIndexExists (HWND hTree, short idr)
{
   TV_INSERTSTRUCT ti;
   TCHAR           szDesc[ cchRESOURCE ];
   HWND            hwndParent = NULL;
   HWND            hwndName   = NULL;

            // If we already HAVE a root in the tree, we're done.
            //
   if (aDriverRoot[ idr ].hti != NULL)
      return TRUE;

    if (g_dcFilterClass != dcINVALID)
    {
        if (g_dcFilterClass == dcLEGACY)
        {
            if ((aDriverRoot[ idr ].dc != dcWAVE) &&
                (aDriverRoot[ idr ].dc != dcMIDI) &&
                (aDriverRoot[ idr ].dc != dcMIXER) &&
                (aDriverRoot[ idr ].dc != dcAUX))
            {
                return FALSE;
            }
        }
        else if (aDriverRoot[ idr ].dc != g_dcFilterClass)
        {
            return FALSE;
        }
    }

   aDriverRoot[idr].hti = TVI_ROOT;

   LoadString (myInstance, aDriverRoot[idr].idDesc, szDesc, cchRESOURCE);

    if ((g_dcFilterClass == dcINVALID) || (g_dcFilterClass == dcLEGACY))
    {
       ti.hInsertAfter = TVI_LAST;
       ti.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
       ti.item.iImage = idr;
       ti.item.iSelectedImage = idr;
       ti.item.pszText = szDesc;
       ti.item.lParam = NOPIDRIVER;

       if (aDriverRoot[idr].dc == dcINVALID)
          ti.hParent = TVI_ROOT;
       else
          ti.hParent = AdvDlgFindTopLevel ();

       if ((aDriverRoot[idr].hti = TreeView_InsertItem (hTree, &ti)) == NULL)
          return FALSE;
    }

    if (g_dcFilterClass != dcINVALID)
    {
       hwndParent = GetParent(hTree);
       if (hwndParent)
       {
            hwndName = GetDlgItem(hwndParent,IDC_DEVICECLASS);
            if (hwndName)
            {
                if (g_dcFilterClass == dcLEGACY)
                {
                    LoadString (myInstance, IDS_WAVE_HEADER, szDesc, cchRESOURCE);
                }

                SetWindowText(hwndName,szDesc);
            }
       }
    }

   return TRUE;
}


/*
 **** AdvDlgFindTopLevel - Finds the HTREEITEM associated with the tree root
 *
 * If there's a "Multimedia Devices" tree item under which the other roots
 * are collected, this will return that item.  Otherwise, it returns TVI_ROOT.
 *
 */

HTREEITEM AdvDlgFindTopLevel (void)
{
   short  idr;

   for (idr = 0; idr < nDriverROOTS; idr++)
      {
      if (aDriverRoot[idr].dc == dcINVALID)
         return aDriverRoot[idr].hti;
      }

   return TVI_ROOT;
}



/*
 **** InitAdvDlgTree - Prepares the AdvDlg's treeview to display devices
 *
 */

BOOL InitAdvDlgTree (HWND hTree)
{
   int   cxIcon, cyIcon;
   short idr;
   UINT  uFlags;
   DWORD dwLayout;

   #ifdef UNICODE
   TreeView_SetUnicodeFormat(hTree,TRUE);
   #endif

    // Make sure we start with a clean slate
    //
   hAdvDlgTree = hTree;
   SendMessage (hTree, WM_SETREDRAW, FALSE, 0L);

   for (idr = 0; idr < nDriverROOTS; idr++)
   {
      aDriverRoot[ idr ].hti = NULL;
      aDriverRoot[ idr ].dwBit = ((DWORD)1) << idr;
   }

            // Create an imagelist for the icons in the treeview
            //
   cxIcon = (int)GetSystemMetrics (SM_CXSMICON);
   cyIcon = (int)GetSystemMetrics (SM_CYSMICON);
   uFlags = ILC_MASK | ILC_COLOR32;

   if (GetProcessDefaultLayout(&dwLayout) &&
            (dwLayout & LAYOUT_RTL)) 
   {
       uFlags |= ILC_MIRROR;
   }

   if ((hImageList = ImageList_Create (cxIcon, cyIcon,
                                       uFlags, nDriverROOTS, 1)) == NULL)
      return FALSE;

   for (idr = 0; idr < nDriverROOTS; idr++)
      {
      HICON hi = LoadImage (myInstance,
                            MAKEINTRESOURCE( aDriverRoot[idr].idIcon ),
                            IMAGE_ICON, cxIcon, cyIcon, LR_DEFAULTCOLOR);
      ImageList_AddIcon (hImageList, hi);
      }

   TreeView_SetImageList (hTree, hImageList, TVSIL_NORMAL);


    if (g_dcFilterClass == dcINVALID)
    {
            // Create the root nodes that are supposed to exist
            // even without children (note that not all are)
            //
   for (idr = 0; idr < nDriverROOTS; idr++)
      {
      if (aDriverRoot[ idr ].dc == dcINVALID)
         {
         if (!EnsureRootIndexExists (hTree, idr))
            return FALSE;
         }
      }
   for (idr = 0; idr < nDriverROOTS; idr++)
      {
      if (aDriverRoot[ idr ].dc != dcINVALID && aDriverRoot[ idr ].fAlwaysMake)
         {
         if (!EnsureRootIndexExists (hTree, idr))
            return FALSE;
         }
      }
    }
            // Expand the tree somewhat, so the user doesn't get
            // greeted by a blank page
            //
   TreeView_Expand (hTree, AdvDlgFindTopLevel(), TVE_EXPAND);

   SendMessage (hTree, WM_SETREDRAW, TRUE, 0L);
   return TRUE;
}


/*
 **** FreeAdvDlgTree - Removes and frees all items within the AdvDlg treeview
 *
 */

void FreeAdvDlgTree (HWND hTree)
{
   short  idr;

            // Delete all leaf nodes
            //
   for (idr = 0; idr < nDriverROOTS; idr++)
      {
      HTREEITEM  hti;

      if (aDriverRoot[idr].dc == dcINVALID)
         continue;
      if (aDriverRoot[idr].hti == NULL)
         continue;

      while ((hti = TreeView_GetChild (hTree, aDriverRoot[idr].hti)) != NULL)
         {
         if (aDriverRoot[ idr ].dc == dcMIDI)
            {
            HTREEITEM  htiInstrument;

            while ((htiInstrument = TreeView_GetChild (hTree, hti)) != NULL)
               {
               TV_ITEM tvi;
               tvi.mask = TVIF_PARAM;
               tvi.hItem = htiInstrument;
               tvi.lParam = 0;

               TreeView_GetItem(hTree, &tvi);

               if (tvi.lParam != 0)
                  LocalFree ((HANDLE)tvi.lParam);

               TreeView_DeleteItem (hTree, htiInstrument);
               }
            }

         TreeView_DeleteItem (hTree, hti);
         }
      }

            // Delete everything else
            //
   TreeView_DeleteAllItems (hTree);

            // Delete the tree's image list
            //
   if (hImageList)
      {
      TreeView_SetImageList (hTree, NULL, TVSIL_NORMAL);
      ImageList_Destroy (hImageList);
      hImageList = NULL;
      }

            // Delete the InstalledDrivers array
            //
   if (aInstalledDrivers != NULL)
      {
      mysize_t  ii;
      for (ii = 0; ii < cInstalledDrivers; ++ii)
         {
         if (aInstalledDrivers[ ii ].pIDriver != NULL)
            {
            LocalFree ((HANDLE)aInstalledDrivers[ ii ].pIDriver);
            aInstalledDrivers[ ii ].pIDriver = NULL;
            }
         }

      GlobalFree ((HGLOBAL)aInstalledDrivers);
      aInstalledDrivers = NULL;
      cInstalledDrivers = 0;
      }
}


int lstrnicmp (LPTSTR pszA, LPTSTR pszB, size_t cch)
{
#ifdef UNICODE
   size_t  cchA, cchB;
   TCHAR  *pch;

   for (cchA = 1, pch = pszA; cchA < cch; cchA++, pch++)
      {
      if (*pch == TEXT('\0'))
         break;
      }
   for (cchB = 1, pch = pszB; cchB < cch; cchB++, pch++)
      {
      if (*pch == TEXT('\0'))
         break;
      }

   return (CompareStringW (GetThreadLocale(), NORM_IGNORECASE,
                           pszA, cchA, pszB, cchB)
          )-2;  // CompareStringW returns {1,2,3} instead of {-1,0,1}.
#else
   return _strnicmp (pszA, pszB, cch);
#endif
}


LPTSTR lstrchr (LPTSTR pszTarget, TCHAR ch)
{
   size_t ich;
   if (pszTarget == NULL)
      return NULL;
   for (ich = 0; pszTarget[ich] != TEXT('\0'); ich++)
      {
      if (pszTarget[ich] == ch)
         return &pszTarget[ ich ];
      }

   return NULL;
}


void lsplitpath (LPTSTR pszSource,
                 LPTSTR pszDrive, LPTSTR pszPath, LPTSTR pszName, LPTSTR pszExt)
{
   LPTSTR   pszLastSlash = NULL;
   LPTSTR   pszLastDot = NULL;
   LPTSTR   pch;
   size_t   cchCopy;

        /*
         * NOTE: This routine was snitched out of USERPRI.LIB 'cause the
         * one in there doesn't split the extension off the name properly.
         *
         * We assume that the path argument has the following form, where any
         * or all of the components may be missing.
         *
         *      <drive><dir><fname><ext>
         *
         * and each of the components has the following expected form(s)
         *
         *  drive:
         *      0 to _MAX_DRIVE-1 characters, the last of which, if any, is a
         *      ':'
         *  dir:
         *      0 to _MAX_DIR-1 characters in the form of an absolute path
         *      (leading '/' or '\') or relative path, the last of which, if
         *      any, must be a '/' or '\'.  E.g -
         *      absolute path:
         *          \top\next\last\     ; or
         *          /top/next/last/
         *      relative path:
         *          top\next\last\      ; or
         *          top/next/last/
         *      Mixed use of '/' and '\' within a path is also tolerated
         *  fname:
         *      0 to _MAX_FNAME-1 characters not including the '.' character
         *  ext:
         *      0 to _MAX_EXT-1 characters where, if any, the first must be a
         *      '.'
         *
         */

             // extract drive letter and :, if any
             //
   if (*(pszSource + _MAX_DRIVE - 2) == TEXT(':'))
      {
      if (pszDrive)
         {
         lstrncpy (pszDrive, pszSource, _MAX_DRIVE-1);
         pszDrive[ _MAX_DRIVE-1 ] = TEXT('\0');
         }
      pszSource += _MAX_DRIVE-1;
      }
    else if (pszDrive)
      {
      *pszDrive = TEXT('\0');
      }

          // extract path string, if any.  pszSource now points to the first
          // character of the path, if any, or the filename or extension, if
          // no path was specified.  Scan ahead for the last occurence, if
          // any, of a '/' or '\' path separator character.  If none is found,
          // there is no path.  We will also note the last '.' character found,
          // if any, to aid in handling the extension.
          //
   for (pch = pszSource; *pch != TEXT('\0'); pch++)
      {
      if (*pch == TEXT('/') || *pch == TEXT('\\'))
         pszLastSlash = pch;
      else if (*pch == TEXT('.'))
         pszLastDot = pch;
      }

          // if we found a '\\' or '/', fill in pszPath
          //
   if (pszLastSlash)
      {
      if (pszPath)
         {
         cchCopy = (size_t)min((UINT)_MAX_DIR-1, (pszLastSlash-pszSource) + 1);
         lstrncpy (pszPath, pszSource, cchCopy);
         pszPath[ cchCopy ] = 0;
         }
      pszSource = pszLastSlash +1;
      }
   else if (pszPath)
      {
      *pszPath = TEXT('\0');
      }

             // extract file name and extension, if any.  Path now points to
             // the first character of the file name, if any, or the extension
             // if no file name was given.  Dot points to the '.' beginning the
             // extension, if any.
             //

   if (pszLastDot && (pszLastDot >= pszSource))
      {
               // found the marker for an extension -
               // copy the file name up to the '.'.
               //
      if (pszName)
         {
         cchCopy = (size_t)min( (UINT)_MAX_DIR-1, (pszLastDot-pszSource) );
         lstrncpy (pszName, pszSource, cchCopy);
         pszName[ cchCopy ] = 0;
         }

               // now we can get the extension
               //
      if (pszExt)
         {
         lstrncpy (pszExt, pszLastDot, _MAX_EXT -1);
         pszExt[ _MAX_EXT-1 ] = TEXT('\0');
         }
      }
   else
      {
               // found no extension, give empty extension and copy rest of
               // string into fname.
               //
      if (pszName)
         {
         lstrncpy (pszName, pszSource, _MAX_FNAME -1);
         pszName[ _MAX_FNAME -1 ] = TEXT('\0');
         }

      if (pszExt)
         {
         *pszExt = TEXT('\0');
         }
      }

}

void lstrncpy (LPTSTR pszTarget, LPTSTR pszSource, size_t cch)
{
   size_t ich;
   for (ich = 0; ich < cch; ich++)
      {
      if ((pszTarget[ich] = pszSource[ich]) == TEXT('\0'))
         break;
      }
}



/*
 * DEVICE PROPERTY SHEETS _____________________________________________________
 *
 */

         // General flag macros
         //
#define SetFlag(obj, f)             do {obj |= (f);} while (0)
#define ToggleFlag(obj, f)          do {obj ^= (f);} while (0)
#define ClearFlag(obj, f)           do {obj &= ~(f);} while (0)
#define IsFlagSet(obj, f)           (BOOL)(((obj) & (f)) == (f))
#define IsFlagClear(obj, f)         (BOOL)(((obj) & (f)) != (f))

BOOL          InstrumentToResource      (PIRESOURCE, HTREEITEM);
BOOL          DriverToResource          (HWND,PIRESOURCE,PIDRIVER,DriverClass);
DriverClass   OldClassIDToDriverClass   (int);
void          FreeClassNode             (PCLASSNODE);
PIDRIVER      FindIDriverByResource     (PIRESOURCE);
void          EnableDriverService       (PIRESOURCE, BOOL);

BOOL PASCAL DoDevPropCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify);
STATIC void SetDevStatus(int iStatus, HWND hDlg);
INT_PTR CALLBACK ACMDlg(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);


/*
 *** DriverToResource - create a PIRESOURCE structure from a PIDRIVER structure
 *
 * The Win95 code uses PIRESOURCEs (among other structures) to keep track of
 * the item in the Advanced tab's treeview; the old WinNT code used PIDRIVER
 * structures to keep track of the items in its listbox.  The tree now uses
 * PIDRIVER structures, but we create PIRESOURCE structures out of 'em before
 * passing control off to the Win95 properties dialogs, which came through
 * largely unmodified.
 *
 * BTW, I retained the PIDRIVER structures as the main treeview structure
 * because it prevented difficulty in porting over the Install/Remove Driver
 * code from the old NT code.
 *
 */

BOOL DriverToResource (HWND hPar, PIRESOURCE pir, PIDRIVER pid, DriverClass dc)
{
   if (dc == dcINVALID)
      {
      if ((dc = GuessDriverClass (pid)) == dcINVALID)
         return FALSE;
      }

   if ((pir->pcn = (PCLASSNODE)LocalAlloc (LPTR, sizeof(CLASSNODE))) == NULL)
      return FALSE;

   if (!DriverClassToClassNode (pir->pcn, dc))
      {
      LocalFree ((HANDLE)pir->pcn);
      return FALSE;
      }

   if (dc == dcACODEC)
      pir->iNode = 3;   // 1=class, 2=device, 3=acm, 4=instmt
// else if (dc == dcINSTRUMENT)
//    pir->iNode = 4;   // 1=class, 2=device, 3=acm, 4=instmt
   else
      pir->iNode = 2;   // 1=class, 2=device, 3=acm, 4=instmt

   lstrcpy (pir->szFriendlyName, pid->szDesc);
   lstrcpy (pir->szDesc,         pid->szDesc);
   lstrcpy (pir->szFile,         pid->szFile);
   lstrcpy (pir->szDrvEntry,     pid->szAlias);
   lstrcpy (pir->szClass,        pir->pcn->szClass);

   pid->fQueryable = IsConfigurable (pid, hPar);
   pir->fQueryable = (short)pid->fQueryable;
   pir->iClassID = (short)DriverClassToOldClassID (dc);
   pir->szParam[0] = 0;
   pir->dnDevNode = 0;
   pir->hDriver = NULL;

            // Find fStatus, which despite its name is really a series of
            // flags--in Win95 it's composed of DEV_* flags (from the old
            // mmcpl.h), but those are tied with PNP.  Here, we use the
            // dwStatus* flags:
            //
   pir->fStatus = (int)GetDriverStatus (pid);

   return TRUE;
}


BOOL InstrumentToResource (PIRESOURCE pir, HTREEITEM hti)
{
   TV_ITEM tvi;
   PINSTRUM pin;

   tvi.mask = TVIF_PARAM;
   tvi.hItem = hti;
   tvi.lParam = 0;
   TreeView_GetItem(hAdvDlgTree, &tvi);

   if ((pin = (PINSTRUM)tvi.lParam) == NULL)
      return FALSE;


   if ((pir->pcn = (PCLASSNODE)LocalAlloc (LPTR, sizeof(CLASSNODE))) == NULL)
      return FALSE;

   if (!DriverClassToClassNode (pir->pcn, dcMIDI))
      {
      LocalFree ((HANDLE)pir->pcn);
      return FALSE;
      }

   pir->iNode = 4;  // 1=class, 2=device, 3=acm, 4=instmt

   lstrcpy (pir->szFriendlyName, pin->szFriendly);
   lstrcpy (pir->szDesc,         pin->szKey);
// lstrcpy (pir->szFile,         TEXT("unused"));
// lstrcpy (pir->szDrvEntry,     TEXT("unused"));
   lstrcpy (pir->szClass,        pir->pcn->szClass);

   pir->fQueryable = FALSE;
   pir->iClassID = MIDI_ID;
   pir->szParam[0] = 0;
   pir->dnDevNode = 0;
   pir->hDriver = NULL;
   pir->fStatus = 0;

   return TRUE;
}


BOOL DriverClassToClassNode (PCLASSNODE pcn, DriverClass dc)
{
   short  idr;
   short  ii;
   int    cxIcon, cyIcon;

   if ((idr = DriverClassToRootIndex (dc)) == -1)
      return FALSE;

   pcn->iNode = 1;  // 1=class, 2=device, 3=acm, 4=instmt

   GetString (pcn->szClassName, aDriverRoot[idr].idDesc);
   pcn->szClass[0] = TEXT('\0');

   for (ii = 0; ii < nKeywordDESCS; ii++)
      {
      if (aKeywordDesc[ii].dc == dc)
         {
         lstrcpy (pcn->szClass, aKeywordDesc[ii].psz);
         break;
         }
      }

   cxIcon = (int)GetSystemMetrics (SM_CXICON);
   cyIcon = (int)GetSystemMetrics (SM_CYICON);

   pcn->hIcon = LoadImage (myInstance,
                           MAKEINTRESOURCE( aDriverRoot[ idr ].idIcon ),
                           IMAGE_ICON, cxIcon, cyIcon, LR_DEFAULTCOLOR);

   return TRUE;
}


int DriverClassToOldClassID (DriverClass dc)
{
   switch (dc)
      {
      case dcWAVE:   return WAVE_ID;      break;
      case dcMIDI:   return MIDI_ID;      break;
      case dcMIXER:  return MIXER_ID;     break;
      case dcAUX:    return AUX_ID;       break;
      case dcMCI:    return MCI_ID;       break;
      case dcACODEC: return ACM_ID;       break;
      case dcVCODEC: return ICM_ID;       break;
      case dcVIDCAP: return VIDCAP_ID;    break;
      case dcJOY:    return JOYSTICK_ID;  break;
      default:       return JOYSTICK_ID;  break;
      }
}


DriverClass OldClassIDToDriverClass (int ii)
{
   switch (ii)
      {
      case WAVE_ID:      return dcWAVE;    break;
      case MIDI_ID:      return dcMIDI;    break;
      case MIXER_ID:     return dcMIXER;   break;
      case AUX_ID:       return dcAUX;     break;
      case MCI_ID:       return dcMCI;     break;
      case ACM_ID:       return dcACODEC;  break;
      case ICM_ID:       return dcVCODEC;  break;
      case VIDCAP_ID:    return dcVIDCAP;  break;
      case JOYSTICK_ID:  return dcJOY;     break;
      default:           return dcOTHER;
      }
}


void FreeIResource (PIRESOURCE pir)
{
   if (pir->pcn != NULL)
      {
      FreeClassNode (pir->pcn);
      LocalFree ((HANDLE)pir->pcn);
      pir->pcn = NULL;
      }
}


void FreeClassNode (PCLASSNODE pcn)
{
   if (pcn->hIcon != NULL)
      {
      DestroyIcon (pcn->hIcon);
      pcn->hIcon = NULL;
      }
}


DWORD GetDriverStatus (PIDRIVER pid)
{
   DWORD     dwStatus;
   SC_HANDLE scManager;
   SC_HANDLE scDriver;
   TCHAR     szName[ cchRESOURCE ];

   dwStatus = 0;

   lsplitpath (pid->szFile, NULL, NULL, szName, NULL);

            // First step: determine if the driver has a service
            //
   if ((scManager = OpenSCManager (NULL, NULL, GENERIC_READ)) != NULL)
      {
      if ((scDriver = OpenService (scManager, szName, GENERIC_READ)) != NULL)
         {
         QUERY_SERVICE_CONFIG  qsc;
         SERVICE_STATUS        ss;
         DWORD                 cbReq;
         void                 *pqsc;

         SetFlag (dwStatus, dwStatusHASSERVICE);

                  // Great!  It has a service.  Find out if the service
                  // is actively running, and whether it is disabled.
                  //
         if (QueryServiceConfig (scDriver, &qsc, sizeof(qsc), &cbReq))
            {
            if (qsc.dwStartType != SERVICE_DISABLED)
               {
               SetFlag (dwStatus, dwStatusSvcENABLED);
               }
            }
         else if ((pqsc = (void *)LocalAlloc (LPTR, cbReq)) != NULL)
            {
            if (QueryServiceConfig (scDriver,
                                    (QUERY_SERVICE_CONFIG *)pqsc,
                                    cbReq, &cbReq))
               {
               if ( ((QUERY_SERVICE_CONFIG *)pqsc)->dwStartType
                     != SERVICE_DISABLED)
                  {
                  SetFlag (dwStatus, dwStatusSvcENABLED);
                  }
               }

            LocalFree ((HANDLE)pqsc);
            }

         if (QueryServiceStatus (scDriver, &ss))
            {
            if ((ss.dwCurrentState != SERVICE_STOPPED) &&
                (ss.dwCurrentState != SERVICE_STOP_PENDING))
               {
               SetFlag (dwStatus, dwStatusSvcSTARTED);
               }
            }

         CloseServiceHandle (scDriver);
         }

      CloseServiceHandle (scManager);
      }

            // If no service, see if we can talk to the driver itself
            //
   if (!IsFlagSet (dwStatus, dwStatusHASSERVICE))
      {
      HANDLE hDriver;

      if ((hDriver = OpenDriver (pid->wszAlias, pid->wszSection, 0L)) != NULL)
         {
         SetFlag (dwStatus, dwStatusDRIVEROK);

         CloseDriver (hDriver, 0L, 0L);
         }
      }

            // If it's a wave device, can we map through it?
            //
   if (GetMappable (pid))
      {
      SetFlag (dwStatus, dwStatusMAPPABLE);
      }

   return dwStatus;
}


void GetTreeItemNodeDesc (LPTSTR pszTarget, PIRESOURCE pir)
{
   lstrcpy (pszTarget, pir->szFriendlyName);
}


void GetTreeItemNodeID (LPTSTR pszTarget, PIRESOURCE pir)
{
   DriverClass dc;
   CLASSNODE cn;

   *pszTarget = 0;  // In case we fail later

   dc = OldClassIDToDriverClass (pir->iClassID);
   if (!DriverClassToClassNode (&cn, dc))
      return;

   switch (pir->iNode)  // 1=class, 2=device, 3=acm, 4=instmt
      {
      case 1:   // class
         lstrcpy (pszTarget, cn.szClass);
         break;

      case 2:   // instrument
         wsprintf (pszTarget, TEXT("%s\\%s"), cn.szClass, pir->szDrvEntry);
         break;

      case 4:   // instrument
         lstrcpy (pszTarget, pir->szDesc);
         break;

      default:
         lstrcpy (pszTarget, pir->szDesc);
         break;
      }

   FreeClassNode (&cn);
}


void ShowDeviceProperties (HWND hPar, HTREEITEM hti)
{
   IRESOURCE   ir;
   CLASSNODE   cn;
   DEVTREENODE dtn;
   short       idr;
   TCHAR        szTitle[ cchRESOURCE ];
   TCHAR        szTab[ cchRESOURCE ];
   DriverClass dc;
   PIDRIVER    pid;

   if (hti == NULL)
      return;

   if (TreeView_GetParent (hAdvDlgTree, hti) &&
       TreeView_GetGrandParent (hAdvDlgTree, hti) &&
       (GuessDriverClassFromTreeItem (
                       TreeView_GetGrandParent (hAdvDlgTree, hti)
                                     ) == dcMIDI))
      {
      if (InstrumentToResource (&ir, hti))
         {
         ShowMidiPropSheet (NULL,
                            ir.szFriendlyName,
                            hPar,
                            MIDI_INSTRUMENT_PROP,
                            ir.szFriendlyName,
                            hti,
                            (LPARAM)&ir,
                            (LPARAM)hAdvDlgTree);

         FreeIResource (&ir);
         }
      return;
      }
   else if ((pid = FindIDriverByTreeItem (hti)) != NULL)
      {
      dc = GuessDriverClassFromTreeItem (TreeView_GetParent(hAdvDlgTree,hti));
      if (dc == dcINVALID)
         {
         if ((dc = GuessDriverClass (pid)) == dcINVALID)
            return;
         }
      }
   else
      {
      if ((dc = GuessDriverClassFromTreeItem (hti)) == dcINVALID)
         return;
      }

   if (g_dcFilterClass != dcINVALID)
   {
        if ((dc == dcOTHER) || (dc == dcINVALID))
        {
            dc = g_dcFilterClass;
        }
   }

   if ((idr = DriverClassToRootIndex (dc)) == -1)
      return;

   if (pid == NULL) // Just want class properties?
      {
      if (!DriverClassToClassNode (&cn, dc))
         return;

      if (dc == dcMIDI) // MIDI class properties?
         {
         GetString (szTitle, aDriverRoot[idr].idDesc);

         ShowMidiPropSheet (NULL,
                            szTitle,
                            hPar,
                            MIDI_CLASS_PROP,
                            szTitle,
                            hti,
                            (LPARAM)&cn,
                            (LPARAM)hAdvDlgTree);
         }
      else // Generic class properties?  (nothing to do, really)
         {
         GetString (szTab,   IDS_GENERAL);
         GetString (szTitle, aDriverRoot[idr].idDesc);

         dtn.lParam = (LPARAM)&cn;
         dtn.hwndTree = hAdvDlgTree;

         ShowPropSheet (szTab,
                        DevPropDlg,
                        DLG_DEV_PROP,
                        hPar,
                        szTitle,
                        (LPARAM)&dtn);
         }

      FreeClassNode (&cn);
      }
   else
      {
      switch (dc)
         {
         case dcACODEC:
               GetString (szTab, IDS_GENERAL);

               ShowPropSheet (szTab,
                              ACMDlg,
                              DLG_ACMDEV_PROP,
                              hPar,
                              pid->szDesc,
                              pid->lp);

               // Re-sort the Audio Codec entries, in case their priorities
               // have changed.  Then find treeview item for this codec,
               // and select it.
               //
               {
               HTREEITEM  hti;
               short      idr;

               SendMessage (hAdvDlgTree, WM_SETREDRAW, FALSE, 0L);
               FillTreeFromMSACM (hAdvDlgTree);
               SendMessage (hAdvDlgTree, WM_SETREDRAW, TRUE, 0L);

               if ((idr = DriverClassToRootIndex (dcACODEC)) != -1)
                  {
                  if ((hti = aDriverRoot[ idr ].hti) != NULL)
                     {
                     for (hti = TreeView_GetChild (hAdvDlgTree, hti);
                          hti != NULL;
                          hti = TreeView_GetNextSibling (hAdvDlgTree, hti))
                        {
                        if (pid == FindIDriverByTreeItem (hti))
                           {
                           TreeView_SelectItem (hAdvDlgTree, hti);
                           break;
                           }
                        }
                     }
                  }
               }
              break;

         case dcMIDI:
               if (!DriverToResource (hPar, &ir, pid, dc))
                  break;

               GetString (szTab, IDS_GENERAL);

               dtn.lParam = (LPARAM)&ir;
               dtn.hwndTree = hAdvDlgTree;

               ShowWithMidiDevPropSheet (szTab,
                                         DevPropDlg,
                                         DLG_DEV_PROP,
                                         hPar,
                                         pid->szDesc,
                                         hti,
                                         (LPARAM)&dtn,
                                         (LPARAM)&ir,
                                         (LPARAM)hAdvDlgTree);

               FreeIResource (&ir);
              break;

         case dcWAVE:
               if (!DriverToResource (hPar, &ir, pid, dc))
                  break;

               GetString (szTab, IDS_GENERAL);

               dtn.lParam = (LPARAM)&ir;
               dtn.hwndTree = hAdvDlgTree;

               ShowPropSheet (szTab,
                              DevPropDlg,
                              DLG_WAVDEV_PROP,
                              hPar,
                              pid->szDesc,
                              (LPARAM)&dtn);

               FreeIResource (&ir);
              break;

         default:
               if (!DriverToResource (hPar, &ir, pid, dc))
                  break;

               GetString (szTab, IDS_GENERAL);

               dtn.lParam = (LPARAM)&ir;
               dtn.hwndTree = hAdvDlgTree;

               ShowPropSheet (szTab,
                              DevPropDlg,
                              DLG_DEV_PROP,
                              hPar,
                              pid->szDesc,
                              (LPARAM)&dtn);

               FreeIResource (&ir);
              break;
         }
      }
}



#include "medhelp.h"

const static DWORD aDevPropHelpIds[] = {  // Context Help IDs
    ID_DEV_SETTINGS,     IDH_MMCPL_DEVPROP_SETTINGS,
    IDC_DEV_ICON,        NO_HELP,
    IDC_DEV_DESC,        NO_HELP,
    IDC_DEV_STATUS,      NO_HELP,
    IDC_ENABLE,          IDH_MMCPL_DEVPROP_ENABLE,
    IDC_DISABLE,         IDH_MMCPL_DEVPROP_DISABLE,
    IDC_DONOTMAP,        IDH_MMCPL_DEVPROP_DONT_MAP,
    0, 0
};

/*
 ***************************************************************
 * DlgProc for device Property sheet.
 ***************************************************************
 */
INT_PTR CALLBACK DevPropDlg (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    NMHDR FAR   *lpnm;

    switch (uMsg)
    {
        case WM_NOTIFY:
            lpnm = (NMHDR FAR *)lParam;
            switch(lpnm->code)
            {
                case PSN_KILLACTIVE:
                    FORWARD_WM_COMMAND(hDlg, IDOK, 0, 0, SendMessage);
                    break;

                case PSN_APPLY:
                    FORWARD_WM_COMMAND(hDlg, ID_APPLY, 0, 0, SendMessage);
                    break;

                case PSN_SETACTIVE:
                    FORWARD_WM_COMMAND(hDlg, ID_INIT, 0, 0, SendMessage);
                    break;

                case PSN_RESET:
                    FORWARD_WM_COMMAND(hDlg, IDCANCEL, 0, 0, SendMessage);
                    break;
            }
            break;

        case WM_INITDIALOG:
        {
            PIRESOURCE pIResource;
            PCLASSNODE pcn;
            PDEVTREENODE pdtn = (PDEVTREENODE)(((LPPROPSHEETPAGE)lParam)->lParam);

            SetWindowLongPtr(hDlg, DWLP_USER, ((LPPROPSHEETPAGE)lParam)->lParam);

            if (*((short *)(pdtn->lParam)) == 1)
            {
                HWND hwndTree = pdtn->hwndTree;
                HTREEITEM  htiCur = TreeView_GetSelection(hwndTree);
                TCHAR   sz[cchRESOURCE];
                TV_ITEM tvi;

                tvi.mask = TVIF_CHILDREN;
                tvi.hItem = htiCur;
                TreeView_GetItem(hwndTree, &tvi);

                pcn = (PCLASSNODE)(pdtn->lParam);
                //set class icon.
                SendDlgItemMessage(hDlg, IDC_DEV_ICON, STM_SETICON, (WPARAM)pcn->hIcon , 0L);
                SetWindowText(GetDlgItem(hDlg, IDC_DEV_DESC), pcn->szClassName);
                DestroyWindow(GetDlgItem(hDlg, IDC_ENABLE));
                DestroyWindow(GetDlgItem(hDlg, IDC_DISABLE));
                DestroyWindow(GetDlgItem(hDlg, ID_DEV_SETTINGS));

                GetString (sz, (tvi.cChildren) ? IDS_NOPROP : IDS_NODEVS);
                SetWindowText(GetDlgItem(hDlg, IDC_DEV_STATUS), sz);
            }
            else
            {
                pIResource = (PIRESOURCE)(pdtn->lParam);
                SendDlgItemMessage(hDlg, IDC_DEV_ICON, STM_SETICON, (WPARAM)pIResource->pcn->hIcon , 0L);
                SetWindowText(GetDlgItem(hDlg, IDC_DEV_DESC), pIResource->szDesc);
                if (!IsFlagSet(pIResource->fStatus, dwStatusHASSERVICE) &&
                    !IsFlagSet(pIResource->fStatus, dwStatusDRIVEROK))
                {
                    SetDevStatus(pIResource->fStatus, hDlg);
                }
                else
                {
                    if (pIResource->iClassID == WAVE_ID)
                    {
                        CheckDlgButton (hDlg,
    IDC_DONOTMAP,
    IsFlagClear(pIResource->fStatus,
              dwStatusMAPPABLE));
                    }

                    if (!pIResource->fQueryable || pIResource->fQueryable == -1)
                        EnableWindow(GetDlgItem(hDlg, ID_DEV_SETTINGS), FALSE);
                    if (!IsFlagSet (pIResource->fStatus, dwStatusHASSERVICE))
                    {
                        DestroyWindow(GetDlgItem(hDlg, IDC_ENABLE));
                        DestroyWindow(GetDlgItem(hDlg, IDC_DISABLE));
                    }
                    else
                    {
                        TCHAR szStatusStr[MAXSTR];
                        DriverClass dc;
                        short idr;

                        dc = OldClassIDToDriverClass (pIResource->iClassID);
                        idr = DriverClassToRootIndex (dc);

                        if (idr == -1)
                        {
                           DestroyWindow (GetDlgItem(hDlg, IDC_ENABLE));
                           DestroyWindow (GetDlgItem(hDlg, IDC_DISABLE));
                        }
                        else
                        {
                           GetString (szStatusStr, aDriverRoot[idr].idEnable);
                           SetDlgItemText(hDlg, IDC_ENABLE, szStatusStr);
                           GetString (szStatusStr, aDriverRoot[idr].idDisable);
                           SetDlgItemText(hDlg, IDC_DISABLE, szStatusStr);
                        }
                    }
                }
                SetDevStatus(pIResource->fStatus, hDlg);
            }

#ifdef FIX_BUG_15451
            if (szDriverWhichNeedsSettings[0] != TEXT('\0'))
            {
                MakeThisDialogLookLikeTheOldDialog (GetParent(hDlg));
            }
#endif // FIX_BUG_15451
            break;
        }

        case WM_DESTROY:
            break;

        case WM_DROPFILES:
            break;

        case WM_CONTEXTMENU:
            WinHelp ((HWND) wParam, NULL, HELP_CONTEXTMENU,
                    (UINT_PTR) (LPTSTR) aDevPropHelpIds);
            return TRUE;

        case WM_HELP:
        {
            LPHELPINFO lphi = (LPVOID) lParam;
            WinHelp (lphi->hItemHandle, NULL, HELP_WM_HELP,
                    (UINT_PTR) (LPTSTR) aDevPropHelpIds);
            return TRUE;
        }

        case WM_COMMAND:
            HANDLE_WM_COMMAND(hDlg, wParam, lParam, DoDevPropCommand);
            break;
    }
    return FALSE;
}

/*
 ***************************************************************
 *
 ***************************************************************
 */
BOOL PASCAL DoDevPropCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify)
{
    PDEVTREENODE pdtn = (PDEVTREENODE)GetWindowLongPtr(hDlg, DWLP_USER);
    PIRESOURCE pIResource;
    static int fDevStatus;

    if (!pdtn)
        return FALSE;
    pIResource = (PIRESOURCE)(pdtn->lParam);

    switch (id)
    {

    case ID_APPLY:
        if ((pIResource->iNode == 2) && (fDevStatus != pIResource->fStatus))
        {
            if ( (IsFlagSet (fDevStatus, dwStatusMAPPABLE)) !=
                 (IsFlagSet (pIResource->fStatus, dwStatusMAPPABLE)) )
            {
                SetMappable (pIResource,
                             (BOOL)IsFlagSet (fDevStatus, dwStatusMAPPABLE));
            }

            if ( (IsFlagSet (fDevStatus, dwStatusHASSERVICE)) &&
                 (IsFlagSet (fDevStatus, dwStatusSvcENABLED) !=
                  IsFlagSet (pIResource->fStatus, dwStatusSvcENABLED)) )
            {
#if 0 // TODO: Multiportmidi
                if ( (pIResource->iClassID == MIDI_ID) &&
                     (IsFlagSet(fDevStatus, DEV_MULTIPORTMIDI)) )
                {
                    EnableMultiPortMIDI (pIResource,
                                         IsFlagSet (fDevStatus,
                dwStatusSvcENABLED));
                }
                else
#endif
                {
                    EnableDriverService (pIResource,
                                         IsFlagSet (fDevStatus,
                dwStatusSvcENABLED));
                }
            }
            DisplayMessage(hDlg, IDS_CHANGESAVED, IDS_RESTART, MB_OK);
        }
        return TRUE;

    case IDOK:
        return TRUE;
    case IDCANCEL:
        break;

    case ID_INIT:
        if (pIResource->iNode == 2)
            fDevStatus = pIResource->fStatus;
#ifdef FIX_BUG_15451
        if (szDriverWhichNeedsSettings[0] != TEXT('\0'))
        {
            FORWARD_WM_COMMAND(hDlg,ID_DEV_SETTINGS,0,0,PostMessage);
        }
#endif // FIX_BUG_15451
        break;

    case IDC_DONOTMAP:
        if(Button_GetCheck(GetDlgItem(hDlg, IDC_DONOTMAP)))
            ClearFlag(fDevStatus, dwStatusMAPPABLE);
        else
            SetFlag(fDevStatus, dwStatusMAPPABLE);
        PropSheet_Changed(GetParent(hDlg),hDlg);
        break;

    case IDC_ENABLE:
        if (IsFlagSet (fDevStatus, dwStatusHASSERVICE))
        {
           SetFlag(fDevStatus, dwStatusSvcENABLED);
           SetDevStatus(fDevStatus, hDlg);
           PropSheet_Changed(GetParent(hDlg),hDlg);
#if 0 // TODO: Multiportmidi
           if (IsFlagSet(fDevStatus, DEV_MULTIPORTMIDI))
           {
               DisplayMessage(hDlg, IDS_ENABLE, IDS_ENABLEMULTIPORTMIDI, MB_OK);
           }
#endif
        }
        break;

    case IDC_DISABLE:
        if (IsFlagSet (fDevStatus, dwStatusHASSERVICE))
        {
           ClearFlag(fDevStatus, dwStatusSvcENABLED);
           SetDevStatus(fDevStatus, hDlg);
           PropSheet_Changed(GetParent(hDlg),hDlg);
#if 0 // TODO: Multiportmidi
           if (IsFlagSet(fDevStatus, DEV_MULTIPORTMIDI))
           {
               DisplayMessage(hDlg, IDS_DISABLE, IDS_DISABLEMULTIPORTMIDI, MB_OK);
           }
#endif
        }
        break;

    case ID_DEV_SETTINGS:
#ifdef FIX_BUG_15451
        if (szDriverWhichNeedsSettings[0] != TEXT('\0'))
        {
            ConfigureDriver (hDlg, szDriverWhichNeedsSettings);
            szDriverWhichNeedsSettings[0] = 0;
        }
        else
        {
            PIDRIVER  pid;

            if ((pid = FindIDriverByResource (pIResource)) == NULL)
                break;

            ShowDriverSettings (hDlg, pid->szFile);
        }
#else // FIX_BUG_15451
    {
        PIDRIVER  pid;
        HANDLE    hDriver;

        if ((pid = FindIDriverByResource (pIResource)) == NULL)
            break;

        if ((hDriver = OpenDriver (pid->wszAlias, pid->wszSection, 0L)) == 0)
        {
            OpenDriverError(hDlg, pid->szDesc, pid->szFile);
        }
        else
        {
            DRVCONFIGINFO   DrvConfigInfo;
            InitDrvConfigInfo(&DrvConfigInfo, pid);
            if ((SendDriverMessage(
                     hDriver,
                     DRV_CONFIGURE,
                     (LONG)hDlg,
                     (LONG)(LPDRVCONFIGINFO)&DrvConfigInfo) ==
                DRVCNF_RESTART))
            {
               iRestartMessage= 0;
               DialogBox(myInstance,
                  MAKEINTRESOURCE(DLG_RESTART), hDlg, RestartDlg);
            }
            CloseDriver(hDriver, 0L, 0L);
        }
        }
#endif // FIX_BUG_15451
        break;
    }
    return FALSE;
}


/*
 ***************************************************************
 * Check the status flag for the device and display the appropriate text the
 *  the device properties prop sheet.
 ***************************************************************
 */
STATIC void SetDevStatus(int iStatus, HWND hDlg)
{
    HWND hwndS = GetDlgItem(hDlg, IDC_DEV_STATUS);
    TCHAR szStatus[cchRESOURCE];

    if (IsFlagSet (iStatus, dwStatusHASSERVICE))
    {
        if (IsFlagSet (iStatus, dwStatusSvcENABLED))
        {
            if (IsFlagSet (iStatus, dwStatusSvcSTARTED))
                GetString (szStatus, IDS_DEVENABLEDOK);
            else
                GetString (szStatus, IDS_DEVENABLEDNOTOK);

            CheckRadioButton (hDlg, IDC_ENABLE, IDC_DISABLE, IDC_ENABLE);
        }
        else // service has been disabled
        {
            if (IsFlagSet (iStatus, dwStatusSvcSTARTED))
                GetString (szStatus, IDS_DEVDISABLEDOK);
            else
                GetString (szStatus, IDS_DEVDISABLED);

            CheckRadioButton(hDlg, IDC_ENABLE, IDC_DISABLE, IDC_DISABLE);
        }

        SetWindowText(hwndS, szStatus);
    }
    else // driver does not have a service, and thus can't be disabled
    {
        if (IsFlagSet (iStatus, dwStatusDRIVEROK))
            GetString (szStatus, IDS_DEVENABLEDOK);
        else
            GetString (szStatus, IDS_DEVENABLEDNODRIVER);

        CheckRadioButton(hDlg, IDC_ENABLE, IDC_DISABLE, IDC_ENABLE);
        SetWindowText(hwndS, szStatus);
    }
}


/*
 *** EnableDriverService - enable or disable a service-based driver
 *
 * If Enable, the service will be set to start==system_start.
 * If !Enable, the service will be set to start==disabled.
 *
 */

void EnableDriverService (PIRESOURCE pir, BOOL fEnable)
{
   SC_HANDLE scManager;
   SC_HANDLE scDriver;
   TCHAR     szName[ cchRESOURCE ];

   lsplitpath (pir->szFile, NULL, NULL, szName, NULL);

            // First step: determine if the driver has a service
            //
   if ((scManager = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS)) != NULL)
      {
      if ((scDriver = OpenService (scManager, szName, SERVICE_ALL_ACCESS)) != 0)
         {
         QUERY_SERVICE_CONFIG  qsc;
         SERVICE_STATUS        ss;
         DWORD                 cbReq;
         void                 *pqsc;

         ChangeServiceConfig (scDriver,
                              SERVICE_NO_CHANGE,
                              (fEnable) ? SERVICE_SYSTEM_START
                                        : SERVICE_DISABLED,
                              SERVICE_NO_CHANGE,
                              NULL,
                              NULL,
                              NULL,
                              NULL,
                              NULL,
                              NULL,
                              NULL);

         CloseServiceHandle (scDriver);
         }

      CloseServiceHandle (scManager);
      }
}


/*
 ***************************************************************
 * SetMappable
 *
 * Sets the "Mappable" value for wave devices in the registry.
 * The registry key is created if necessary
 *
 ***************************************************************
 */

BOOL SetMappable (PIRESOURCE pIResource, BOOL fMappable)
{
    TCHAR   szFile[ _MAX_FNAME +1 +_MAX_EXT +1 ];
    TCHAR   szExt[ _MAX_EXT +1 ];
    TCHAR   szRegKey[MAX_PATH+1];
    DWORD   dwMappable;
    HKEY    hKey;

    dwMappable = (fMappable) ? 1 : 0;

    lsplitpath (pIResource->szFile, NULL, NULL, szFile, szExt);
    if (szExt[0] != TEXT('\0'))
        lstrcat (szFile, szExt);

    wsprintf (szRegKey, TEXT("%s\\%s"), REGSTR_PATH_WAVEMAPPER, szFile);

    if (RegCreateKey (HKEY_LOCAL_MACHINE, szRegKey, &hKey) != ERROR_SUCCESS)
    {
        return FALSE;
    }

    if (RegSetValueEx (hKey,
                       REGSTR_VALUE_MAPPABLE,
                       (DWORD)0,
                       REG_DWORD,
                       (void *)&dwMappable,
                       sizeof(dwMappable)) != ERROR_SUCCESS)
    {
        RegCloseKey (hKey);
        return FALSE;
    }

    RegCloseKey (hKey);

    if (fMappable)
    {
        SetFlag(pIResource->fStatus, dwStatusMAPPABLE);
    }
    else
    {
        ClearFlag(pIResource->fStatus, dwStatusMAPPABLE);
    }

    return TRUE;
}


BOOL GetMappable (PIDRIVER pIDriver)
{
    TCHAR   szFile[ _MAX_FNAME +1 +_MAX_EXT +1 ];
    TCHAR   szExt[ _MAX_EXT +1 ];
    TCHAR   szRegKey[MAX_PATH+1];
    DWORD   dwMappable;
    DWORD   dwSize;
    DWORD   dwType;
    HKEY    hKey;

    lsplitpath (pIDriver->szFile, NULL, NULL, szFile, szExt);
    if (szExt[0] != TEXT('\0'))
        lstrcat (szFile, szExt);

    wsprintf (szRegKey, TEXT("%s\\%s"), REGSTR_PATH_WAVEMAPPER, szFile);

    if (RegOpenKey (HKEY_LOCAL_MACHINE, szRegKey, &hKey) != ERROR_SUCCESS)
    {
        return TRUE;
    }

    dwSize = sizeof(dwMappable);
    if (RegQueryValueEx (hKey,
                         REGSTR_VALUE_MAPPABLE,
                         NULL,
                         &dwType,
                         (void *)&dwMappable,
                         &dwSize) != ERROR_SUCCESS)
    {
        RegCloseKey (hKey);
        return TRUE;
    }

    RegCloseKey (hKey);

    return (dwMappable) ? TRUE : FALSE;
}


#ifdef FIX_BUG_15451

        // FixDriverService - work around known problems with sound drivers
        //
        // If there was a functioning kernel-mode service in place before the
        // config dialog was presented, then there should still be one
        // afterwards.  However, there are two known problems with the
        // currently-release drivers:
        //
        // 1) The service may not have shut down properly, and is stuck
        //    in a STOP_PENDING state(*).  If this is the case, we need to
        //    ensure that the service is set to load on SYSTEM_START, and
        //    tell the user that the machine has to be rebooted before sound
        //    will work again.
        //
        // 2) The service may have failed to restart properly, and is
        //    stopped(**).  If this is the case, and LoadType!=0, try setting
        //    LoadType=1 and starting the service.
        //
        // (*) -- bug #15451 in NT/SUR, where pending IRPs and open mixer
        //        handles prevent the service from shutting down
        //
        // (**) -- bug #XXXXX in NT/SUR, where some RISC machines stop the
        //         service, set LoadType=1, and fail to restart the service
        //         after you cancel their config dialog
        //
BOOL FixDriverService (PIDRIVER pid)
{
    SC_HANDLE scManager;
    SC_HANDLE scDriver;
    SERVICE_STATUS ss;
    BOOL rc = FALSE;
    TCHAR szName[ cchRESOURCE ];

    lsplitpath (pid->szFile, NULL, NULL, szName, NULL);

    // First step: open the service...even if it's hosed, we should
    // still be able to do this.
    //
    if ((scManager = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS)) == 0)
    {
        return FALSE;
    }
    if ((scDriver = OpenService (scManager, szName, SERVICE_ALL_ACCESS)) == 0)
    {
        CloseServiceHandle (scManager);
        return FALSE;
    }

    // Now check its status.  Look for STOP_PENDING and STOPPED states.
    //
    if (QueryServiceStatus (scDriver, &ss))
    {
        if (ss.dwCurrentState == SERVICE_STOP_PENDING)
        {
            // The service didn't stop properly--we'll have to reboot.
            // Make sure the service is configured such that it will start
            // properly when we restart.
            //
            ChangeServiceConfig (scDriver,
                                 SERVICE_NO_CHANGE,
                                 SERVICE_SYSTEM_START,   // Enable this puppy!
                                 SERVICE_NO_CHANGE,
                                 NULL,
                                 NULL,
                                 NULL,
                                 NULL,
                                 NULL,
                                 NULL,
                                 NULL);

            // Tell the caller that a reboot is mandatory
            rc = TRUE;
        }
        else if (ss.dwCurrentState == SERVICE_STOPPED)
        {
            TCHAR szKey[ cchRESOURCE ];
            HKEY  hkParams;

            // The service stopped, but didn't restart properly--it could
            // just be the LoadType problem.  To fix this, we'll need
            // to enumerate all the keys underneath the key
            // HKLM\System\CurrentControlSet\Services\(thisdriver)\Parameters,
            // and look for ...\Parameters\*\LoadType==(DWORD)1.
            //
            // First step is to open HKLM\System\CCS\Services\(driver)\Parms.
            //
            wsprintf (szKey, TEXT("%s\\%s\\Parameters"),
                             REGSTR_PATH_SERVICES,
                             szName);

            if (RegOpenKey (HKEY_LOCAL_MACHINE, szKey, &hkParams) == 0)
            {
                DWORD cSubKeys;
                DWORD iSubKey;
                BOOL fFixedLoadType = FALSE;

                // Find out how many subkeys there are under here--the
                // keys we want are named "Device0" through "Device(n-1)"
                //
                RegQueryInfoKey (hkParams,
                                 NULL,          // lpClass
                                 NULL,          // lpcbClass
                                 NULL,          // lpReserved
                                 &cSubKeys,     // Whoops!  We want this.
                                 NULL,          // lpcbMaxSubKeyLen
                                 NULL,          // lpcbMaxClassLen
                                 NULL,          // lpcValues
                                 NULL,          // lpcbMaxValueNameLen
                                 NULL,          // lpcbMaxValueLen
                                 NULL,          // lpcbSecurityDescriptor
                                 NULL);         // lpftLastWriteTime

                // Open each subkey in turn, and look for a LoadType=
                // which is bogus.
                //
                for (iSubKey = 0; iSubKey < cSubKeys; ++iSubKey)
                {
                    HKEY hk;
                    TCHAR szSubKey[ cchRESOURCE ];
                    wsprintf (szSubKey, TEXT("Device%lu"), (LONG)iSubKey);

                    if (RegOpenKey (hkParams, szSubKey, &hk) == ERROR_SUCCESS)
                    {
                        DWORD dwLoadType;
                        DWORD dwType;
                        DWORD dwSize = sizeof(dwType);

                        if (RegQueryValueEx (hk,
                                             cszRegValueLOADTYPE,
                                             NULL,
                                             &dwType,
                                             (void *)&dwLoadType,
                                             &dwSize) == 0)
                        {
                            if (dwLoadType == 1)
                            {
                                dwLoadType = 0;
                                fFixedLoadType = TRUE;

                                RegSetValueEx (hk,
                                               cszRegValueLOADTYPE,
                                               0,
                                               REG_DWORD,
                                               (void *)&dwLoadType,
                                               sizeof(dwLoadType));
                            }
                        }

                        RegCloseKey (hk);
                    }
                }

                // If we fixed a LoadType value, try to restart the service.
                //
                if (fFixedLoadType)
                {
                    if (StartService (scDriver, 0, NULL))
                    {
                        ChangeServiceConfig (scDriver,
                                             SERVICE_NO_CHANGE,
                                             SERVICE_SYSTEM_START,
                                             SERVICE_NO_CHANGE,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL);
                    }
                }
            }
        }
    }


    // Clean up.  We'll return TRUE if a reboot is necessary, and FALSE
    // otherwise.
    //
    CloseServiceHandle (scDriver);
    CloseServiceHandle (scManager);
    return rc;
}


void ConfigureDriver (HWND hDlg, LPTSTR pszName)
{
    PIDRIVER  pid;
    HANDLE    hDriver;
    BOOL      fShowTrayVol;
    BOOL      fRestartDialog = FALSE;

    if ((pid = FindIDriverByName (pszName)) == NULL)
        return;

    fShowTrayVol = GetTrayVolumeEnabled();
    if (fShowTrayVol)
        SetTrayVolumeEnabled(FALSE);

    if ((hDriver = OpenDriver (pid->wszAlias, pid->wszSection, 0L)) == 0)
    {
        OpenDriverError(hDlg, pid->szDesc, pid->szFile);
    }
    else
    {
        DWORD dwStatus = GetDriverStatus (pid);
        BOOL fHadService = IsFlagSet (dwStatus, dwStatusSvcSTARTED) &&
                           IsFlagSet (dwStatus, dwStatusHASSERVICE);

        DRVCONFIGINFO   DrvConfigInfo;
        InitDrvConfigInfo(&DrvConfigInfo, pid);
        if ((SendDriverMessage(
                 hDriver,
                 DRV_CONFIGURE,
                 (LONG_PTR)hDlg,
                 (LONG_PTR)(LPDRVCONFIGINFO)&DrvConfigInfo) ==
            DRVCNF_RESTART))
        {
            iRestartMessage = 0;
            fRestartDialog = TRUE;
        }
        CloseDriver(hDriver, 0L, 0L);

        // If there was a functioning kernel-mode service in place before the
        // config dialog was presented, then we should verify that there is
        // still one in place now.  See FixDriverService() for details.
        //
        if (fHadService)
        {
            dwStatus = GetDriverStatus (pid);

            if (!IsFlagSet (dwStatus, dwStatusSvcSTARTED) ||
                !IsFlagSet (dwStatus, dwStatusHASSERVICE))
            {
                if (FixDriverService (pid))
                {
                    iRestartMessage = IDS_RESTART_NOSOUND;
                    fRestartDialog = TRUE;
                }
            }
        }
    }


    if (fShowTrayVol)
        SetTrayVolumeEnabled(TRUE);

    if (fRestartDialog)
    {
        DialogBox(myInstance,MAKEINTRESOURCE(DLG_RESTART),hDlg,RestartDlg);
    }
}


BOOL fDeviceHasMixers (LPTSTR pszName)
{
    HKEY  hk;
    UINT  ii;
    BOOL  rc = FALSE;

    if (RegOpenKey(HKEY_LOCAL_MACHINE, REGSTR_PATH_DRIVERS32, &hk))
    {
        return FALSE;
    }

    for (ii = 0; ; ++ii)
    {
        TCHAR  szLHS[ cchRESOURCE ];
        TCHAR  szRHS[ cchRESOURCE ];
        DWORD  dw1;
        DWORD  dw2;
        DWORD  dw3;

        dw1 = cchRESOURCE;
        dw3 = cchRESOURCE;
        if (RegEnumValue (hk, ii,  szLHS, &dw1,
                          0, &dw2, (LPBYTE)szRHS, &dw3) != ERROR_SUCCESS)
        {
            break;
        }

        if ( (GuessDriverClassFromAlias (szLHS) == dcMIXER) &&
             (!FileNameCmp (pszName, szRHS)) )
        {
            rc = TRUE;
            break;
        }
    }

    RegCloseKey (hk);
    return rc;
}
#endif // FIX_BUG_15451


TCHAR c_tszControlExeS[] = TEXT("control.exe %s");

BOOL RunJoyControlPanel(void)
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    TCHAR tsz[MAX_PATH];
    BOOL  fRtn;

    memset(&si, 0, sizeof(si));
    si.cb = sizeof(si);
    wsprintf(tsz, c_tszControlExeS, TEXT("joy.cpl"));
    if (CreateProcess(0, tsz, 0, 0, 0, 0, 0, 0, &si, &pi)) {
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    fRtn = TRUE;
    } else {
        fRtn = FALSE;
    }

    return fRtn;
}
