/**************************************************************************** * * registry.c * * Copyright (c) 1992 Microsoft Corporation. All Rights Reserved. * * 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 ??? * * Device * 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 #ifdef UNICODE #include #else #include #define wcscat strcat #define wcscpy strcpy #define wcsicmp _stricmp #endif #include #include #include #include #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") HKEY DrvOpenRegKey(LPTSTR DriverName) { TCHAR RegistryPath[MAX_PATH]; HKEY NodeHandle; // // Create the path to our node // wcscpy(RegistryPath, STR_SERVICES_NODE); wcscat(RegistryPath, DriverName); // // See if we can get a registry handle to our device data // if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegistryPath, 0L, KEY_ALL_ACCESS, &NodeHandle) != ERROR_SUCCESS) { return NULL; } else { return NodeHandle; } } 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]; wcscpy(BinaryPath, STR_DRIVERS_DIR); wcscat(BinaryPath, DriverName); wcscat(BinaryPath, STR_SYS_EXT); wcscpy(ServiceName, STR_DRIVER); wcscat(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; } } /*************************************************************************** * * 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; HKEY NodeHandle; NodeHandle = DrvOpenRegKey(RegAccess->DriverName); // // Make sure we're not accessing the registry ourselves and // that there aren't any subkeys // if (NodeHandle) { RegDeleteKey(NodeHandle, STR_DEVICE_DATA); RegFlushKey(NodeHandle); RegCloseKey(NodeHandle); } // // 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 : * 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 DrvSetDeviceParameter( PREG_ACCESS RegAccess, LPTSTR ValueName, DWORD Value) { HKEY ServiceNodeKey; HKEY ParmsKey; LONG ReturnCode; ServiceNodeKey = DrvOpenRegKey(RegAccess->DriverName); if (ServiceNodeKey == NULL) { return ERROR_FILE_NOT_FOUND; } // // First try to get a handle to the parameters subkey - which may // involve creating the subkey // ReturnCode = RegCreateKey(ServiceNodeKey, STR_DEVICE_DATA, &ParmsKey); RegCloseKey(ServiceNodeKey); if (ReturnCode != ERROR_SUCCESS) { return ReturnCode; } // // 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 : * DrvCreateParamsKey * * Parameters : * RegAccess Registry access info * * Return code : * * Standard error code (see winerror.h) * * Description : * * Create the device parameters section under the * services node if it does not already exist. No values are * written to the key. * ***************************************************************************/ LONG DrvCreateParamsKey( PREG_ACCESS RegAccess) { HKEY ServiceNodeKey; HKEY ParmsKey; LONG ReturnCode; ServiceNodeKey = DrvOpenRegKey(RegAccess->DriverName); if (ServiceNodeKey == NULL) { return ERROR_FILE_NOT_FOUND; } // // Try to get a handle to the parameters subkey - which may // involve creating the subkey // ReturnCode = RegCreateKey(ServiceNodeKey, STR_DEVICE_DATA, &ParmsKey); RegCloseKey(ServiceNodeKey); if (ReturnCode == ERROR_SUCCESS) { RegCloseKey(ParmsKey); } return ReturnCode; } /*************************************************************************** * * Function : * DrvQueryDeviceParameter * * 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 DrvQueryDeviceParameter( PREG_ACCESS RegAccess, LPTSTR ValueName, PDWORD pValue) { HKEY ServiceNodeKey; HKEY ParmsKey; LONG ReturnCode; DWORD Index; ServiceNodeKey = DrvOpenRegKey(RegAccess->DriverName); if (ServiceNodeKey == NULL) { return ERROR_FILE_NOT_FOUND; } // // First try to get a handle to the parameters subkey // ReturnCode = RegOpenKey(ServiceNodeKey, STR_DEVICE_DATA, &ParmsKey); RegCloseKey(ServiceNodeKey); if (ReturnCode != ERROR_SUCCESS) { return ReturnCode; } // // That was OK // Now we have to laboriously search the key values to // see if one corresponds to the one we want // for (Index = 0; ; Index++) { // Enumerate key values TCHAR ThisValueName[MAX_PATH]; DWORD NameLength; DWORD Type; DWORD Value; DWORD ValueLength; NameLength = MAX_PATH; NameLength = sizeof(ThisValueName); ValueLength = sizeof(Value); ReturnCode = RegEnumValue(ParmsKey, // Handle to key Index, // Index of value ThisValueName, // Where to put the name &NameLength, // Length of the name NULL, // Reserved NULL &Type, // returns REG_... type (LPBYTE)&Value, // Where to put the value &ValueLength); // Max length of data if (ReturnCode == ERROR_SUCCESS) { // // We've found something - does it's name match the // name we're looking for ? // if (0 == _wcsicmp(ValueName, ThisValueName)) { RegCloseKey(ParmsKey); // // If it's the wrong type there's something // wrong !! // if (Type == REG_DWORD) { // // Return Value to caller // *pValue = Value; return ERROR_SUCCESS; } else { return ERROR_FILE_NOT_FOUND; } } } else { // // We didn't find the value with the name we were // looking for so return the default // RegCloseKey(ParmsKey); 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; 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)) && DrvDeleteServicesNode(RegAccess)) { return Loaded ? DRVCNF_RESTART : DRVCNF_OK; } else { return DRVCNF_CANCEL; } }