/*  REMOVE.C
**
**  Copyright (C) Microsoft, 1990, All Rights Reserved.
**
**  Multimedia Control Panel Applet for removing
**  device drivers.  See the ispec doc DRIVERS.DOC for more information.
**
**  History:
**
**      Thu Oct 17 1991 -by- Sanjaya
**      Created. Originally part of drivers.c
*/

#include <windows.h>
#include <mmsystem.h>
#include <winsvc.h>
#include <memory.h>
#include <string.h>
#include <stdlib.h>
#include <cpl.h>
#include <cphelp.h>
#include <regstr.h>

#include "drivers.h"
#include "sulib.h"

BOOL     SetValidAlias      (LPTSTR, LPTSTR);

static CONST TCHAR gszRunDLL32Path[]    = TEXT("\"%s\\RUNDLL32.EXE\" \"%s\\MMSYS.CPL\",mmWOW64MediaClassInstallerA \"%s\"%d");
static CONST TCHAR gszAliasValue[]      = TEXT("Alias");
static CONST TCHAR gszAliasWOW64Value[] = TEXT("AliasWOW64");
static CONST TCHAR gszWaveWOW64Value[]  = TEXT("WOW64");

#ifdef DOBOOT
BOOL     FindBootDriver     (TCHAR *);
PSTR     strstri            (LPTSTR, LPTSTR);
#endif // DOBOOT

/*
 *  RemoveService(szFile)
 *
 *  Remove the service corresponding to the file szFile
 *
 *  returns TRUE if successful, FALSE otherwise
 */

