/****************************************************************************
 *
 *   registry.c
 *
 *   Copyright (c) 1992-1994 Microsoft Corporation
 *
 *   This file contains functions to maintain registry entries for
 *   kernel drivers installed via the drivers control panel applet.
 *
 *   Note that the ONLY state maintained between calls here is whatever
 *   state the registry and its handles maintain.
 *
 *   The registry entries are structured as follows :
 *   (see also winreg.h, winnt.h)
 *
 *   HKEY_LOCAL_MACHINE
 *       SYSTEM
 *           CurrentControlSet
 *               Services
 *                   DriverNode      (eg sndblst)
 *                           Type         = SERVICE_KERNEL_DRIVER (eg)
 *                           Group        = "Base"
 *                           ErrorControl = SERVICE_ERROR_NORMAL
 *                           Start        = SERVICE_SYSTEM_START |
 *                                          SERVICE_DEMAND_START |
 *                                          SERVICE_DISABLED
 *                                          ...
 *                           Tag          = A unique number ???
 *
 *                        Parameters
 *                           Device0
 *                                   Interrupt  =
 *                                   Port       =
 *                                   DMAChannel =
 *
 *   The Driver node is set up by the services manager when we call
 *   CreateService but we have to insert the device data ourselves.
 *
 *
 *
 *   The registry entries are shared between :
 *
 *        The system loader (which uses the Services entry)
 *        The kernel driver (which reads from the Device entry)
 *        This component called from the drivers control panel applet
 *        The service control manager
 *        The Setup utility
 *
 *   Security access
 *   ---------------
 *
 *   The driver determines whether it can perform configuration and
 *   installation by whether it can get read and write access to the
 *   service control manager.  This is required to manipulate the kernel
 *   driver database.
 *
 *   Services controller
 *   -------------------
 *
 *   The services controller is used because this is the only way we
 *   are allowed to load and unload kernel drivers.  Only the services
 *   controller can call LoadDriver and UnloadDriver and not get 'access
 *   denied'.
 *
 *   Note also that we can't keep the services controller handle open
 *   at the same time as the registry handle because then we can't get
 *   write access (actually we only need KEY_CREATE_SUB_KEY access) to
 *   our device parameters subkey.
 *
 ***************************************************************************/

 #include <stdio.h>
 #include <windows.h>
 #include <mmsystem.h>
 #include <winsvc.h>
 #include <soundcfg.h>
 #include "registry.h"

