NT4/private/windows/media/vidcap/vcuser/registry.c
2020-09-30 17:12:29 +02:00

991 lines
25 KiB
C

/****************************************************************************
*
* 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 <stdio.h>
#ifdef UNICODE
#include <wchar.h>
#else
#include <string.h>
#define wcscat strcat
#define wcscpy strcpy
#define wcsicmp stricmp
#endif
#include <windows.h>
#include <mmsystem.h>
#include <winsvc.h>
// #include <soundcfg.h>
// include the video interface file to get the PARMS_SUBKEY defn
#include <ntddvidc.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")
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
* pValueType Place to return the type(REG_SZ...) of value (optional)
* pValue Returned value
* ValueLength Size of buffer for returned value (bytes)
*
*
* 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 pValueType,
PVOID pValue,
DWORD ValueLength
)
{
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
//
ReturnCode = RegOpenKey(ServiceNodeKey, STR_DEVICE_DATA, &ParmsKey);
RegCloseKey(ServiceNodeKey);
if (ReturnCode != ERROR_SUCCESS) {
return ReturnCode;
}
ReturnCode = RegQueryValueEx(
ParmsKey,
ValueName,
NULL,
pValueType,
pValue,
&ValueLength);
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)) {
// for the video driver, we do not need to restart even if loaded.
// return Loaded ? DRVCNF_RESTART : DRVCNF_OK;
return DRVCNF_OK;
} else {
return DRVCNF_CANCEL;
}
}