BOOL RemoveService(LPTSTR szFile)
{
    SC_HANDLE SCManagerHandle;
    SC_HANDLE ServiceHandle;
    TCHAR ServiceName[MAX_PATH];
    BOOL Status = FALSE;

    /*
     *  Extract the service name from the file name
     */

    {
        TCHAR drive[MAX_PATH], directory[MAX_PATH], ext[MAX_PATH];
        lsplitpath(szFile, drive, directory, ServiceName, ext);
    }

    /*
     *  First try and obtain a handle to the service controller
     */

    SCManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (SCManagerHandle == NULL)
    {

        TCHAR szMesg[MAXSTR];
        TCHAR szMesg2[MAXSTR];

        LoadString(myInstance, IDS_INSUFFICIENT_PRIVILEGE, szMesg, sizeof(szMesg)/sizeof(TCHAR));
        wsprintf(szMesg2, szMesg, szFile);
        MessageBox(hMesgBoxParent, szMesg2, szRemove, MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
        return FALSE;
    }


    ServiceHandle = OpenService(SCManagerHandle,
                                ServiceName,
                                SERVICE_ALL_ACCESS);
    if (ServiceHandle != NULL)
    {
        SERVICE_STATUS ServiceStatus;
        SC_LOCK ServicesDatabaseLock;

        /*
         *  Stop the service if possible.
         */

        ControlService(ServiceHandle, SERVICE_CONTROL_STOP, &ServiceStatus);

        /*
         *  Delete the service.
         *  We aren't detecting if we can just carry on.
         */

        /*
         *  Lock the service controller database to avoid deadlocks
         *  we have to loop because we can't wait
         */


        for (ServicesDatabaseLock = NULL;
            (ServicesDatabaseLock =
             LockServiceDatabase(SCManagerHandle))
            == NULL;
            Sleep(100))
        {
        }

        Status = DeleteService(ServiceHandle);

        UnlockServiceDatabase(ServicesDatabaseLock);

        CloseServiceHandle(ServiceHandle);
    }
    else
    {

        /*
         *  It's possible there was no services entry so the driver
         *  wasn't really installed after all.
         */

        LONG Error = GetLastError();

        if (Error == ERROR_FILE_NOT_FOUND ||
            Error == ERROR_PATH_NOT_FOUND ||
            Error == ERROR_SERVICE_DOES_NOT_EXIST)
        {
            Status = TRUE;
        }
    }

    CloseServiceHandle(SCManagerHandle);

    return Status;
}

/*
**  PostRemove()
**
**  Mark an installed driver for removal later AND remove the driver's entry
**  in SYSTEM.INI to avoid conflicts when we add or remove later.
*/
LONG_PTR PostRemove(PIDRIVER pIDriver, BOOL bLookAtRelated)
{

    TCHAR *keystr;
    TCHAR allkeystr[MAXSTR];
    TCHAR szfile[MAX_PATH];
    HANDLE hDriver;
    LONG_PTR Status = DRVCNF_CANCEL;
    LPTSTR pstr;


    GetPrivateProfileString(pIDriver->szSection,
                            pIDriver->szAlias,
                            pIDriver->szFile,
                            pIDriver->szFile,
                            MAX_PATH,
                            szSysIni);


    /*
     *  Remove parameters from file name
     */


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

    if (bLookAtRelated && (!bRelated || pIDriver->bRelated))
        wcscpy(szRestartDrv,  pIDriver->szDesc);

    /*
     *  If it's a kernel driver remove it from the config registry
     *  and services controller
     */

    if (pIDriver->KernelDriver)
    {

        Status = RemoveService(pIDriver->szFile) ? DRVCNF_RESTART : DRVCNF_CANCEL;

        if (Status == DRVCNF_CANCEL)
        {
            return DRVCNF_CANCEL;
        }

    }
    else
    {

        hDriver = OpenDriver(pIDriver->wszAlias, pIDriver->wszSection, 0L);

        if (hDriver)
        {

            /*
             *  Removal can fail so don't mark as deleted in this case
             */

            Status = SendDriverMessage(hDriver, DRV_REMOVE, 0L, 0L);
            CloseDriver(hDriver, 0L, 0L);

            if (Status == DRVCNF_CANCEL)
            {
                return DRVCNF_CANCEL;
            }
        }
    }

    // Remove the driver from the treeview,
    //  but don't free its structure
    //
    RemoveIDriver (hAdvDlgTree, pIDriver, FALSE);

    if (bLookAtRelated)
    {
        TCHAR allkeystr[MAXSTR];

        if (GetPrivateProfileString(szRelatedDesc, pIDriver->szAlias,
                                    allkeystr, allkeystr, sizeof(allkeystr) / sizeof(TCHAR), szControlIni))
        {
            int  i;
            TCHAR szTemp[MAXSTR];

            for (i = 1; infParseField(allkeystr, i, szTemp);i++)
            {
                PIDRIVER pid;

                if ((pid = FindIDriverByName (szTemp)) != NULL)
                {
                    if (PostRemove (pid, FALSE) == DRVCNF_RESTART)
                    {
                        Status = DRVCNF_RESTART;
                    }
                }
            }
        }
    }

    // Remove the driver entry from SYSTEM.INI so you don't
    // conflict with other drivers.

    GetPrivateProfileString(pIDriver->szSection, NULL, NULL,
                            allkeystr, sizeof(allkeystr) / sizeof(TCHAR), szSysIni);
    keystr = allkeystr;
    while (wcslen(keystr) > 0)
    {
        GetPrivateProfileString(pIDriver->szSection, keystr, NULL, szfile, sizeof(szfile) / sizeof(TCHAR), szSysIni);
        if (!FileNameCmp(pIDriver->szFile, szfile))
            RemoveDriverEntry(keystr, pIDriver->szFile, pIDriver->szSection, bLookAtRelated);
        keystr = &keystr[wcslen(keystr) + sizeof(TCHAR)];
    }
    return Status;
}



void RemoveDriverEntry (LPTSTR szKey, LPTSTR szFile, LPTSTR szSection, BOOL bLookAtRelated)
{

    /*
     *  Remove entry for loading driver
     */

    WritePrivateProfileString(szSection, szKey, NULL, szSysIni);

    /*
     *  Delete entry for parameters for this driver
     */

    WriteProfileString(szFile, szKey, NULL);

    /*
     *  Remove entry which says this is a user driver (as opposed to
     *  a pre-installed one).
     */

    WritePrivateProfileString(szUserDrivers, szKey, NULL, szControlIni);

    /*
     *  Remove description
     */

    WritePrivateProfileString(szDriversDesc, szFile, NULL, szControlIni);

    /*
     *  Remove links to related drivers
     */

    WritePrivateProfileString(szRelatedDesc, szKey, NULL, szControlIni);

#ifdef DOBOOT
    FindBootDriver(szKey);
#endif // DOBOOT

    if (bLookAtRelated)
        SetValidAlias(szKey, szSection);
}


/*
 *  SetValidAlias()
 *
 * Check to see if the alias removed would create a hole in the device
 * numbering scheme. If so switch the last device number with the deleted one.
 */
BOOL SetValidAlias(LPTSTR pstrType, LPTSTR pstrSection)
{
    TCHAR *keystr;
    static TCHAR allkeystr[MAXSTR];
    static TCHAR szExKey[MAXSTR], szExFile[MAXSTR], szExDesc[MAXSTR];
    BOOL bfound = FALSE, bExchange = FALSE;
    int val, maxval = 0, typelen, len;

    /*
     *  Getting length of alias
     */

    len = typelen = wcslen(pstrType);

    // If the last TCHAR on the type is a number don't consider it

    if (pstrType[typelen - 1] > TEXT('0') && pstrType[typelen - 1] <= TEXT('9'))
        typelen--;

    // Get all the aliases in the drivers section

    GetPrivateProfileString(pstrSection, NULL, NULL, allkeystr,
                            sizeof(allkeystr) / sizeof(TCHAR), szSysIni);
    keystr = allkeystr;
    while (*keystr != TEXT('\0'))
    {
        // Compare the root of the aliases
        if (!_wcsnicmp(keystr, pstrType, typelen) && ((keystr[typelen] <= TEXT('9') && keystr[typelen] > TEXT('0')) || keystr[typelen] == TEXT('\0')))
        {

            //We found a common alias
            bfound = TRUE;
            val = _wtoi(&keystr[typelen]);
            if (val > maxval)
            {
                maxval = val;
                wcscpy(szExKey, keystr);
            }
        }
        //Pointer to next alias
        keystr = &keystr[wcslen(keystr) + sizeof(TCHAR)];
    }
    //If we found one
    if (bfound)
    {
        if (len == typelen)
            bExchange = TRUE;
        else
            if (_wtoi(&pstrType[typelen]) < maxval)
            bExchange = TRUE;

        // We need to exchange it with the one we found
        if (bExchange)
        {
            //Exchanging the one in the drivers section in system.ini
            GetPrivateProfileString(pstrSection, szExKey, NULL, szExFile,
                                    sizeof(szExFile) / sizeof(TCHAR), szSysIni);
            WritePrivateProfileString(pstrSection, szExKey, NULL, szSysIni);
            WritePrivateProfileString(pstrSection, pstrType, szExFile, szSysIni);

#ifdef TRASHDRIVERDESC
            //Exchanging the one in the drivers description section of control.ini
            GetPrivateProfileString(szDriversDesc, szExKey, NULL, szExDesc, sizeof(szExFile) / sizeof(TCHAR), szControlIni);
            WritePrivateProfileString(szDriversDesc, szExKey, NULL, szControlIni);
            WritePrivateProfileString(szDriversDesc, pstrType, szExDesc, szControlIni);
#endif

            //If any related drivers were present under old alias switch them
            GetPrivateProfileString(szRelatedDesc, szExKey, NULL, szExDesc, sizeof(szExFile) / sizeof(TCHAR), szControlIni);

            if (wcslen(szExDesc))
            {
                WritePrivateProfileString(szRelatedDesc, szExKey, NULL, szControlIni);
                WritePrivateProfileString(szRelatedDesc, pstrType, szExDesc, szControlIni);
            }

            //If user installed driver under old alias switch them
            GetPrivateProfileString(szUserDrivers, szExKey, NULL, szExDesc, sizeof(szExFile) / sizeof(TCHAR), szControlIni);

            if (wcslen(szExDesc))
            {
                WritePrivateProfileString(szUserDrivers, szExKey, NULL, szControlIni);
                WritePrivateProfileString(szUserDrivers, pstrType, szExDesc, szControlIni);
            }

#ifdef DOBOOT
            if (FindBootDriver(szExKey))
            {
                static TCHAR szTemp[MAXSTR];

                GetPrivateProfileString(szBoot, szDrivers, szTemp, szTemp,
                                        sizeof(szTemp) / sizeof(TCHAR), szSysIni);
                strcat(szTemp, TEXT(" "));
                strcat(szTemp, pstrType);
                WritePrivateProfileString(szBoot, szDrivers, szTemp, szSysIni);
            }
#endif // DOBOOT

        }
    }
    return(bExchange);
}

int FileNameCmp(TCHAR far *pch1, TCHAR far *pch2)
{
    LPTSTR pchEOS;

    while (*pch1 == TEXT(' ')) pch1++; // eat spaces
    while (*pch2 == TEXT(' ')) pch2++; // eat spaces

    for (pchEOS = pch1; *pchEOS && *pchEOS != TEXT(' '); pchEOS++);

    return _wcsnicmp(pch1, pch2, (size_t)(pchEOS - pch1));
}

#ifdef DOBOOT

PSTR strstri(LPTSTR pszStr, LPTSTR pszKey)
{
    while (pszStr)
        if (!_strnicmp(pszStr, pszKey, lstrlen(pszKey)))
            return(pszStr);
        else
            pszStr++;
    return(NULL);
}

/*
 *   FindBootDriver()
 *  Checks to see if the driver alias is on the drivers line of the
 *  boot section. If so the alias is removed from the line.
 */

BOOL FindBootDriver(TCHAR *szKey)
{
    TCHAR *ptr;
    int wKeyLen = (int)wcslen(szKey);
    TCHAR *endkey;
    static TCHAR szDriverline[MAXSTR];

    GetPrivateProfileString(TEXT("boot"), TEXT("drivers"), szDriverline, szDriverline,
                            MAX_PATH, szSysIni);
    ptr = strstri(szDriverline, szKey);
    if (ptr)
    {

        if ((((ptr != szDriverline) && (*(ptr - 1) == TEXT(' ') )) ||
             (ptr == szDriverline)) &&
            (*(ptr + wKeyLen) == TEXT(' ') || *(ptr + wKeyLen) == NULL))
        {
            endkey = ptr + wKeyLen;
            while (*endkey)
                *ptr++ = *endkey++;
            *ptr = NULL;
            WritePrivateProfileString(TEXT("boot"), TEXT("drivers"), szDriverline,
                                      szSysIni);
            return(TRUE);
        }
    }
    return(FALSE);
}

#endif // DOBOOT

// Steal use of function in midi.c to delete a reg subtree.
LONG SHRegDeleteKey(HKEY hKey, LPCTSTR lpSubKey);

//****************************************************************************
// Function: mystrtok()
//
// Purpose: Returns a pointer to the next token in a string.
//
// Parameters:
//      SrcString   String containing token(s)
//      Seps        Set of delimiter characters
//      State       Pointer to a char* to hold state info
// Return Code:
//      Ptr to next token, or NULL if no tokens left
//
// Comments:
//      Fixes problem with standard strtok, which can't be called recursively.
//
//****************************************************************************
LPTSTR mystrtok(LPTSTR SrcString, LPCTSTR Seps, LPTSTR FAR *State)
{
    LPTSTR ThisString;
    LPTSTR NextString;

    // If Seps is NULL, use default separators
    if (!Seps)
    {
        Seps = TEXT(" ,\t");  // space, comma, tab chars
    }

    if (SrcString)
        ThisString = SrcString;
    else
        ThisString = *State;

    // Find beginning of the current string
    ThisString = ThisString + wcsspn(ThisString,Seps);
    if (ThisString[0]==TEXT('\0'))
        return NULL;

    // Find the end of the current string
    NextString = ThisString + wcscspn(ThisString,Seps);
    if (NextString[0]!=TEXT('\0'))
    {
        *NextString++=TEXT('\0');
    }

    *State = NextString;
    return ThisString;
}

BOOL RemoveDriver(IN HDEVINFO         DeviceInfoSet,
                  IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
                 )
{
    BOOL bRet = FALSE;          // Return value

    TCHAR szDriverKey[MAX_PATH]; // Value of Driver's key in Enum branch
    TCHAR *pszDrvInst;           // Driver's instance, e.g. "0000", "0001", etc.

    HKEY hkDevReg  = NULL;      // Key to Driver portion of registry (e.g. classguid\0000)
    HKEY hkDrivers32 = NULL;    // Key to Drivers32 portion of registry
    HKEY hkDrivers = NULL;      // Key to classguid\0000\Drivers

    TCHAR szSubClasses[256];     // List of subclasses to process
    TCHAR *strtok_State;         // strtok state
    TCHAR *pszClass;             // Information about e.g. classguid\0000\Drivers\wave
    HKEY hkClass;

    DWORD idxR3DriverName;      // Information about e.g. classguid\0000\Drivers\wave\foo.drv
    HKEY hkR3DriverName;
    TCHAR szR3DriverName[64];

    TCHAR szAlias[64];           // Alias in Drivers32 (e.g. wave1)
    TCHAR szDriver[64];          // Name of driver

    DWORD cbLen;

    LPCTSTR szAliasStringToUse;  // Pointer to the Alias value name to use
    BOOL bIsWOW64Process = FALSE;// TRUE if we're running under WOW64

    //
    // If we're running in WOW64, we need to use a different Alias string so that
    // we don't overwrite the 64-bit alias string
    //
    if( IsWow64Process(GetCurrentProcess(), &bIsWOW64Process)
    &&  bIsWOW64Process )
    {
        szAliasStringToUse = gszAliasWOW64Value;
    }
    else
    {
        szAliasStringToUse = gszAliasValue;
    }

    // Get the Drivers key value under the device's Enum branch,
    // e.g. something like "{4D36E96C-E325-11CE-BFC1-08002BE10318}\0000"
    SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
                                     DeviceInfoData,
                                     SPDRP_DRIVER ,
                                     NULL,
                                     (LPBYTE)szDriverKey,
                                     MAX_PATH,
                                     NULL);

    // Get everything after the last \ character
    pszDrvInst = wcsrchr(szDriverKey,TEXT('\\'));
    if (!pszDrvInst)
    {
        goto RemoveDrivers32_exit;
    }
    pszDrvInst++;
    // Now pszDrvInst points to a string with the Driver Instance, e.g. "0000"

    // Open the Drivers32 section of the registry
    if (RegOpenKey(HKEY_LOCAL_MACHINE,
                   TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Drivers32"),
                   &hkDrivers32))
    {
        goto RemoveDrivers32_exit;
    }

    // Open the Driver reg key
    hkDevReg = SetupDiOpenDevRegKey(DeviceInfoSet,
                                    DeviceInfoData,
                                    DICS_FLAG_GLOBAL,
                                    0,
                                    DIREG_DRV,
                                    KEY_ALL_ACCESS);
    if (hkDevReg == INVALID_HANDLE_VALUE)
    {
        goto RemoveDrivers32_exit;
    }

    // Enumerate through supporter classes in the Drivers subkey
    if (RegOpenKey(hkDevReg, TEXT("Drivers"), &hkDrivers))
    {
        goto RemoveDrivers32_exit;
    }

    // Read the SubClasses key to determine which subclasses to process
    cbLen=sizeof(szSubClasses);
    if (RegQueryValueEx(hkDrivers, TEXT("Subclasses"), NULL, NULL, (LPBYTE)szSubClasses, &cbLen))
    {
        goto RemoveDrivers32_exit;
    }

    // Enumerate all the subclasses
    for (
        pszClass = mystrtok(szSubClasses,NULL,&strtok_State);
        pszClass;
        pszClass = mystrtok(NULL,NULL,&strtok_State)
        )
    {
#ifdef _WIN64
        //
        // Check for magic WaveWOW64 value
        if( 0 == _wcsnicmp( pszClass, gszWaveWOW64Value, wcslen(gszWaveWOW64Value) ) )
        {
            // Thunk the installation to the 32-bit mmsys.cpl installer
            mmWOW64ThunkMediaClassInstaller(DIF_REMOVE, DeviceInfoSet, DeviceInfoData);

            continue;
        }
#endif //_WIN64

        // Open up each subclass
        if (RegOpenKey(hkDrivers, pszClass, &hkClass))
        {
            continue;
        }

        // Under each class is a set of driver name subkeys.
        // For each driver (e.g. foo1.drv, foo2.drv, etc.)
        for (idxR3DriverName = 0;
            !RegEnumKey(hkClass, idxR3DriverName, szR3DriverName, sizeof(szR3DriverName)/sizeof(TCHAR));
            idxR3DriverName++)
        {

            // Open the key to the driver name
            if (RegOpenKey(hkClass, szR3DriverName, &hkR3DriverName))
            {
                continue;
            }

            // Get the value of Driver under the driver name key
            cbLen = sizeof(szDriver);
            if (!RegQueryValueEx(hkR3DriverName, TEXT("Driver"), NULL, NULL, (LPBYTE)szDriver, &cbLen))
            {
                // Send the driver a DRV_REMOVE message to the driver
                HANDLE hDriver;

                hDriver = OpenDriver(szDriver, NULL, 0L);

                if (hDriver)
                {
                    SendDriverMessage(hDriver, DRV_REMOVE, 0L, 0L);
                    CloseDriver(hDriver, 0L, 0L);
                }
            }

            // Get the value of Alias under the driver name key
            cbLen = sizeof(szAlias);
            if (!RegQueryValueEx(hkR3DriverName, szAliasStringToUse, NULL, NULL, (LPBYTE)szAlias, &cbLen))
            {
                // Delete the corresponding entry in Drivers32
                RegDeleteValue(hkDrivers32,szAlias);
            }

            // Close the Driver Name key
            RegCloseKey(hkR3DriverName);
        }
        // Close the class key
        RegCloseKey(hkClass);
    }

    bRet = TRUE;

    RemoveDrivers32_exit:

    if (hkDrivers32)    RegCloseKey(hkDrivers32);
    if (hkDevReg)       RegCloseKey(hkDevReg);
    if (hkDrivers)      RegCloseKey(hkDrivers);

    return bRet;
}