/***************************************************************************
 *
 * Constants for accessing the registry
 *
 ***************************************************************************/

    /*
     *  Path to service node key
     */

     #define STR_SERVICES_NODE TEXT("SYSTEM\\CurrentControlSet\\Services\\")

    /*
     *  Node sub-key for device parameters
     */

     #define STR_DEVICE_DATA PARMS_SUBKEY

    /*
     *  Name of Base group where sound drivers normally go
     */

     #define STR_BASE_GROUP TEXT("Base")

    /*
     *  Name of driver group for synthesizers
     *     - we use our own name here to make sure
     *       we are loaded after things like the PAS driver.  (Base is a
     *       known group and drivers in it are always loaded before unknown
     *       groups like this one).
     */

     #define STR_SYNTH_GROUP TEXT("Synthesizer Drivers")

    /*
     *  Name of service
     */

     #define STR_DRIVER TEXT("\\Driver\\")

    /*
     *  Path to kernel drivers directory from system directory
     */

     #define STR_DRIVERS_DIR TEXT("\\SystemRoot\\System32\\drivers\\")

    /*
     *  Extension for drivers
     */

     #define STR_SYS_EXT TEXT(".SYS")

 BOOL DrvSaveParametersKey(PREG_ACCESS RegAccess)
 {
     TCHAR TempFilePath[MAX_PATH];
     HKEY  ParametersKey;


     if (GetTempPath(MAX_PATH, TempFilePath) > MAX_PATH) {
         return FALSE;
     }

     if (GetTempFileName(TempFilePath,
                         TEXT("DRV"),
                         0,
                         RegAccess->TempKeySaveFileName) == 0) {
         return FALSE;
     }

     ParametersKey = DrvOpenRegKey(RegAccess->DriverName, NULL);

     if (ParametersKey == NULL) {
         RegAccess->TempKeySaveFileName[0] = '\0';
         return FALSE;
     }

     if (ERROR_SUCCESS != RegSaveKey(ParametersKey,
                                     RegAccess->TempKeySaveFileName,
                                     NULL)) {
         RegCloseKey(ParametersKey);
         RegAccess->TempKeySaveFileName[0] = '\0';
         return FALSE;
     }

     RegCloseKey(ParametersKey);

     return TRUE;
 }

 BOOL DrvRestoreParametersKey(PREG_ACCESS RegAccess)
 {
     BOOL Rc;
     HKEY ParametersKey;

     ParametersKey = DrvOpenRegKey(RegAccess->DriverName, NULL);

     Rc = ParametersKey != NULL &&
          ERROR_SUCCESS == RegRestoreKey(ParametersKey,
                                         RegAccess->TempKeySaveFileName,
                                         0);

     RegCloseKey(ParametersKey);
     DeleteFile(RegAccess->TempKeySaveFileName);

     RegAccess->TempKeySaveFileName[0] = '\0';

     return Rc;
 }

 HKEY DrvOpenRegKey(LPCTSTR DriverName, LPCTSTR Path)
 {
     TCHAR RegistryPath[MAX_PATH];
     HKEY NodeHandle;

     //
     // Create the path to our node
     //

     lstrcpy(RegistryPath, STR_SERVICES_NODE);
     lstrcat(RegistryPath, DriverName);
     lstrcat(RegistryPath, TEXT("\\"));
     lstrcat(RegistryPath, PARMS_SUBKEY);
     if (Path != NULL && lstrlen(Path) != 0) {
         lstrcat(RegistryPath, TEXT("\\"));
         lstrcat(RegistryPath, Path);
     }

     //
     // See if we can get a registry handle to our device data
     //


     if (RegCreateKey(HKEY_LOCAL_MACHINE, RegistryPath, &NodeHandle)
         != ERROR_SUCCESS) {
         return NULL;
     } else {
         return NodeHandle;
     }
 }

 HKEY DrvCreateDeviceKey(LPCTSTR DriverName) {
     UINT i;
     HKEY hKey;

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

         hKey = DrvOpenDeviceKey(DriverName, i);

         if (hKey == NULL) {
             TCHAR DeviceKeyName[MAX_PATH];

             wsprintf(DeviceKeyName, TEXT("Device%d"), i);

             return DrvOpenRegKey(DriverName, DeviceKeyName);
         } else {
             RegCloseKey(hKey);
         }
     }
 }

 HKEY DrvOpenDeviceKey(LPCTSTR DriverName, UINT n)
 {
     TCHAR DeviceKeyName[MAX_PATH];
     HKEY  hKeyParameters;
     HKEY  hKeyReturn;
     DWORD SubKeySize;

     SubKeySize = MAX_PATH;

     hKeyParameters = DrvOpenRegKey(DriverName, NULL);

     if (hKeyParameters == NULL) {
         return NULL;
     }

     hKeyReturn = NULL;

     if (ERROR_SUCCESS == RegEnumKeyEx(hKeyParameters,
                                       n,
                                       DeviceKeyName,
                                       &SubKeySize,
                                       NULL,
                                       NULL,
                                       NULL,
                                       NULL)) {
         RegOpenKey(hKeyParameters, DeviceKeyName, &hKeyReturn);
     }

     RegCloseKey(hKeyParameters);

     return hKeyReturn;
 }

 SC_HANDLE DrvOpenService(PREG_ACCESS RegAccess)
 {
     SC_HANDLE Handle;
     Handle = OpenService(RegAccess->ServiceManagerHandle,
                          RegAccess->DriverName,
                          SERVICE_ALL_ACCESS);

#if 0
     if (Handle == NULL) {
         char buf[100];
         sprintf(buf, "OpenService failed code %d\n", GetLastError());
         OutputDebugStringA(buf);
     }
#endif

     return Handle;
 }

 void DrvCloseService(PREG_ACCESS RegAccess, SC_HANDLE ServiceHandle)
 {
     CloseServiceHandle(ServiceHandle);
 }

