windows-nt/Source/XPSP1/NT/multimedia/media/drivers/drvlib/registry.c

1124 lines
29 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/****************************************************************************
*
* 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;
}