// The driver's private registry section is located in something like:
// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E96C-E325-11CE-BFC1-08002BE10318}\xxxx
// where xxxx is the device instance (e.g. 0000, 0001, etc.)
// These last four digits are used to index into the driver's MediaResources section.

// For example, suppose a device has a driver instance
// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E96C-E325-11CE-BFC1-08002BE10318}\0001
// and under that entry there is a Drivers\wave\foo.drv, meaning that the foo.drv driver supports a wave
// API.
// In this case, there would be an entry in
// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\MediaResources\wave\foo.drv<0001>
//
// On removal, we need to delete that entry.
BOOL RemoveMediaResources(IN HDEVINFO         DeviceInfoSet,
                          IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
                         )


{
    BOOL bRet = FALSE;          // Return value

    TCHAR szDriverKey[MAX_PATH]; // Value of Driver's key in Enum branch
    TCHAR *pszDrvInst;           // Driver's instance, e.g. "0000", "0001", etc.

    HKEY hkDevReg  = NULL;      // Key to Driver portion of registry (e.g. classguid\0000)
    HKEY hkDrivers = NULL;      // Key to classguid\0000\Drivers
    HKEY hkMR      = NULL;      // Handle to MediaResources section

    TCHAR szSubClasses[256];     // List of subclasses to process
    TCHAR *strtok_State;         // strtok state
    TCHAR *pszClass;             // Information about e.g. classguid\0000\Drivers\wave
    HKEY hkClass;

    DWORD idxR3DriverName;      // Information about e.g. classguid\0000\Drivers\wave\foo.drv
    HKEY hkR3DriverName;
    TCHAR szR3DriverName[64];

    TCHAR szDriver[64];          // Driver name (e.g. foo.drv)
    DWORD cbLen;                // Size of szDriver

    TCHAR szDevNode[MAX_PATH+1];         // Path to driver's reg entry
    TCHAR szSoftwareKey[MAX_PATH+1];     // Value of SOFTWAREKEY

    // Open Media Resources section of registry
    if (RegOpenKey(HKEY_LOCAL_MACHINE, REGSTR_PATH_MEDIARESOURCES, &hkMR))
    {
        goto RemoveMediaResources_exit;
    }

    // Get the Drivers key value under the device's Enum branch,
    // e.g. something like "{4D36E96C-E325-11CE-BFC1-08002BE10318}\0000"
    SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
                                     DeviceInfoData,
                                     SPDRP_DRIVER ,
                                     NULL,
                                     (LPBYTE)szDriverKey,
                                     MAX_PATH,
                                     NULL);

    // Get everything after the last \ character
    pszDrvInst = wcsrchr(szDriverKey,TEXT('\\'));
    if (!pszDrvInst)
    {
        goto RemoveMediaResources_exit;
    }
    pszDrvInst++;
    // Now pszDrvInst points to a string with the Driver Instance, e.g. "0000"

    // Get full path to driver key
    wsprintf(szDevNode,
             TEXT("%s\\%s"),
             REGSTR_PATH_CLASS_NT,
             (LPTSTR)szDriverKey);

    // Open the Driver reg key
    hkDevReg = SetupDiOpenDevRegKey(DeviceInfoSet,
                                    DeviceInfoData,
                                    DICS_FLAG_GLOBAL,
                                    0,
                                    DIREG_DRV,
                                    KEY_ALL_ACCESS);
    if (hkDevReg == INVALID_HANDLE_VALUE)
    {
        goto RemoveMediaResources_exit;
    }

    // Enumerate through supporter classes in the Drivers subkey
    if (RegOpenKey(hkDevReg, TEXT("Drivers"), &hkDrivers))
    {
        goto RemoveMediaResources_exit;
    }

    // Read the SubClasses key to determine which subclasses to process
    cbLen=sizeof(szSubClasses);
    if (RegQueryValueEx(hkDrivers, TEXT("Subclasses"), NULL, NULL, (LPBYTE)szSubClasses, &cbLen))
    {
        goto RemoveMediaResources_exit;
    }

    // Enumerate all the subclasses
    for (
        pszClass = mystrtok(szSubClasses,NULL,&strtok_State);
        pszClass;
        pszClass = mystrtok(NULL,NULL,&strtok_State)
        )
    {
        if (RegOpenKey(hkDrivers, pszClass, &hkClass))
        {
            continue;
        }

        // Under each class is a set of driver name subkeys.
        // For each driver (e.g. foo1.drv, foo2.drv, etc.)
        for (idxR3DriverName = 0;
            !RegEnumKey(hkClass, idxR3DriverName, szR3DriverName, sizeof(szR3DriverName)/sizeof(TCHAR));
            idxR3DriverName++)
        {

            // Open the key to the driver name
            if (RegOpenKey(hkClass, szR3DriverName, &hkR3DriverName))
            {
                continue;
            }

            // Get the value of Driver in under the driver name key
            cbLen = sizeof(szDriver);
            if (!RegQueryValueEx(hkR3DriverName, TEXT("Driver"), NULL, NULL, (LPBYTE)szDriver, &cbLen))
            {
                TCHAR szR3Path[256];

                // Create a path to the MediaResources entry to be deleted
                wsprintf(szR3Path,
                         TEXT("%s\\%s\\%s<%s>"),
                         REGSTR_PATH_MEDIARESOURCES,
                         (LPTSTR)pszClass,
                         (LPTSTR)szDriver,
                         (LPTSTR)pszDrvInst);
                // Delete the key
                SHRegDeleteKey(HKEY_LOCAL_MACHINE, szR3Path);
            }
            // Close the Driver Name key
            RegCloseKey(hkR3DriverName);
        }

        // Close the class key in the devnode
        RegCloseKey(hkClass);

        // Backup mechanism, in case we missed something.
        // This shouldn't be necessary, but Win98 does it.

        // Open the class key in MediaResources
        if (RegOpenKey(hkMR, pszClass, &hkClass))
        {
            continue;
        }
        // Count the number of subkeys under the class key
        // We're gonna do this backwards because we'll be deleting keys later
        for (idxR3DriverName = 0;
            !RegEnumKey(hkClass, idxR3DriverName, szR3DriverName, sizeof(szR3DriverName)/sizeof(TCHAR));
            idxR3DriverName++)
        {
            ;
        }

        // For each driver subkey, working backwards.
        // Subkeys are e.g. msacm.iac2, msacm.imaadpcm, etc.
        for (idxR3DriverName--;
            ((int)idxR3DriverName >= 0) &&
            !RegEnumKey(hkClass, idxR3DriverName, szR3DriverName, sizeof(szR3DriverName)/sizeof(TCHAR));
            idxR3DriverName--)
        {
            // Open the driver key
            if (RegOpenKey(hkClass, szR3DriverName, &hkR3DriverName))
            {
                continue;
            }

            // Query the value of "SOFTWAREKEY"
            szSoftwareKey[0]=TEXT('\0');      // Init to safe value in case call fails

            cbLen = sizeof(szSoftwareKey);
            RegQueryValueEx(hkR3DriverName, TEXT("SOFTWAREKEY"), NULL, NULL, (LPBYTE)szSoftwareKey, &cbLen);

            // Close now, since we might delete in next line
            RegCloseKey(hkR3DriverName);

            // If the value of "SOFTWAREKEY" matches the path to the devnode, delete the key
            if (!lstrcmpi(szSoftwareKey, szDevNode))
            {
                SHRegDeleteKey(hkClass, szR3DriverName);
            }
        }

        // Close the class key in MediaResources
        RegCloseKey(hkClass);
    }

    bRet = TRUE;

    RemoveMediaResources_exit:
    if (hkDevReg)   RegCloseKey(hkDevReg);
    if (hkDrivers)  RegCloseKey(hkDrivers);
    if (hkMR)       RegCloseKey(hkMR);

    return bRet;
}