/***************************************************************************
 *
 *  Function :
 *      DrvCreateServicesNode
 *
 *  Parameters :
 *      DriverNodeName      The name of the service node.  Same as the
 *                          name of the driver which must be
 *                          DriverNodeName.sys for the system to find it.
 *
 *      DriverType          Type of driver - see registry.h
 *
 *      ServiceNodeKey      Pointer to where to put returned handle
 *
 *  Return code :
 *
 *      Standard error code (see winerror.h)
 *
 *  Description :
 *
 *      Create the service node key
 *
 *      The class name of the registry node is ""
 *
 ***************************************************************************/

 BOOL
 DrvCreateServicesNode(LPTSTR DriverName,
                       SOUND_KERNEL_MODE_DRIVER_TYPE DriverType,
                       PREG_ACCESS RegAccess,
                       BOOL Create)
 {
     SERVICE_STATUS ServiceStatus;
     SC_HANDLE ServiceHandle;                 // Handle to our driver 'service'

     RegAccess->DriverName = DriverName;

     //
     // See if we can open the registry
     //

     if (RegAccess->ServiceManagerHandle == NULL) {
         RegAccess->ServiceManagerHandle =
             OpenSCManager(
                 NULL,                        // This machine
                 NULL,                        // The active database
                 SC_MANAGER_ALL_ACCESS);      // We want to create and change

         if (RegAccess->ServiceManagerHandle == NULL) {
             return FALSE;
         }
     }

     //
     // Open our particular service
     //

     ServiceHandle = DrvOpenService(RegAccess);

     //
     // See if that worked
     //

     if (ServiceHandle == NULL &&
         GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
         if (Create) {
             SC_LOCK ServicesDatabaseLock;
             TCHAR ServiceName[MAX_PATH];
             TCHAR BinaryPath[MAX_PATH];

             lstrcpy(BinaryPath, STR_DRIVERS_DIR);
             lstrcat(BinaryPath, DriverName);
             lstrcat(BinaryPath, STR_SYS_EXT);

             lstrcpy(ServiceName, STR_DRIVER);
             lstrcat(ServiceName, DriverName);

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


             for (ServicesDatabaseLock = NULL;
                  (ServicesDatabaseLock =
                       LockServiceDatabase(RegAccess->ServiceManagerHandle))
                     == NULL;
                  Sleep(100)) {
             }


            /*
             *  Create the service
             */


             ServiceHandle =
                 CreateService(
                     RegAccess->ServiceManagerHandle,
                     DriverName,               // Service name
                     NULL,                     // ???
                     SERVICE_ALL_ACCESS,       // Full access
                     SERVICE_KERNEL_DRIVER,    // Kernel driver
                     SERVICE_DEMAND_START,     // Start at sys start
                     SERVICE_ERROR_NORMAL,     // Not a disaster if fails
                     BinaryPath,               // Default path

                     DriverType == SoundDriverTypeSynth ?
                         STR_SYNTH_GROUP :     // Driver group
                         STR_BASE_GROUP,
                     NULL,                     // do not want TAG information
                     TEXT("\0"),               // No dependencies
                     NULL, // ServiceName,              // Driver object - optional
                     NULL);                    // No password

             UnlockServiceDatabase(ServicesDatabaseLock);
#if DBG
             if (ServiceHandle == NULL) {
                 TCHAR buf[100];
                 wsprintf(buf, TEXT("CreateService failed code %d\n"), GetLastError());
                 OutputDebugString(buf);
             }
#endif

         }
     }

     //
     // Check at least that it's a device driver
     //

     if (ServiceHandle != NULL) {
         if (!QueryServiceStatus(
                 ServiceHandle,
                 &ServiceStatus) ||
             ServiceStatus.dwServiceType != SERVICE_KERNEL_DRIVER) {

             //
             // Doesn't look like ours
             //

             CloseServiceHandle(RegAccess->ServiceManagerHandle);
             RegAccess->ServiceManagerHandle = NULL;
             DrvCloseService(RegAccess, ServiceHandle);
             return FALSE;
         }

     }

     if (ServiceHandle == NULL) {
         //
         // Leave the SC manager  handle open.  We use the presence of this
         // handle to test whether the driver can be configured (ie whether we
         // have the access rights to get into the SC manager).
         //

         return FALSE;
     } else {
         //
         // We can't keep this handle open (even though we'd like to) because
         // we need write access to the registry node created so that
         // we can add device parameters
         //

         DrvCloseService(RegAccess, ServiceHandle);
         return TRUE;
     }
 }