// Clear out entries in the Driver's branch of the registry, e.g. in {4D36E96C-E325-11CE-BFC1-08002BE10318}\0000
BOOL RemoveDriverInfo(IN HDEVINFO         DeviceInfoSet,
                      IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
                     )
{
    HKEY hkDevReg;      // Key to Driver portion of registry (e.g. classguid\0000)

    // Remove entries in the driver's reg section
    hkDevReg = SetupDiOpenDevRegKey(DeviceInfoSet,
                                    DeviceInfoData,
                                    DICS_FLAG_GLOBAL,
                                    0,
                                    DIREG_DRV,
                                    KEY_ALL_ACCESS);
    if (hkDevReg == INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }

    // Delete any entries that might cause trouble
    RegDeleteValue(hkDevReg,REGSTR_VAL_DEVLOADER);
    RegDeleteValue(hkDevReg,REGSTR_VAL_DRIVER);
    RegDeleteValue(hkDevReg,REGSTR_VAL_ENUMPROPPAGES);
    RegDeleteValue(hkDevReg,TEXT("NTMPDriver"));
    RegDeleteValue(hkDevReg,TEXT("AssociatedFilters"));
    RegDeleteValue(hkDevReg,TEXT("FDMA"));
    RegDeleteValue(hkDevReg,TEXT("DriverType"));

    // Blow away the Drivers subtree
    SHRegDeleteKey(hkDevReg,TEXT("Drivers"));

    // For future use, allow a key under which everything gets blown away
    SHRegDeleteKey(hkDevReg,TEXT("UnretainedSettings"));

    RegCloseKey(hkDevReg);

    return TRUE;
}

// Clear out entries in the Device's Enum branch of the registry:
BOOL RemoveDeviceInfo(IN HDEVINFO         DeviceInfoSet,
                      IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
                     )
{
    // Remove the Driver key. It looks something like "Driver = {4D36E96C-E325-11CE-BFC1-08002BE10318}\0000"
    // !!NO don't remove driver key, or else on driver upgrade system loses track of node & creates a new one
    //    SetupDiSetDeviceRegistryProperty(DeviceInfoSet, DeviceInfoData, SPDRP_DRIVER , NULL, 0);

    // Remove the Service key.
    // Make sure before doing anything else that there is no service property for
    // this device instance.  This allows us to know whether we should clean up the
    // device instance if we boot and find that it's no longer present.
    SetupDiSetDeviceRegistryProperty(DeviceInfoSet, DeviceInfoData, SPDRP_SERVICE, NULL, 0);

    return TRUE;
}


/* 5/14/98 andyraf for NT5 */
/* Media_RemoveDevice
 *
 * This function gets called on driver removal (DIF_REMOVE) and driver installation (DIF_INSTALL).
 * It cleans up all the registry entries associated with the driver.
 */
DWORD Media_RemoveDevice(IN HDEVINFO         DeviceInfoSet,
                         IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
                        )
{
#if 0
    // Don't know how to do this on NT5
    if ((diFunction == DIF_REMOVE) &&
        (lpdi->Flags & DI_CLASSINSTALLPARAMS) &&
        (((LPREMOVEDEVICE_PARAMS)lpdi->lpClassInstallParams)->dwFlags & DI_REMOVEDEVICE_CONFIGSPECIFIC))
    {
        return ERROR_DI_DO_DEFAULT;
    }

    // Not needed on NT5??
    CleanupDummySysIniDevs();           //remove the wave=*.drv and midi=*.drv dummy devices.
#endif

#if 0   // We'll allow people to remove these drivers for now
    if (IsSpecialDriver(DeviceInfoSet, DeviceInfoData))
    {
        return NO_ERROR;
    }
#endif

    // Send DRV_REMOVE to each driver and clean out Drivers32 section of registry
    RemoveDriver        (DeviceInfoSet, DeviceInfoData);

    // Clean out MediaResources section of registry
    RemoveMediaResources(DeviceInfoSet, DeviceInfoData);

    // Clean out driver's classguid\instance section of registry
    RemoveDriverInfo    (DeviceInfoSet, DeviceInfoData);

    // Clean out device's enum section of registry
    RemoveDeviceInfo    (DeviceInfoSet, DeviceInfoData);

    return ERROR_DI_DO_DEFAULT;
}