/***************************************************************************
 *
 *  Function :
 *      DrvCloseServicesNode
 *
 *  Parameters :
 *      ServiceNodeKey
 *
 *  Return code :
 *
 *      Standard error code (see winerror.h)
 *
 *  Description :
 *
 *      Close our handle
 *
 ***************************************************************************/

 VOID
 DrvCloseServiceManager(
     PREG_ACCESS RegAccess)
 {
     if (RegAccess->ServiceManagerHandle != NULL) {
         CloseServiceHandle(RegAccess->ServiceManagerHandle);
         RegAccess->ServiceManagerHandle = NULL;
     }

     if (RegAccess->TempKeySaveFileName[0] != '\0') {
         DeleteFile(RegAccess->TempKeySaveFileName);
     }
 }

/***************************************************************************
 *
 *  Function :
 *      DrvDeleteServicesNode
 *
 *  Parameters :
 *      DeviceName
 *
 *  Return code :
 *
 *      TRUE = success, FALSE = failed
 *
 *  Description :
 *
 *      Delete our node using the handle proviced
 *
 ***************************************************************************/

 BOOL
 DrvDeleteServicesNode(
     PREG_ACCESS RegAccess)
 {
     BOOL Success;
     SC_LOCK ServicesDatabaseLock;
     SC_HANDLE ServiceHandle;

     /*
     **  Delete the service node and free tha handle
     **  (Note the service cannot be deleted until all handles are closed)
     */

     ServiceHandle = DrvOpenService(RegAccess);

     if (ServiceHandle == NULL) {
         LONG Error;
         Error = GetLastError();
         if (Error == ERROR_SERVICE_DOES_NOT_EXIST) {
             /*
             **  It's already gone !
             */
             return TRUE;
         } else {
             return FALSE; // It was there but something went wrong
         }
     }

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


     for (ServicesDatabaseLock = NULL;
          (ServicesDatabaseLock =
               LockServiceDatabase(RegAccess->ServiceManagerHandle))
             == NULL;
          Sleep(100)) {
     }

     Success = DeleteService(ServiceHandle);

     UnlockServiceDatabase(ServicesDatabaseLock);

     DrvCloseService(RegAccess, ServiceHandle);

     return Success;
 }


/***************************************************************************
 *
 *  Function :
 *      DrvNumberOfDevices
 *
 *  Parameters :
 *      ServiceNodeKey       Handle to the device services node key
 *      NumberOfDevices      DWORD value to set to number of subkeys
 *
 *  Return code :
 *
 *      Standard error code (see winerror.h)
 *
 *  Description :
 *
        Find out how many device keys we have
 *
 ***************************************************************************/
 LONG
 DrvNumberOfDevices(
     PREG_ACCESS RegAccess,
     LPDWORD NumberOfDevices)
{
     HKEY ParmsKey;
     LONG ReturnCode;
     DWORD Junk;
     DWORD cchClassName;
     TCHAR ClassName[100];
     DWORD cbJunk = 0;
     FILETIME FileTime;

     *NumberOfDevices = 0;
     ParmsKey = DrvOpenRegKey(RegAccess->DriverName, NULL);

     if (ParmsKey == NULL) {
         return ERROR_FILE_NOT_FOUND;
     }

     cchClassName = 100;
     ReturnCode =  RegQueryInfoKey(
                       ParmsKey,
                       ClassName,
                       &cchClassName,
                       NULL,
                       NumberOfDevices,
                       &Junk,
                       &Junk,
                       &Junk,
                       &Junk,
                       &Junk,
                       &Junk,
                       &FileTime);

     RegCloseKey(ParmsKey);

     return ReturnCode;

}


/***************************************************************************
 *
 *  Function :
 *      DrvSetDeviceParameter
 *
 *  Parameters :
 *      ServiceNodeKey       Handle to the device services node key
 *      ValueName            Name of value to set
 *      Value                DWORD value to set
 *
 *  Return code :
 *
 *      Standard error code (see winerror.h)
 *
 *  Description :
 *
 *      Add the value to the device parameters section under the
 *      services node.
 *      This section is created if it does not already exist.
 *
 ***************************************************************************/

 LONG
 DrvSetDeviceIdParameter(
     PREG_ACCESS RegAccess,
     UINT   DeviceNumber,
     LPTSTR ValueName,
     DWORD Value)
 {
     HKEY ParmsKey;
     LONG ReturnCode;

     //
     //  ALWAYS create a key 0 - that way old drivers work
     //
     if (DeviceNumber == 0) {
         ParmsKey = DrvOpenRegKey(RegAccess->DriverName, TEXT("Device0"));
     } else {
         ParmsKey = DrvOpenDeviceKey(RegAccess->DriverName, DeviceNumber);
     }

     if (ParmsKey == NULL) {
         return ERROR_FILE_NOT_FOUND;
     }

     //
     // Write the value
     //


     ReturnCode = RegSetValueEx(ParmsKey,             // Registry handle
                                ValueName,            // Name of item
                                0,                    // Reserved 0
                                REG_DWORD,            // Data type
                                (LPBYTE)&Value,       // The value
                                sizeof(Value));       // Data length

     //
     // Free the handles we created
     //

     RegCloseKey(ParmsKey);

     return ReturnCode;
 }


/***************************************************************************
 *
 *  Function :
 *      DrvQueryDeviceIdParameter
 *
 *  Parameters :
 *      ServiceNodeKey       Handle to the device services node key
 *      ValueName            Name of value to query
 *      pValue               Returned value
 *
 *  Return code :
 *
 *      Standard error code (see winerror.h)
 *
 *  Description :
 *
 *      Add the value to the device parameters section under the
 *      services node.
 *      This section is created if it does not already exist.
 *
 ***************************************************************************/

 LONG
 DrvQueryDeviceIdParameter(
     PREG_ACCESS RegAccess,
     UINT  DeviceNumber,
     LPTSTR ValueName,
     PDWORD pValue)
 {
     HKEY ParmsKey;
     LONG ReturnCode;
     DWORD Index;
     DWORD Type;
     DWORD Value;
     DWORD ValueLength;

     ParmsKey = DrvOpenDeviceKey(RegAccess->DriverName, DeviceNumber);

     if (ParmsKey == NULL) {
         return ERROR_FILE_NOT_FOUND;
     }

     ValueLength = sizeof(Value);

     ReturnCode = RegQueryValueEx(ParmsKey,
                                  ValueName,
                                  NULL,
                                  &Type,
                                  (LPBYTE)&Value,
                                  &ValueLength);

     RegCloseKey(ParmsKey);

     if (ReturnCode == ERROR_SUCCESS) {

         if (Type == REG_DWORD) {
             *pValue = Value;
         } else {
             ReturnCode = ERROR_FILE_NOT_FOUND;
         }
     }

     return ReturnCode;
 }