#if 0   // Unused at present
BOOL AddDrivers32(IN HDEVINFO         DeviceInfoSet,
                  IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
                 )
{
    BOOL bRet = FALSE;          // Return value

    TCHAR szDriverKey[MAX_PATH]; // Value of Driver's key in Enum branch
    TCHAR *pszDrvInst;           // Driver's instance, e.g. "0000", "0001", etc.

    HKEY hkDevReg  = NULL;      // Key to Driver portion of registry (e.g. classguid\0000)
    HKEY hkDrivers32 = NULL;    // Key to Drivers32 portion of registry
    HKEY hkDrivers = NULL;      // Key to classguid\0000\Drivers
    HKEY hkDriversDesc = NULL;  // Key to drivers.desc portion of registry

    TCHAR szSubClasses[256];     // List of subclasses to process
    TCHAR *strtok_State;         // strtok state
    TCHAR *pszClass;             // Information about e.g. classguid\0000\Drivers\wave
    HKEY hkClass;

    DWORD idxR3DriverName;      // Information about e.g. classguid\0000\Drivers\wave\foo.drv
    HKEY hkR3DriverName;
    TCHAR szR3DriverName[64];

    TCHAR szAlias[64];           // Alias in Drivers32 (e.g. wave1)

    TCHAR szDriver[64];          // Name of driver
    TCHAR szDescription[MAX_PATH];

    DWORD cbLen;

    // Get the Drivers key value under the device's Enum branch,
    // e.g. something like "{4D36E96C-E325-11CE-BFC1-08002BE10318}\0000"
    SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
                                     DeviceInfoData,
                                     SPDRP_DRIVER ,
                                     NULL,
                                     (LPBYTE)szDriverKey,
                                     MAX_PATH,
                                     NULL);

    // Get everything after the last \ character
    pszDrvInst = strrchr(szDriverKey,TEXT('\\'));
    if (!pszDrvInst)
    {
        goto RemoveDrivers32_exit;
    }
    pszDrvInst++;
    // Now pszDrvInst points to a string with the Driver Instance, e.g. "0000"

    // Open the Drivers32 section of the registry
    if (RegOpenKey(HKEY_LOCAL_MACHINE,
                   TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Drivers32"),
                   &hkDrivers32))
    {
        goto RemoveDrivers32_exit;
    }

    // If we're adding a driver, need to open key to drivers.desc also
    if (RegOpenKey(HKEY_LOCAL_MACHINE,
                   TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\drivers.desc"),
                   &hkDriversDesc))
    {
        goto RemoveDrivers32_exit;
    }

    // Open the Driver reg key
    hkDevReg = SetupDiOpenDevRegKey(DeviceInfoSet,
                                    DeviceInfoData,
                                    DICS_FLAG_GLOBAL,
                                    0,
                                    DIREG_DRV,
                                    KEY_ALL_ACCESS);
    if (hkDevReg == INVALID_HANDLE_VALUE)
    {
        goto RemoveDrivers32_exit;
    }

    // Enumerate through supporter classes in the Drivers subkey
    if (RegOpenKey(hkDevReg, TEXT("Drivers"), &hkDrivers))
    {
        goto RemoveDrivers32_exit;
    }

    // Read the SubClasses key to determine which subclasses to process
    cbLen=sizeof(szSubClasses);
    if (RegQueryValueEx(hkDrivers, TEXT("Subclasses"), NULL, NULL, (LPBYTE)szSubClasses, &cbLen))
    {
        goto RemoveDrivers32_exit;
    }

    // Enumerate all the subclasses
    for (
        pszClass = mystrtok(szSubClasses,NULL,&strtok_State);
        pszClass;
        pszClass = mystrtok(NULL,NULL,&strtok_State)
        )
    {
        // Open up each subclass
        if (RegOpenKey(hkDrivers, pszClass, &hkClass))
        {
            continue;
        }

        // Under each class is a set of driver name subkeys.
        // For each driver (e.g. foo1.drv, foo2.drv, etc.)
        for (idxR3DriverName = 0;
            !RegEnumKey(hkClass, idxR3DriverName, szR3DriverName, sizeof(szR3DriverName)/sizeof(TCHAR));
            idxR3DriverName++)
        {

            // Open the key to the driver name
            if (RegOpenKey(hkClass, szR3DriverName, &hkR3DriverName))
            {
                continue;
            }

            // Get driver name
            cbLen = sizeof(szDriver);
            if (!RegQueryValueEx(hkR3DriverName, TEXT("Driver"), NULL, NULL, (LPBYTE)szDriver, &cbLen))
            {
                // Create the alias
                wsprintf(szAlias,TEXT("%s.%s<%s>"),(LPTSTR)pszClass,(LPTSTR)szDriver,(LPTSTR)pszDrvInst);

                // Write into Drivers32
                RegSetValueExA(hkDrivers32,szAlias,0,REG_SZ,(PBYTE)szDriver,(wcslen(szDriver)*sizeof(TCHAR)) + sizeof(TCHAR));

                // Write alias back into driver's reg area
                RegSetValueExA(hkR3DriverName,TEXT("Alias"),0,REG_SZ,(PBYTE)szAlias,(wcslen(szAlias)*sizeof(TCHAR)) + sizeof(TCHAR));
            }

            // Write out Description
            // Get driver description
            cbLen = sizeof(szDescription);
            if (!RegQueryValueEx(hkR3DriverName, TEXT("Description"), NULL, NULL, (LPBYTE)szDescription, &cbLen))
            {
                RegSetValueExA(hkDriversDesc,szDriver,0,REG_SZ,(PBYTE)szDescription,(wcslen(szDescription)*sizeof(TCHAR)) + sizeof(TCHAR));
            }

            // Close the Driver Name key
            RegCloseKey(hkR3DriverName);
        }
        // Close the class key
        RegCloseKey(hkClass);
    }

    bRet = TRUE;

    RemoveDrivers32_exit:

    if (hkDrivers32)    RegCloseKey(hkDrivers32);
    if (hkDevReg)       RegCloseKey(hkDevReg);
    if (hkDrivers)      RegCloseKey(hkDrivers);
    if (hkDriversDesc)  RegCloseKey(hkDriversDesc);

    return bRet;
}
#endif