/***************************************************************************
 *
 *  Function :
 *      DrvLoadKernelDriver
 *
 *  Parameters :
 *      Drivername         Name of driver to load
 *
 *  Return code :
 *
 *      TRUE if successful, otherwise FALSE
 *
 *  Description :
 *
 *      Call StartService to load the driver.  This assumes the services
 *      name is the driver name
 *
 ***************************************************************************/

 BOOL
 DrvLoadKernelDriver(
     PREG_ACCESS RegAccess)
 {
     SC_HANDLE ServiceHandle;
     BOOL Success;
     ServiceHandle = DrvOpenService(RegAccess);

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

    /*
     *  StartService causes the system to try to load the kernel driver
     */

     Success = StartService(ServiceHandle, 0, NULL);

    /*
     *  If this was successful we can change the start type to system
     *  start
     */

     if (Success) {
         Success = ChangeServiceConfig(ServiceHandle,
                                       SERVICE_NO_CHANGE,
                                       SERVICE_SYSTEM_START,
                                       SERVICE_NO_CHANGE,
                                       NULL,
                                       NULL,
                                       NULL,
                                       NULL,
                                       NULL,
                                       NULL,
                                       NULL);
     }
     DrvCloseService(RegAccess, ServiceHandle);
     return Success;
 }

/***************************************************************************
 *
 *  Function :
 *      DrvUnLoadKernelDriver
 *
 *  Parameters :
 *      RegAccess          Access variables to registry and Service control
 *                         manager
 *
 *  Return code :
 *
 *      TRUE if successful, otherwise FALSE
 *
 *  Description :
 *
 *      Call ControlService to unload the driver.  This assumes the services
 *      name is the driver name
 *
 ***************************************************************************/

 BOOL
 DrvUnloadKernelDriver(
     PREG_ACCESS RegAccess)
 {
     SERVICE_STATUS ServiceStatus;
     SC_HANDLE ServiceHandle;
     BOOL Success;


     ServiceHandle = DrvOpenService(RegAccess);
     if (ServiceHandle == NULL) {
         return GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST;
     }

    /*
     *  Set it not to load at system start until we've reconfigured
     */

     Success = ChangeServiceConfig(ServiceHandle,
                                   SERVICE_NO_CHANGE,
                                   SERVICE_DEMAND_START,
                                   SERVICE_NO_CHANGE,
                                   NULL,
                                   NULL,
                                   NULL,
                                   NULL,
                                   NULL,
                                   NULL,
                                   NULL);

     if (Success) {
        /*
         *  Don't try to unload if it's not loaded
         */

         if (DrvIsDriverLoaded(RegAccess)) {

            /*
             *  Note that the driver object name will not be found if
             *  the driver is not loaded.  However, the services manager may
             *  get in first and decide that the driver file does not exist.
             */

             Success = ControlService(ServiceHandle,
                                      SERVICE_CONTROL_STOP,
                                      &ServiceStatus);

         }

     }

     DrvCloseService(RegAccess, ServiceHandle);
     return Success;
 }

/***************************************************************************
 *
 *  Function :
 *
 *      DrvIsDriverLoaded
 *
 *  Parameters :
 *
 *      RegAccess          Access variables to registry and Service control
 *                         manager
 *
 *  Return code :
 *
 *      TRUE if successful, otherwise FALSE
 *
 *  Description :
 *
 *      See if a service by our name is started.
 *      Note - this assumes that we think our service is installed
 *
 ***************************************************************************/
 BOOL
 DrvIsDriverLoaded(
     PREG_ACCESS RegAccess)
 {
     SERVICE_STATUS ServiceStatus;
     SC_HANDLE ServiceHandle;
     BOOL Success;
     ServiceHandle = DrvOpenService(RegAccess);
     if (ServiceHandle == NULL) {
         return FALSE;
     }


     if (!QueryServiceStatus(ServiceHandle, &ServiceStatus)) {
         DrvCloseService(RegAccess, ServiceHandle);
         return FALSE;
     }

     Success = ServiceStatus.dwServiceType == SERVICE_KERNEL_DRIVER &&
               (ServiceStatus.dwCurrentState == SERVICE_RUNNING ||
                ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING);

     DrvCloseService(RegAccess, ServiceHandle);
     return Success;
 }


/***************************************************************************
 *
 *  Function :
 *
 *      DrvConfigureDriver
 *
 *  Parameters :
 *
 *      RegAccess          Access variables to registry and Service control
 *                         manager
 *
 *      DriverName         Name of the driver
 *
 *      DriverType         Type of driver (see registry.h)
 *
 *      SetParms           Callback to set the registry parameters
 *
 *      Context            Context value for callback
 *
 *  Return code :
 *
 *      TRUE if successful, otherwise FALSE
 *
 *  Description :
 *
 *      Performs the necessary operations to (re) configure a driver :
 *
 *      1.  If the driver is already installed :
 *
 *              Unload it if necessary
 *
 *              Set its start type to Demand until we know we're safe
 *              (This is so the system won't load a bad config if we crash)
 *
 *      2.  If the driver is not installed create its service entry in the
 *          registry.
 *
 *      3.  Run the callback to set up the driver's parameters
 *
 *      4.  Load the driver
 *
 *      5.  If the load returns success set the start type to System start
 *
 ***************************************************************************/

 BOOL DrvConfigureDriver(
          PREG_ACCESS RegAccess,
          LPTSTR      DriverName,
          SOUND_KERNEL_MODE_DRIVER_TYPE
                      DriverType,
          BOOL (*     SetParms    )(PVOID),
          PVOID       Context)
 {
     return

    /*
     *  If there isn't a services node create one - this is done first
     *  because this is how the driver name gets into the REG_ACCESS
     *  structure
     */

     DrvCreateServicesNode(
         DriverName,
         DriverType,
         RegAccess,
         TRUE)

     &&

    /*
     *  Unload driver if it's loaded
     */

     DrvUnloadKernelDriver(RegAccess)

     &&

    /*
     *  Run the callback
     */

     (SetParms == NULL || (*SetParms)(Context))

     &&

    /*
     *  Try reloading the driver
     */

     DrvLoadKernelDriver(RegAccess)

     ;

 }


/***************************************************************************
 *
 *  Function :
 *
 *      DrvRemoveDriver
 *
 *  Parameters :
 *
 *      RegAccess          Access variables to registry and Service control
 *                         manager
 *
 *  Return code :
 *
 *      DRVCNF_CANCEL  - Error occurred
 *
 *      DRVCNF_OK      - Registry entry delete but driver wasn't loaded
 *
 *      DRVCNF_RESTART - Driver unloaded and registry entry deleted
 *
 *  Description :
 *
 *      Unload the driver and remove its service control entry
 *
 ***************************************************************************/

 LRESULT DrvRemoveDriver(
             PREG_ACCESS RegAccess)
 {
     BOOL Loaded;

     Loaded = DrvIsDriverLoaded(RegAccess);

     if (Loaded) {
         DrvUnloadKernelDriver(RegAccess);
     }
     if (DrvDeleteServicesNode(RegAccess)) {
         return Loaded ? DRVCNF_RESTART : DRVCNF_OK;
     } else {
         return DRVCNF_CANCEL;
     }
 }

/***************************************************************************
 *
 *  Function :
 *
 *      DrvSetMapperName
 *
 *  Parameters :
 *
 *      Mapping Name       Name of mapping from midimap.cfg to use
 *
 *  Return code :
 *
 *      None - may or may not work
 *
 *  Description :
 *
 *      Tell the midi mapper which map to use
 *
 ***************************************************************************/

 VOID DrvSetMapperName(LPTSTR SetupName)
 {
     HKEY hKey;

     if (ERROR_SUCCESS ==
         RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                      TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Midimap"),
                      0L,
                      KEY_WRITE,
                      &hKey)) {

         RegSetValueEx( hKey,
                        TEXT("Mapping Name"),
                        0L,
                        REG_SZ,
                        (LPBYTE)SetupName,
                        sizeof(TCHAR) * (1 + lstrlen(SetupName)));

         RegCloseKey(hKey);
     }

     return;
 }