#if 0 // Unused at present
// The driver's private registry section is located in something like:
// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E96C-E325-11CE-BFC1-08002BE10318}\xxxx
// where xxxx is the device instance (e.g. 0000, 0001, etc.)
// These last four digits are used to index into the driver's MediaResources section.

// For example, suppose a device has a driver instance
// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E96C-E325-11CE-BFC1-08002BE10318}\0001
// and under that entry there is a Drivers\wave\foo.drv, meaning that the foo.drv driver supports a wave
// API.
// In this case, there would be an entry in
// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\MediaResources\wave\foo.drv<0001>
//
// On removal, we need to delete that entry.
BOOL AddMediaResources(IN HDEVINFO         DeviceInfoSet,
                       IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
                      )


{
    BOOL bRet = FALSE;          // Return value

    TCHAR szDriverKey[MAX_PATH]; // Value of Driver's key in Enum branch
    TCHAR *pszDrvInst;           // Driver's instance, e.g. "0000", "0001", etc.

    HKEY hkDevReg  = NULL;      // Key to Driver portion of registry (e.g. classguid\0000)
    HKEY hkDrivers = NULL;      // Key to classguid\0000\Drivers
    HKEY hkMR      = NULL;      // Handle to MediaResources section

    TCHAR szSubClasses[256];     // List of subclasses to process
    TCHAR *strtok_State;         // strtok state
    TCHAR *pszClass;             // Information about e.g. classguid\0000\Drivers\wave
    HKEY hkClass;

    DWORD idxR3DriverName;      // Information about e.g. classguid\0000\Drivers\wave\foo.drv
    HKEY hkR3DriverName;
    TCHAR szR3DriverName[64];

    TCHAR szDriver[64];          // Driver name (e.g. foo.drv)
    DWORD cbLen;                // Size of szDriver

    TCHAR szDevNode[MAX_PATH+1];         // Path to driver's reg entry
    TCHAR szSoftwareKey[MAX_PATH+1];     // Value of SOFTWAREKEY

    // Open Media Resources section of registry
    if (RegOpenKey(HKEY_LOCAL_MACHINE, REGSTR_PATH_MEDIARESOURCES, &hkMR))
    {
        goto RemoveMediaResources_exit;
    }

    // Get the Drivers key value under the device's Enum branch,
    // e.g. something like "{4D36E96C-E325-11CE-BFC1-08002BE10318}\0000"
    SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
                                     DeviceInfoData,
                                     SPDRP_DRIVER ,
                                     NULL,
                                     (LPBYTE)szDriverKey,
                                     MAX_PATH,
                                     NULL);

    // Get everything after the last \ character
    pszDrvInst = strrchr(szDriverKey,TEXT('\\'));
    if (!pszDrvInst)
    {
        goto RemoveMediaResources_exit;
    }
    pszDrvInst++;
    // Now pszDrvInst points to a string with the Driver Instance, e.g. "0000"

    // Get full path to driver key
    wsprintf(szDevNode,
             TEXT("%s\\%s"),
             REGSTR_PATH_CLASS_NT,
             (LPTSTR)szDriverKey);

    // Open the Driver reg key
    hkDevReg = SetupDiOpenDevRegKey(DeviceInfoSet,
                                    DeviceInfoData,
                                    DICS_FLAG_GLOBAL,
                                    0,
                                    DIREG_DRV,
                                    KEY_ALL_ACCESS);
    if (hkDevReg == INVALID_HANDLE_VALUE)
    {
        goto RemoveMediaResources_exit;
    }

    // Enumerate through supporter classes in the Drivers subkey
    if (RegOpenKey(hkDevReg, TEXT("Drivers"), &hkDrivers))
    {
        goto RemoveMediaResources_exit;
    }

    // Read the SubClasses key to determine which subclasses to process
    cbLen=sizeof(szSubClasses);
    if (RegQueryValueEx(hkDrivers, TEXT("Subclasses"), NULL, NULL, (LPBYTE)szSubClasses, &cbLen))
    {
        goto RemoveMediaResources_exit;
    }

    // Enumerate all the subclasses
    for (
        pszClass = mystrtok(szSubClasses,NULL,&strtok_State);
        pszClass;
        pszClass = mystrtok(NULL,NULL,&strtok_State)
        )
    {
        if (RegOpenKey(hkDrivers, pszClass, &hkClass))
        {
            continue;
        }

        // Under each class is a set of driver name subkeys.
        // For each driver (e.g. foo1.drv, foo2.drv, etc.)
        for (idxR3DriverName = 0;
            !RegEnumKey(hkClass, idxR3DriverName, szR3DriverName, sizeof(szR3DriverName)/sizeof(TCHAR));
            idxR3DriverName++)
        {

            // Open the key to the driver name
            if (RegOpenKey(hkClass, szR3DriverName, &hkR3DriverName))
            {
                continue;
            }

            // Get the value of Driver in under the driver name key
            cbLen = sizeof(szDriver);
            if (!RegQueryValueEx(hkR3DriverName, TEXT("Driver"), NULL, NULL, (LPBYTE)szDriver, &cbLen))
            {
                HKEY hkMRClass;
                HKEY hkMRDriver;
                TCHAR szMRDriver[256];

                // Create the class key if it doesn't already exist
                if (!RegCreateKey(hkMR,pszClass,&hkMRClass))
                {
                    continue;
                }

                // Create the driver key if it doesn't already exist
                wsprintf(szMRDriver,
                         TEXT("%s<%s>"),
                         (LPTSTR)szDriver,
                         (LPTSTR)pszDrvInst);

                if (!RegCreateKey(hkMRClass,szMRDriver,&hkMRDriver))
                {
                    RegCloseKey(hkMRClass);
                    continue;
                }

                // Migrate the values from the driver into the MediaResources key
                // First write out driver name
                // FEATURE: Not implemented yet.

                RegCloseKey(hkMRClass);
                RegCloseKey(hkMRDriver);
            }
            // Close the Driver Name key
            RegCloseKey(hkR3DriverName);
        }

        // Close the class key in the devnode
        RegCloseKey(hkClass);
    }

    bRet = TRUE;

    RemoveMediaResources_exit:
    if (hkDevReg)   RegCloseKey(hkDevReg);
    if (hkDrivers)  RegCloseKey(hkDrivers);
    if (hkMR)       RegCloseKey(hkMR);

    return bRet;
}
#endif

#ifdef _WIN64
void mmWOW64ThunkMediaClassInstaller(DWORD dwInstallationFlag, HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData)
{
    DWORD dwInstanceIDSize = 0;
    DWORD dwWow64DirSize = 0;
    LPTSTR tcstrDeviceInstanceId = NULL;
    LPTSTR tcstrWow64Directory = NULL;
    LPTSTR tcstrRunDLL32Path = NULL;
    PROCESS_INFORMATION processInformation;
    STARTUPINFO startupInfo;

    // Get the device instance ID
    SetupDiGetDeviceInstanceId( DeviceInfoSet, DeviceInfoData, NULL, 0, &dwInstanceIDSize );
    if( 0 == dwInstanceIDSize )
    {
        // Unable to retrieve required size - return
        return;
    }

    tcstrDeviceInstanceId = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR) * dwInstanceIDSize);
    if( NULL == tcstrDeviceInstanceId
    ||  0 == SetupDiGetDeviceInstanceId( DeviceInfoSet, DeviceInfoData, tcstrDeviceInstanceId, dwInstanceIDSize, NULL ) )
    {
        // Unable to retrieve device instance ID - return
        LocalFree((HANDLE)tcstrDeviceInstanceId);
        return;
    }

    dwWow64DirSize = GetSystemWow64Directory( NULL, 0 );
    if( 0 == dwWow64DirSize )
    {
        // Unable to retrieve the Wow64Directory size - return
        LocalFree((HANDLE)tcstrDeviceInstanceId);
        return;
    }

    tcstrWow64Directory = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR) * dwWow64DirSize);
    if( NULL == tcstrWow64Directory
    ||  0 == GetSystemWow64Directory( tcstrWow64Directory, dwWow64DirSize ) )
    {
        // Unable to retrieve the Wow64Directory - return
        LocalFree((HANDLE)tcstrWow64Directory);
        LocalFree((HANDLE)tcstrDeviceInstanceId);
        return;
    }

    // The full path is the size of the format string, plus two times the length of the system path,
    // plus the length of the instance ID, plus 10 (maximum length of a DWORD)
    tcstrRunDLL32Path = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR) * (wcslen(gszRunDLL32Path) +
        dwWow64DirSize * 2 + dwInstanceIDSize + 10));
    if( NULL == tcstrRunDLL32Path )
    {
        // Unable to allocate RunDLL32 path - return
        LocalFree((HANDLE)tcstrWow64Directory);
        LocalFree((HANDLE)tcstrDeviceInstanceId);
        return;
    }

    // Create the command line
    wsprintf( tcstrRunDLL32Path, gszRunDLL32Path, tcstrWow64Directory, tcstrWow64Directory, tcstrDeviceInstanceId, dwInstallationFlag );

    // Initialize the CreateProcess structures
    ZeroMemory( &processInformation, sizeof(PROCESS_INFORMATION) );
    ZeroMemory( &startupInfo, sizeof(STARTUPINFO) );
    startupInfo.cb = sizeof(STARTUPINFO);

    if( CreateProcess( NULL, tcstrRunDLL32Path, NULL, NULL, FALSE,
	    CREATE_NO_WINDOW, NULL, NULL, &startupInfo, &processInformation ) )
    {
        // Wait for the process to end
	    WaitForSingleObject( processInformation.hProcess, 5000 );

	    // Close process and thread handles. 
	    CloseHandle( processInformation.hProcess );
	    CloseHandle( processInformation.hThread );
    }

    // Free up the strings we allocated
    LocalFree((HANDLE)tcstrRunDLL32Path);
    LocalFree((HANDLE)tcstrWow64Directory);
    LocalFree((HANDLE)tcstrDeviceInstanceId);
}
#endif //_WIN64
