Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4469 lines
111 KiB

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
rdevnode.c
Abstract:
This module contains the server-side hardware tree traversal APIs.
PNP_CreateDevInst
PNP_DeviceInstanceAction
PNP_GetDeviceStatus
Author:
Paula Tomlinson (paulat) 7-11-1995
Environment:
User-mode only.
Revision History:
11-July-1995 paulat
Creation and initial implementation.
--*/
//
// includes
//
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntpnpapi.h>
#include "precomp.h"
#include "umpnpdat.h"
//
// private prototypes
//
CONFIGRET
CreateDevInst(
IN PCWSTR pszDeviceID,
IN PCWSTR pszParentID,
IN ULONG ulFlags
);
CONFIGRET
MoveDevInst(
IN PCWSTR pszSourceID,
IN PCWSTR pszDestinationID
);
CONFIGRET
SetupDevInst(
IN PCWSTR pszDeviceID,
IN ULONG ulFlags
);
CONFIGRET
EnableDevInst(
IN PCWSTR pszDeviceID
);
CONFIGRET
DisableDevInst(
IN PCWSTR pszDeviceID
);
CONFIGRET
ReenumerateDevInst(
IN PCWSTR pszDeviceID
);
CONFIGRET
QueryRemoveSubTree(
IN PCWSTR pszDeviceID,
ULONG ulFlags
);
CONFIGRET
RemoveSubTree(
IN PCWSTR pszDeviceID,
ULONG ulFlags
);
CONFIGRET
CreateDefaultDeviceInstance(
IN PCWSTR pszDeviceID,
IN PCWSTR pszParentID,
IN BOOL bPhantom
);
BOOL
IsDevicePresent(
IN OUT HKEY hKey
);
BOOL
IsDeviceInstalled(
IN OUT HKEY hKey
);
CONFIGRET
CleanupMovedDevNode(
IN PCWSTR pszMovedID,
IN HKEY hMovedKey,
IN PCWSTR pszNewID,
IN HKEY hNewKey
);
ULONG
GetDeviceStatusFlag(
IN HKEY hKey
);
ULONG
GetDeviceConfigFlag(
IN HKEY hKey
);
ULONG
GetCurrentConfigFlag(
IN PCWSTR pDeviceID
);
BOOL
MarkDevicePresent(
IN HKEY hKey,
IN ULONG ulValue
);
BOOL
MarkDevicePhantom(
IN HKEY hKey,
IN ULONG ulValue
);
CONFIGRET
GenerateDeviceInstance(
LPWSTR pszFullDeviceID,
LPWSTR pszDeviceID
);
CONFIGRET
BuildServiceList(
IN PCWSTR pDeviceID,
OUT PWSTR *pDeviceList,
OUT PWSTR *pServiceList
);
CONFIGRET
StartDeviceList(
IN LPCWSTR pszDeviceList,
IN LPCWSTR pszService,
SC_HANDLE hSCManager
);
CONFIGRET
StartDevice(
IN LPCWSTR pszDevice,
IN LPCWSTR pszService
);
CONFIGRET
StopService(
IN LPCWSTR pszService
);
CONFIGRET
StopDevice(
IN LPCWSTR pszDeviceID
);
CONFIGRET
QueryStopService(
IN SC_HANDLE hService,
IN LPCWSTR pszService
);
CONFIGRET
QueryStopDevice(
IN LPCWSTR pszDeviceID
);
CONFIGRET
UninstallRealDevice(
IN LPCWSTR pszDeviceID
);
BOOL
IsDeviceRegistered(
IN LPCWSTR pszDeviceID,
IN LPCWSTR pszService
);
//
// global data
//
extern HKEY ghEnumKey; // Key to HKLM\CCC\System\Enum - DO NOT MODIFY
extern HKEY ghServicesKey; // Key to HKLM\CCC\System\Services - DO NOT MODIFY
CONFIGRET
PNP_CreateDevInst(
IN handle_t hBinding,
IN OUT LPWSTR pszDeviceID,
IN LPWSTR pszParentDeviceID,
IN ULONG ulLength,
IN ULONG ulFlags
)
/*++
Routine Description:
This is the RPC server entry point for the CM_Create_DevNode routine.
Arguments:
hBinding Not used.
pszDeviceID Device instance to create.
pszParentDeviceID Parent of the new device.
ulLength Max length of pDeviceID on input and output.
ulFlags This value depends on the value of the ulMajorAction and
further defines the specific action to perform
Return Value:
If the function succeeds, the return value is CR_SUCCESS. Otherwise it
returns a CR_ error code.
--*/
{
CONFIGRET Status = CR_SUCCESS;
ULONG RegStatus = ERROR_SUCCESS;
HKEY hKey = NULL;
WCHAR szFullDeviceID[MAX_DEVICE_ID_LEN];
ULONG ulStatusFlag=0, ulConfigFlag=0, ulCSConfigFlag=0, ulProblem=0;
ULONG ulSize=0;
//
// until full PNP implementation, there is no distinction between the
// normal and no wait type of installation, I do the same thing in
// either case - BUGBUG - SUR
//
try {
//
// RULE: validate that parent isn't a phantom; can't create a child
// from a phantom parent
//
if (!IsValidDeviceID(pszParentDeviceID, NULL,
PNP_PRESENT | PNP_NOT_PHANTOM)) {
Status = CR_NO_SUCH_DEVINST;
goto Clean0;
}
//
// create a unique instance value if requested
//
if (ulFlags & CM_CREATE_DEVNODE_GENERATE_ID) {
Status = GenerateDeviceInstance(szFullDeviceID, (LPTSTR)pszDeviceID);
if (Status != CR_SUCCESS) {
goto Clean0;
}
if ((ULONG)lstrlen(szFullDeviceID) + 1 > ulLength) {
Status = CR_BUFFER_SMALL;
goto Clean0;
}
lstrcpy(pszDeviceID, szFullDeviceID);
}
//
// try opening the registry key for this device instance
//
RegStatus = RegOpenKeyEx(ghEnumKey, pszDeviceID, 0,
KEY_READ | KEY_WRITE, &hKey);
//
// first handle phantom devnode case
//
if (ulFlags & CM_CREATE_DEVNODE_PHANTOM) {
//
// for a phantom devnode, it must not already exist in the registry
//
if (RegStatus == ERROR_SUCCESS) {
Status = CR_ALREADY_SUCH_DEVINST;
goto Clean0;
}
//
// it doesn't exist in the registry so create a phantom devnode
//
CreateDefaultDeviceInstance(
pszDeviceID, pszParentDeviceID, TRUE);
goto Clean0;
}
//
// for a normal devnode, fail if the device is already present in the
// registry and alive
//
if (RegStatus == ERROR_SUCCESS && IsDevicePresent(hKey)) {
ulSize = sizeof(DWORD);
if (RegQueryValueEx(
hKey, pszRegValueStatusFlags, NULL, NULL,
(LPBYTE)&ulStatusFlag, &ulSize) != ERROR_SUCCESS) {
ulStatusFlag = 0;
}
ulStatusFlag |= DN_NEED_TO_ENUM;
RegSetValueEx(
hKey, pszRegValueStatusFlags, 0, REG_DWORD,
(LPBYTE)&ulStatusFlag, sizeof(ulStatusFlag));
// BUGBUG: do I need to tell Device Manager to start it??
Status = CR_ALREADY_SUCH_DEVINST;
goto Clean0;
}
//
// if couldn't open the device instance, then most likely the
// key doesn't exist yet, so create a device instance key with
// default values
//
if (RegStatus != ERROR_SUCCESS) {
CreateDefaultDeviceInstance(
pszDeviceID, pszParentDeviceID, FALSE);
RegStatus = RegOpenKeyEx(ghEnumKey, pszDeviceID, 0,
KEY_READ | KEY_WRITE, &hKey);
if (RegStatus != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
}
//
// retrieve flags
//
ulStatusFlag = GetDeviceStatusFlag(hKey);
ulConfigFlag = GetDeviceConfigFlag(hKey);
ulCSConfigFlag = GetCurrentConfigFlag(pszDeviceID);
//
// check if the device is blocked
//
if (ulCSConfigFlag & CSCONFIGFLAG_DO_NOT_CREATE ||
ulConfigFlag & CONFIGFLAG_REMOVED ||
ulConfigFlag & CONFIGFLAG_NET_BOOT) {
Status = CR_CREATE_BLOCKED;
goto Clean0;
}
//
// mark the device instance as present
//
if(!MarkDevicePresent(hKey, TRUE)) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
//
// Clear the phantom value (in case we're turning a phantom into a
// real devinst.
//
RegDeleteValue(hKey, pszRegValuePhantom);
//
// if device not installed, set a problem
//
if (ulConfigFlag & CONFIGFLAG_REINSTALL ||
ulConfigFlag & CONFIGFLAG_FAILEDINSTALL) {
ulProblem = CM_PROB_NOT_CONFIGURED;
RegSetValueEx(
hKey, pszRegValueProblem, 0, REG_DWORD,
(LPBYTE)&ulProblem, sizeof(ulProblem));
ulStatusFlag |= DN_HAS_PROBLEM;
}
if (ulFlags & CM_CREATE_DEVINST_DO_NOT_INSTALL) {
//
// If the device has a service, register it
//
PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA ControlData;
WCHAR szService[MAX_PATH];
ulSize = MAX_PATH * sizeof(WCHAR);
if (RegQueryValueEx(hKey, pszRegValueService, NULL, NULL,
(LPBYTE)szService, &ulSize) == ERROR_SUCCESS) {
if (szService[0]) {
RtlInitUnicodeString(&ControlData.DeviceInstance, pszDeviceID);
NtPlugPlayControl(PlugPlayControlRegisterNewDevice,
&ControlData,
sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA),
&ulSize);
}
}
}
//
// mark the new device instance as needing to be enumerated
//
ulStatusFlag |= DN_NEED_TO_ENUM;
RegSetValueEx(hKey, pszRegValueStatusFlags, 0, REG_DWORD,
(LPBYTE)&ulStatusFlag, sizeof(ulStatusFlag));
Clean0:
; // do nothing
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
return Status;
} // PNP_CreateDevInst
CONFIGRET
PNP_DeviceInstanceAction(
IN handle_t hBinding,
IN ULONG ulAction,
IN ULONG ulFlags,
IN PCWSTR pszDeviceInstance1,
IN PCWSTR pszDeviceInstance2
)
/*++
Routine Description:
This is the RPC server entry point for the ConfigManager routines that
perform some operation on DevNodes (such as create, setup, move, disable,
and enable, etc). It handles various routines in this one routine by
accepting a major and minor action value.
Arguments:
hBinding Not used.
ulMajorAction Specifies the requested action to perform (one of the
PNP_DEVINST_* values)
ulFlags This value depends on the value of the ulMajorAction and
further defines the specific action to perform
pszDeviceInstance1 This is a device instance string to be used in
performing the specified action, it's value depends on
the ulMajorAction value.
pszDeviceInstance2 This is a device instance string to be used in
performing the specified action, it's value depends on
the ulMajorAction value.
Return Value:
If the function succeeds, the return value is CR_SUCCESS.
If the function fails, the return value is one of the following:
CR_ALREADY_SUCH_DEVNODE,
CR_INVALID_DEVICE_ID,
CR_INVALID_DEVNODE,
CR_INVALID_FLAG,
CR_FAILURE,
CR_NOT_DISABLEABLE,
CR_INVALID_POINTER, or
CR_OUT_OF_MEMORY.
--*/
{
CONFIGRET Status = CR_SUCCESS;
UNREFERENCED_PARAMETER(hBinding);
//
// pass the request on to a private routine that handles each major
// device instance action request
//
switch (ulAction) {
case PNP_DEVINST_MOVE:
Status = MoveDevInst(
pszDeviceInstance1, // destination
pszDeviceInstance2); // source
break;
case PNP_DEVINST_SETUP:
Status = SetupDevInst(
pszDeviceInstance1, // device instance to configure
ulFlags); // setup flags
break;
case PNP_DEVINST_ENABLE:
Status = EnableDevInst(
pszDeviceInstance1); // device instance to enable
break;
case PNP_DEVINST_DISABLE:
Status = DisableDevInst(
pszDeviceInstance1); // device instance to disable
break;
case PNP_DEVINST_REENUMERATE:
Status = ReenumerateDevInst(
pszDeviceInstance1); // device instance to reenumerate
break;
case PNP_DEVINST_QUERYREMOVE:
Status = QueryRemoveSubTree(
pszDeviceInstance1,
ulFlags);
break;
case PNP_DEVINST_REMOVESUBTREE:
Status = RemoveSubTree(
pszDeviceInstance1, // device instance tree to remove
ulFlags); // remove flags
break;
default:
Status = CR_INVALID_FLAG;
break;
}
if (!RtlValidateProcessHeaps()) {
Status = CR_FAILURE;
}
return Status;
} // PNP_DeviceInstanceAction
CONFIGRET
PNP_GetDeviceStatus(
IN handle_t hBinding,
IN LPCWSTR pDeviceID,
OUT PULONG pulStatus, OPTIONAL
OUT PULONG pulProblem, OPTIONAL
IN ULONG ulFlags
)
/*++
Routine Description:
This is the RPC server entry point for the ConfigManager
CM_Get_DevNode_Status routines. It retrieves device instance specific
status information.
Arguments:
hBinding Not used.
pDeviceID This is a device instance string to retrieve status
information for.
pulStatus Pointer to ULONG variable to return Status Flags in
pulProblem Pointer to ULONG variable to return Problem in
ulFlags Not used.
Return Value:
If the function succeeds, the return value is CR_SUCCESS.
If the function fails, the return value is one of the following:
CR_INVALID_DEVNODE,
CR_INVALID_FLAG, or
CR_INVALID_POINTER.
--*/
{
CONFIGRET Status = CR_SUCCESS;
ULONG RegStatus = ERROR_SUCCESS;
HKEY hKey = NULL;
WCHAR szService[MAX_PATH];
ULONG ulStaticStatus = 0, ulSize = 0;
SC_HANDLE hSCManager = NULL, hService = NULL;
SERVICE_STATUS ServiceStatus;
WCHAR szEnumerator[MAX_DEVICE_ID_LEN],
szDevice[MAX_DEVICE_ID_LEN],
szInstance[MAX_DEVICE_ID_LEN];
UNREFERENCED_PARAMETER(hBinding);
UNREFERENCED_PARAMETER(ulFlags);
try {
//
// FOR SUR: We can't rely on the status flags in the registry so I
// need to determine dynamically whether the device id
// is really started. For now, I "OR" the current status
// flags value in the registry with DN_STARTED if the
// controlling service is actually running.
//
//
// open a key to the specified device id
//
if (RegOpenKeyEx(ghEnumKey, pDeviceID, 0, KEY_READ,
&hKey) != ERROR_SUCCESS) {
Status = CR_NO_SUCH_DEVINST;
goto Clean0;
}
//
// Query the "static" status flags from the registry
//
ulSize = sizeof(ULONG);
if (RegQueryValueEx(
hKey, pszRegValueStatusFlags, NULL, NULL,
(LPBYTE)&ulStaticStatus, &ulSize) != ERROR_SUCCESS) {
//
// StatusFlag not set yet, assume zero
//
ulStaticStatus = 0;
}
//
// If this is a root enumerated device, then OR in the
// DN_ROOT_ENUMERATED flag.
//
SplitDeviceInstanceString(pDeviceID,
szEnumerator,
szDevice,
szInstance);
if (lstrcmpi(szEnumerator, pszRegKeyRootEnum) == 0) {
ulStaticStatus |= DN_ROOT_ENUMERATED;
}
//------------------------------------------------------
// Retrieve the problem value
//------------------------------------------------------
if (pulProblem != NULL) {
//
// The static status value is enough for determining
// whether there is a problem, don't get dynamic
// status unless calling really asks for it.
//
if (ulStaticStatus | DN_HAS_PROBLEM) {
//
// get the problem value from the registry
//
ulSize = sizeof(ULONG);
if (RegQueryValueEx(
hKey, pszRegValueProblem, NULL, NULL,
(LPBYTE)pulProblem, &ulSize) != ERROR_SUCCESS) {
//
// Problem not set yet, assume zero
//
*pulProblem = 0;
}
} else {
*pulProblem = 0;
}
}
//------------------------------------------------------
// Retrieve the Status value
//------------------------------------------------------
if (pulStatus != NULL) {
*pulStatus = ulStaticStatus;
//
// Find service that is "actually controlling this device
//
if (!GetActiveService(pDeviceID, szService)) {
CLEAR_FLAG(*pulStatus, DN_STARTED);
goto Clean0;
}
if (!IsDeviceRegistered(pDeviceID, szService)) {
//
// This device instance is not registered as being
// controlled by the service, so the device
// instance is by definition not started.
//
CLEAR_FLAG(*pulStatus, DN_STARTED);
goto Clean0;
}
//
// Need to find "dynamic" device status, so open the
// Service Control Manager
//
if ((hSCManager = OpenSCManager(NULL, NULL,
GENERIC_READ)) == NULL) {
goto Clean0; // will have to use static value alone
}
//
// Open a handle to the controlling service
//
if ((hService = OpenService(hSCManager, szService,
SERVICE_QUERY_STATUS)) == NULL) {
CLEAR_FLAG(*pulStatus, DN_STARTED);
goto Clean0;
}
if (!QueryServiceStatus(hService, &ServiceStatus) ||
ServiceStatus.dwCurrentState == SERVICE_STOPPED) {
CLEAR_FLAG(*pulStatus, DN_STARTED);
} else {
SET_FLAG(*pulStatus, DN_STARTED);
}
}
Clean0:
; // do nothing
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
if (hService != NULL) {
CloseServiceHandle(hService);
}
if (hSCManager != NULL) {
CloseServiceHandle(hSCManager);
}
return Status;
} // PNP_GetDeviceStatus
CONFIGRET
PNP_UninstallDevInst(
handle_t hBinding,
LPCWSTR pDeviceID,
ULONG ulFlags
)
/*++
Routine Description:
This is the RPC server entry point for the ConfigManager
CM_Deinstall_DevNode routine. It removes the device instance
registry key and any subkeys (only for phantoms).
Arguments:
hBinding Not used.
pDeviceID The device instance to deinstall.
ulFlags Not used.
Return Value:
If the function succeeds, the return value is CR_SUCCESS.
Otherwise it returns one of the CR_ERROR codes.
--*/
{
CONFIGRET Status = CR_SUCCESS;
HKEY hKey = NULL;
WCHAR RegStr[MAX_CM_PATH];
ULONG ulCount=0, ulProfile = 0, ulLength = 0;
WCHAR szEnumerator[MAX_DEVICE_ID_LEN],
szDevice[MAX_DEVICE_ID_LEN],
szInstance[MAX_DEVICE_ID_LEN],
szParent[MAX_DEVICE_ID_LEN];
NTSTATUS NtStatus = STATUS_SUCCESS;
PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA ControlData;
UNREFERENCED_PARAMETER(hBinding);
UNREFERENCED_PARAMETER(ulFlags);
try {
//------------------------------------------------------------------
// Uninstall deletes instance key (and all subkeys) for all
// the hardware keys (this means the main Enum branch, the
// config specific keys under HKLM, and the Enum branch under
// HKCU). In the case of the user hardware keys (under HKCU),
// I delete those whether it's a phantom or not, but since
// I can't access the user key from the service side, I have
// to do that part on the client side. For the main hw Enum key
// and the config specific hw keys, I only delete them outright
// if they are phantoms. If not a phantom, then I just make the
// device instance volatile (by saving the original key, deleting
// old key, creating new volatile key and restoring the old
// contents) so at least it will go away during the next boot
//------------------------------------------------------------------
//
// If the device is not a phantom, do the volatile-copy-thing
//
if (ulFlags != PNP_PRIVATE &&
!IsDevicePhantom((LPWSTR)pDeviceID)) {
Status = UninstallRealDevice(pDeviceID);
goto Clean0;
}
//-------------------------------------------------------------
// device is a phantom so actually delete it
//-------------------------------------------------------------
//
// 1. Deregister the original device id (only on phantoms)
//
RtlInitUnicodeString(&ControlData.DeviceInstance, pDeviceID);
NtStatus = NtPlugPlayControl(
PlugPlayControlDeregisterDevice,
&ControlData,
sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA),
&ulLength);
#if 0
if (NtStatus != STATUS_SUCCESS) {
Status = CR_FAILURE;
goto Clean0;
}
#endif
//
// 2. Remove the instance under the main enum branch. If this is the
// only instance, then the device will be removed as well. The parent
// key to DeletePrivateKey is the registry path up to the enumerator
// and the child key is the device and instance.
//
//
// before deleting any subkeys, save this device's parent
//
if (RegOpenKeyEx(ghEnumKey, pDeviceID, 0, KEY_QUERY_VALUE,
&hKey) != ERROR_SUCCESS) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
szParent[0] = '\0';
ulLength = MAX_DEVICE_ID_LEN * sizeof(WCHAR);
RegQueryValueEx(
hKey, pszRegValueBaseDevicePath, NULL, NULL,
(LPBYTE)szParent, &ulLength);
//
// Get the device id's component parts.
//
SplitDeviceInstanceString(
pDeviceID,
szEnumerator,
szDevice,
szInstance);
wsprintf(RegStr, TEXT("%s\\%s"),
pszRegPathEnum,
szEnumerator);
lstrcat(szDevice, TEXT("\\"));
lstrcat(szDevice, szInstance);
//
// delete the device instance key
//
Status = DeletePrivateKey(HKEY_LOCAL_MACHINE, RegStr, szDevice);
if (Status != CR_SUCCESS) {
goto Clean0;
}
//
// 3. Now check each hardware profile and delete any entries for this
// device instance.
//
Status = GetProfileCount(&ulCount);
if (Status != CR_SUCCESS) {
goto Clean0;
}
for (ulProfile = 1; ulProfile <= ulCount; ulProfile++) {
wsprintf(RegStr, TEXT("%s\\%04u\\%s\\%s"),
pszRegPathHwProfiles,
ulProfile,
pszRegPathEnum,
szEnumerator);
//
// Ignore the status for profile-specific keys since they may
// not exist. RemoveDeviceInstance() will remove the instance
// and the device if this is the only instance.
//
DeletePrivateKey(HKEY_LOCAL_MACHINE, RegStr, szDevice);
}
//
// 4. Delete this device instance from it's parents attached components
// list
//
if (szParent[0] != '\0') {
RemoveAttachedComponent(szParent, pDeviceID);
}
Clean0:
; // do nothing
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
return Status;
} // PNP_Uninstall_DevInst
CONFIGRET
PNP_AddID(
IN handle_t hBinding,
IN LPCWSTR pszDeviceID,
IN LPCWSTR pszID,
IN ULONG ulFlags
)
/*++
Routine Description:
This is the RPC server entry point for the ConfigManager
CM_Add_ID routine. It adds a hardware or compatible ID to
the registry for this device instance.
Arguments:
hBinding Not used.
pszDeviceID The device instance to add an ID for.
pszID The hardware or compatible ID to add.
ulFlags Not used.
Return Value:
If the function succeeds, the return value is CR_SUCCESS.
Otherwise it returns one of the CR_ERROR codes.
--*/
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
HKEY hKey = NULL;
WCHAR RegStr[MAX_CM_PATH], szCurrentID[1024];
ULONG ulCount=0, ulProfile = 0, ulLength = 0;
UNREFERENCED_PARAMETER(hBinding);
UNREFERENCED_PARAMETER(ulFlags);
try {
//
// Open a key to the device instance in the registry
//
if (RegOpenKeyEx(ghEnumKey, pszDeviceID, 0,
KEY_QUERY_VALUE | KEY_SET_VALUE,
&hKey) != ERROR_SUCCESS) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
//
// select the appropriate registry value name
//
if (ulFlags == CM_ADD_ID_HARDWARE) {
lstrcpy(RegStr, pszRegValueHardwareID);
} else {
lstrcpy(RegStr, pszRegValueCompatibleIDs);
}
//
// If any IDs have already been set, read those now
//
szCurrentID[0] = '\0';
ulLength = 1024 * sizeof(WCHAR);
RegStatus = RegQueryValueEx(
hKey, RegStr, NULL, NULL, (LPBYTE)szCurrentID, &ulLength);
if (RegStatus == CR_SUCCESS) {
//
// If this ID is already in the list, then there's nothing
// new to add
//
if (!MultiSzSearchStringW(szCurrentID, pszID)) {
//
// This ID is not already in the list, so append the new ID
// to the end of the existing IDs and write it back to the
// registry
//
ulLength = 1024 * sizeof(WCHAR);
MultiSzAppendW(szCurrentID, &ulLength, pszID);
RegStatus = RegSetValueEx(
hKey, RegStr, 0, REG_MULTI_SZ, (LPBYTE)szCurrentID, ulLength);
if (RegStatus != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
}
} else {
//
// write out the id with a double null terminator
//
lstrcpy(szCurrentID, pszID);
szCurrentID[lstrlen(pszID) + 1] = 0x0;
RegStatus = RegSetValueEx(
hKey, RegStr, 0, REG_MULTI_SZ, (LPBYTE)szCurrentID,
(lstrlen(szCurrentID) + 2) * sizeof(WCHAR));
if (RegStatus != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
}
Clean0:
; // do nothing
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
return Status;
} // PNP_AddID
//-------------------------------------------------------------------
// Private functions
//-------------------------------------------------------------------
CONFIGRET
MoveDevInst(
IN PCWSTR pszDestinationID,
IN PCWSTR pszSourceID
)
/*++
Routine Description:
Arguments:
Return value:
The return value is CR_SUCCESS if the function suceeds and one of the
CR_* values if it fails.
--*/
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
HKEY hKey = NULL, hSourceKey=NULL, hDestKey=NULL, hLinkKey = NULL;
WCHAR RegStr[MAX_PATH];
ULONG ulSize = 0;
LPWSTR pChild = NULL, pChildren = NULL;
WCHAR szEnumerator[MAX_DEVICE_ID_LEN],
szDevice[MAX_DEVICE_ID_LEN],
szInstance[MAX_DEVICE_ID_LEN];
//
// NOTE, originally I was marking the source devinst with
// a problem of moved, and setting the MovedTo value to
// the destination device id name. Instead, now I'm making
// the old source device id a symbolic link to the new
// destination device id. This allows automatic forwarding
// of requests to a device id that has been moved. It also
// means that to determine whether a device has been moved
// you need to compare the device id you are specifying
// agains a key of the same name under the instance key
// (of course, the key have been converted from a path to
// a string).
//
try {
//
// can't move the root devnode
//
if (IsRootDeviceID((LPWSTR)pszDestinationID)) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
//
// Open the source and destination device instance registry keys
// (guard access to the externally supplied buffers)
//
if (RegOpenKeyEx(ghEnumKey, pszSourceID, 0, KEY_READ | KEY_WRITE,
&hSourceKey) != ERROR_SUCCESS) {
Status = CR_NO_SUCH_DEVINST;
goto Clean0;
}
if (RegOpenKeyEx(ghEnumKey, pszDestinationID, 0, KEY_READ | KEY_WRITE,
&hDestKey) != ERROR_SUCCESS) {
Status = CR_NO_SUCH_DEVINST;
goto Clean0;
}
//
// 1. copy source to destination, including any subkeys
//
Status = CopyRegistryTree(hSourceKey, hDestKey, REG_OPTION_NON_VOLATILE);
if (Status != CR_SUCCESS) {
goto Clean0;
}
//
// 2. mark the new device instance with a reference to the old
// one it was moved from, in case we need this information
// later (it's the only way to tell if you've accessed a link
// rather than a "real" devinst)
//
if (!PathToString(RegStr, pszSourceID)) {
Status = CR_FAILURE;
goto Clean0;
}
RegStatus = RegCreateKeyEx(
hDestKey, RegStr, 0, NULL, REG_OPTION_VOLATILE,
KEY_ALL_ACCESS, NULL, &hKey, NULL);
if (RegStatus != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
RegCloseKey(hKey);
hKey == NULL;
//
// 3. Remove the original (source) device id from it's parent's
// attached components list
//
ulSize = MAX_PATH * sizeof(WCHAR);
RegStatus = RegQueryValueEx(
hSourceKey, pszRegValueBaseDevicePath, NULL,
NULL, (LPBYTE)RegStr, &ulSize);
if (RegStatus != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
if (*RegStr != TEXT('\0')) {
Status = RemoveAttachedComponent(RegStr, pszSourceID);
if (Status != ERROR_SUCCESS) {
goto Clean0;
}
}
#if 0
//
// 4. Add the new (destination) device id to the same
// parent's attached component list
//
Status = AddAttachedComponent(RegStr, pszDestinationID);
if (Status != ERROR_SUCCESS) {
goto Clean0;
}
#endif
//
// 5. Any children of the original device should be updated to
// point to this new (destination) device as their parent
//
RegStatus = RegQueryValueEx(
hDestKey, pszRegValueAttachedComponents, NULL,
NULL, NULL, &ulSize);
if (RegStatus == ERROR_SUCCESS) {
//
// this device does have attached components
//
pChildren = malloc(ulSize);
if (pChildren == NULL) {
Status = CR_OUT_OF_MEMORY;
goto Clean0;
}
RegStatus = RegQueryValueEx(
hDestKey, pszRegValueAttachedComponents, NULL,
NULL, (LPBYTE)pChildren, &ulSize);
if (RegStatus != ERROR_SUCCESS) {
Status = CR_NO_SUCH_DEVNODE;
goto Clean0;
}
//
// for each child, update the parent value
//
for (pChild = pChildren;
*pChild;
pChild += lstrlen(pChildren) + 1) {
//
// open the child device key
//
if (RegOpenKeyEx(ghEnumKey, pChild, 0, KEY_READ | KEY_WRITE,
&hKey) == ERROR_SUCCESS) {
RegSetValueEx(
hKey, pszRegValueBaseDevicePath, 0, REG_SZ,
(LPBYTE)pszDestinationID,
(lstrlen(pszDestinationID)+1) * sizeof(WCHAR));
}
RegCloseKey(hKey);
hKey = NULL;
}
}
//
// 6. Delete the original device instance key and recreate as a volatile
// symbolic link to the new (destination) registry key
//
SplitDeviceInstanceString(
pszSourceID,
szEnumerator,
szDevice,
szInstance);
//
// open a registry key to the device part (not including instance)
//
wsprintf(RegStr, TEXT("%s\\%s"),
szEnumerator,
szDevice);
if (RegOpenKeyEx(ghEnumKey, RegStr, 0, KEY_READ | KEY_WRITE,
&hKey) != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
//
// delete the instance key and any subkeys
//
if (!RegDeleteNode(hKey, szInstance)) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
//
// recreate the instance key as a volatile symbolic link key
//
RegStatus = RegCreateKeyEx(
hKey, szInstance, 0, NULL,
REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK,
KEY_ALL_ACCESS | KEY_CREATE_LINK, NULL, &hLinkKey, NULL);
if (RegStatus != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
wsprintf(RegStr, TEXT("\\Registry\\MACHINE\\%s\\%s"),
pszRegPathEnum,
pszDestinationID);
RegStatus = RegSetValueEx(
hLinkKey, TEXT("SymbolicLinkValue"), 0, REG_LINK,
(LPBYTE)RegStr, lstrlen(RegStr) * sizeof(WCHAR));
if (RegStatus != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
Clean0:
; // do nothing
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hSourceKey != NULL) {
RegCloseKey(hSourceKey);
}
if (hDestKey != NULL) {
RegCloseKey(hDestKey);
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
if (hLinkKey != NULL) {
RegCloseKey(hLinkKey);
}
if (pChildren != NULL) {
free(pChildren);
}
return Status;
} // MoveDevInst
CONFIGRET
SetupDevInst(
IN PCWSTR pszDeviceID,
IN ULONG ulFlags
)
/*++
Routine Description:
Arguments:
Return value:
The return value is CR_SUCCESS if the function suceeds and one of the
CR_* values if it fails.
--*/
{
CONFIGRET Status = CR_SUCCESS;
ULONG RegStatus = ERROR_SUCCESS;
HKEY hKey = NULL;
ULONG ulStatusFlag=0, ulProblem=0, ulDisableCount=0, ulSize=0;
try {
if (IsRootDeviceID(pszDeviceID)) {
goto Clean0;
}
switch(ulFlags) {
case CM_SETUP_DOWNLOAD:
//
// This flag is not supported until FULL PNP IMPLEMENTATION
//
// For full pnp, it will call the parent enumerator with the
// CONFIG_SETUP parameter (according to Win95 terms) which
// causes the enumerator to write out hardware ids and
// compatible ids as well as download any drivers.
//
break;
case CM_SETUP_DEVNODE_READY:
if (RegOpenKeyEx(ghEnumKey, pszDeviceID, 0, KEY_READ | KEY_WRITE,
&hKey) != ERROR_SUCCESS) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
ulStatusFlag = GetDeviceStatusFlag(hKey);
//
// If there's no problem or if install was done already
// (immediately) then there's nothing more to do
//
if (!(ulStatusFlag & DN_HAS_PROBLEM) ||
ulStatusFlag & DN_NO_WAIT_INSTALL) {
break;
}
//
// Check the disable count, if greater than zero, do nothing
// (There may be more to do here for full pnp implementation)
//
ulSize = sizeof(ulDisableCount);
if (RegQueryValueEx(
hKey, pszRegValueDisableCount, NULL, NULL,
(LPBYTE)&ulDisableCount, &ulSize) == ERROR_SUCCESS) {
if (ulDisableCount > 0) break;
}
//
// reset the problem
//
ulStatusFlag &= ~DN_HAS_PROBLEM;
ulStatusFlag |= DN_NEED_TO_ENUM;
ulProblem = 0;
RegSetValueEx(
hKey, pszRegValueProblem, 0, REG_DWORD,
(LPBYTE)&ulProblem, sizeof(ulProblem));
RegSetValueEx(
hKey, pszRegValueStatusFlags, 0, REG_DWORD,
(LPBYTE)&ulStatusFlag, sizeof(ulStatusFlag));
//
// For full pnp implementation, notify the parent
// enumerator that the devnode is ready, walk the devnode,
// sending everyone the equivalent of CONFIG_READY notfications,
// then start reprocessing the devnode
//
break;
}
Clean0:
; // do nothing
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
return Status;
} // SetupDevInst
CONFIGRET
EnableDevInst(
IN PCWSTR pszDeviceID
)
/*++
Routine Description:
This routine performs the server-side work for CM_Enable_DevNode. It
disables the specified device ID
Arguments:
pszDeviceID String that contains the device id to enable
Return value:
The return value is CR_SUCCESS if the function suceeds and one of the
CR_* values if it fails.
--*/
{
CONFIGRET Status = CR_SUCCESS;
ULONG RegStatus = ERROR_SUCCESS;
HKEY hKey = NULL;
ULONG ulDisableCount, ulProblem,ulStatusFlags, ulSize;
try {
//
// verify it isn't the root, can't disable/enable the root
//
if (IsRootDeviceID(pszDeviceID)) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
//
// open a key to the specified device id
//
if (RegOpenKeyEx(ghEnumKey, pszDeviceID, 0, KEY_READ | KEY_WRITE,
&hKey) != ERROR_SUCCESS) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
//
// get the current disable count from the registry
//
ulSize = sizeof(ulDisableCount);
if (RegQueryValueEx(
hKey, pszRegValueDisableCount, NULL, NULL,
(LPBYTE)&ulDisableCount, &ulSize) != ERROR_SUCCESS) {
//
// disable count not set yet, assume zero
//
ulDisableCount = 0;
}
//
// if the DisableCount is zero, then we're already enabled
//
if (ulDisableCount == 0) {
goto Clean0; // success
}
//
// Decrement disable count. If the disable count is greater than one,
// then just return (disable count must drop to zero in order to
// actually reenable)
//
ulDisableCount--;
if (ulDisableCount > 0) {
RegSetValueEx(
hKey, pszRegValueDisableCount, 0, REG_DWORD,
(LPBYTE)&ulDisableCount, sizeof(ulDisableCount));
goto Clean0; // success
}
//
// Retrieve the problem value from the registry
//
ulSize = sizeof(ulProblem);
if (RegQueryValueEx(
hKey, pszRegValueProblem, NULL, NULL,
(LPBYTE)&ulProblem, &ulSize) != ERROR_SUCCESS) {
//
// Problem not set yet, assume zero
//
ulProblem = 0;
}
//
// if the problem is only that the device instance is disabled,
// then reenable it now
//
if (ulProblem == CM_PROB_DISABLED) {
//
// Retrieve the StatusFlags value from the registry
//
ulSize = sizeof(ulStatusFlags);
if (RegQueryValueEx(
hKey, pszRegValueStatusFlags, NULL, NULL,
(LPBYTE)&ulStatusFlags, &ulSize) != ERROR_SUCCESS) {
//
// StatusFlag not set yet, assume zero
//
ulStatusFlags = 0;
}
ulProblem = 0;
CLEAR_FLAG(ulStatusFlags, DN_HAS_PROBLEM);
// for full pnp implementation, we'd set the DN_NEED_TO_ENUM flag
// and schedule an event to preprocess the tree. Just fake it for
// now and set to start.
SET_FLAG(ulStatusFlags, DN_STARTED);
RegSetValueEx(
hKey, pszRegValueDisableCount, 0, REG_DWORD,
(LPBYTE)&ulDisableCount, sizeof(ulDisableCount));
RegSetValueEx(
hKey, pszRegValueProblem, 0, REG_DWORD,
(LPBYTE)&ulProblem, sizeof(ulProblem));
RegSetValueEx(
hKey, pszRegValueStatusFlags, 0, REG_DWORD,
(LPBYTE)&ulStatusFlags, sizeof(ulStatusFlags));
}
//
// for now I'm not doing anything if there was a problem other than
// not being enabled
//
Clean0:
; // do nothing
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
return Status;
} // EnableDevInst
CONFIGRET
DisableDevInst(
IN PCWSTR pszDeviceID
)
/*++
Routine Description:
This routine performs the server-side work for CM_Disable_DevNode. It
disables the specified device ID.
Arguments:
pszDeviceID String that contains the device id to disable
Return value:
The return value is CR_SUCCESS if the function suceeds and one of the
CR_* values if it fails.
--*/
{
CONFIGRET Status = CR_SUCCESS;
ULONG RegStatus = ERROR_SUCCESS;
HKEY hKey = NULL;
ULONG ulDisableCount=0, ulProblem=0, ulStatusFlags=0, ulSize=0;
try {
//
// verify it isn't the root, can't disable/enable the root
//
if (IsRootDeviceID(pszDeviceID)) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
//
// open a key to the specified device id
//
if (RegOpenKeyEx(ghEnumKey, pszDeviceID, 0, KEY_READ | KEY_WRITE,
&hKey) != ERROR_SUCCESS) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
//
// get the current disable count from the registry
//
ulSize = sizeof(ulDisableCount);
if (RegQueryValueEx(
hKey, pszRegValueDisableCount, NULL, NULL,
(LPBYTE)&ulDisableCount, &ulSize) != ERROR_SUCCESS) {
//
// disable count not set yet, assume zero
//
ulDisableCount = 0;
}
//
// if the disable count is currently zero, then this is the first
// disable, so there's work to do. Otherwise, we just increment the
// disable count and resave it in the registry
//
if (ulDisableCount == 0) {
//
// Retrieve the StatusFlags value from the registry
//
ulSize = sizeof(ulStatusFlags);
if (RegQueryValueEx(
hKey, pszRegValueStatusFlags, NULL, NULL,
(LPBYTE)&ulStatusFlags, &ulSize) != ERROR_SUCCESS) {
//
// StatusFlag not set yet, assume zero
//
ulStatusFlags = 0;
}
//
// determine if the device instance is stopable
//
if (!(ulStatusFlags & DN_DISABLEABLE)) {
Status = CR_NOT_DISABLEABLE;
goto Clean0;
}
// For full PNP implementation, I would also walk the device
// instance tree and do a query stop for each device instance
// to determine if disableable
//
// if there are no other pending problems, then set disabled
// as the problem
//
if (!(ulStatusFlags & DN_HAS_PROBLEM)) {
SET_FLAG(ulStatusFlags, DN_HAS_PROBLEM);
ulProblem = CM_PROB_DISABLED;
// For full PNP implementation I would walk the device
// instance tree and stop each device instance and schedule
// and event to reprocess the tree
CLEAR_FLAG(ulStatusFlags, DN_STARTED);
RegSetValueEx(
hKey, pszRegValueProblem, 0, REG_DWORD,
(LPBYTE)&ulProblem, sizeof(ulProblem));
RegSetValueEx(
hKey, pszRegValueStatusFlags, 0, REG_DWORD,
(LPBYTE)&ulStatusFlags, sizeof(ulStatusFlags));
}
}
//
// update and save the disable count
//
ulDisableCount++;
RegSetValueEx(
hKey, pszRegValueDisableCount, 0, REG_DWORD,
(LPBYTE)&ulDisableCount, sizeof(ulDisableCount));
Clean0:
; // do nothing
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
return Status;
} // DisableDevInst
CONFIGRET
ReenumerateDevInst(
IN PCWSTR pszDeviceID
)
/*++
Routine Description:
This routine performs the server-side work for CM_Reenumerate_DevNode. It
reenumerates the specified device instance.
Arguments:
pszDeviceID String that contains the device id to reenumerate
Return value:
The return value is CR_SUCCESS if the function suceeds and one of the
CR_* values if it fails.
--*/
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
HKEY hKey = NULL;
WCHAR szService[MAX_SERVICE_NAME_LEN];
ULONG ulSize = 0, ulValue = 0, ulNumServices = 0, i = 0;
LPWSTR pszChildList = NULL, pszChild = NULL;
SC_HANDLE hSCManager = NULL;
PSERVICE_INFO pServiceList = NULL;
//
// NOTE: For Windows 95, the devnode is marked as needing to be
// reenumerating (by or'ing StatusFlags with DN_NEED_TO_ENUM), then
// sometime later, after the initial flurry of reenumeration requests,
// the whole tree is processed
//
try {
//
// open the registry enum key for this device id
//
if (RegOpenKeyEx(ghEnumKey, pszDeviceID, 0, KEY_READ,
&hKey) != ERROR_SUCCESS) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
//
// Is this device id present? Fail if it isn't.
//
ulSize = sizeof(ULONG);
RegStatus = RegQueryValueEx(
hKey, pszRegValueFoundAtEnum, NULL, NULL,
(LPBYTE)&ulValue, &ulSize);
if (RegStatus != ERROR_SUCCESS || ulValue == FALSE) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
//
// Has this device id been moved? Fail, don't forward, if it has.
//
if (IsDeviceMoved(pszDeviceID, hKey)) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
//
// Read in all the children of the parent we're trying to reenumerate
//
ulSize = 0;
RegStatus = RegQueryValueEx(
hKey, pszRegValueAttachedComponents, NULL, NULL,
NULL, &ulSize);
if (RegStatus != ERROR_SUCCESS) {
//
// we're done, there are no children to enumerate
//
goto Clean0;
}
pszChildList = malloc(ulSize);
if (pszChildList == NULL) {
Status = CR_OUT_OF_MEMORY;
goto Clean0;
}
RegStatus = RegQueryValueEx(
hKey, pszRegValueAttachedComponents, NULL, NULL,
(LPBYTE)pszChildList, &ulSize);
if (RegStatus != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
RegCloseKey(hKey);
hKey = NULL;
//
// open the Service Control Manager
//
hSCManager = OpenSCManager(NULL, NULL, GENERIC_READ);
if (hSCManager == NULL) {
Status = CR_FAILURE;
goto Clean0;
}
//
// Do to limitations with SUR, once a service has been started, I
// can not register additional devices with it. So, I need to
// group each service with it's list of devices and start all
// devices at the same time I start the service. The assumption
// is that I don't need to worry about any grandchildren of
// the parent device id, these legacy devices should all be
// root enumerated. Note that it is more efficient to create
// these groups of services and device lists in one pass through
// the child device list, since it
//
//
// allocate memory the first service info struct
//
pServiceList = (PSERVICE_INFO)malloc(sizeof(SERVICE_INFO));
if (pServiceList == NULL) {
Status = CR_OUT_OF_MEMORY;
goto Clean0;
}
for (pszChild = pszChildList;
*pszChild;
pszChild += lstrlen(pszChild) + 1) {
//
// validate the attached component - if the device instance
// doesn't exist, then delete the orphaned device instance key
//
if (!IsValidDeviceID(pszChild, NULL, 0)) {
if (MultiSzDeleteStringW(pszChildList, pszChild)) {
ulSize = MultiSzSizeW(pszChildList) * sizeof(WCHAR);
RegStatus = RegSetValueEx(hKey,
pszRegValueAttachedComponents, 0,
REG_MULTI_SZ, (LPBYTE)pszChildList,
ulSize);
}
}
//
// get the service name for this device id
//
#if 0
Status = GetServiceName(pszChild, szService, MAX_PATH);
if ((Status != ERROR_SUCCESS) || (*szService == '\0')) {
//
// no service to start, just mark device as started
//
StartDevice(pszChild, NULL);
goto NextDevice;
}
#endif
if (!GetActiveService(pszChild, szService)) {
//
// no active service yet, look for previously installed
// service entry
//
Status = GetServiceName(pszChild, szService, MAX_PATH);
if ((Status != CR_SUCCESS) || (*szService == '\0')) {
//
// no service to start, just mark device as started
//
StartDevice(pszChild, NULL);
goto NextDevice;
}
}
//
// check if this service name has already been found
//
i = 0;
while (i < ulNumServices) {
if (lstrcmpi(pServiceList[i].szService, szService) == 0) {
pServiceList[i].ulDeviceListSize +=
(lstrlen(pszChild) + 1) * sizeof(WCHAR);
pServiceList[i].pszDeviceList = realloc(
pServiceList[i].pszDeviceList,
pServiceList[i].ulDeviceListSize);
if (pServiceList[i].pszDeviceList == NULL) {
Status = CR_OUT_OF_MEMORY;
goto Clean0;
}
ulSize = pServiceList[i].ulDeviceListSize;
MultiSzAppendW(
pServiceList[i].pszDeviceList,
&ulSize,
pszChild);
break;
}
i++;
}
//
// if the service hasn't been found yet, set up a new ServiceInfo
// struct for the nwe service
//
if (i >= ulNumServices) {
pServiceList = realloc(
pServiceList,
(ulNumServices + 1) * sizeof(SERVICE_INFO));
lstrcpy(pServiceList[ulNumServices].szService, szService);
pServiceList[ulNumServices].ulDeviceListSize =
(lstrlen(pszChild) + 2) * sizeof(WCHAR);
pServiceList[ulNumServices].pszDeviceList = malloc(
pServiceList[ulNumServices].ulDeviceListSize);
ulSize = pServiceList[ulNumServices].ulDeviceListSize;
lstrcpy(pServiceList[ulNumServices].pszDeviceList, pszChild);
pServiceList[ulNumServices].pszDeviceList[lstrlen(pszChild)+1] = '\0';
ulNumServices++;
}
NextDevice:
;
}
//
// Now, start each service and it's list of devices
//
for (i = 0; i < ulNumServices; i++) {
Status = StartDeviceList(
pServiceList[i].pszDeviceList,
pServiceList[i].szService,
hSCManager);
if (Status != CR_SUCCESS) {
goto Clean0;
}
}
Clean0:
;
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (pServiceList != NULL) {
for (i=0; i < ulNumServices; i++) {
if (pServiceList[i].pszDeviceList != NULL) {
free(pServiceList[i].pszDeviceList);
}
}
free(pServiceList);
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
if (pszChildList != NULL) {
free(pszChildList);
}
if (hSCManager != NULL) {
CloseServiceHandle(hSCManager);
}
return Status;
} // ReenumerateDevInst
CONFIGRET
QueryRemoveSubTree(
IN PCWSTR pszDeviceID,
IN ULONG ulFlags
)
/*++
Routine Description:
This routine performs the server-side work for CM_Query_Remove_Subtree. It
determines whether subtree can be removed.
Arguments:
pszDeviceID String that contains the device id to query remove
ulFlags Either CM_QUERY_REMOVE_UI_OK or CM_QUERY_REMOVE_UI_NOT_OK
Return value:
The return value is CR_SUCCESS if the function suceeds and one of the
CR_* values if it fails.
--*/
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
WCHAR szService[MAX_PATH], szActiveService[MAX_PATH];
HKEY hKey = NULL;
ULONG ulSize = 0, ulValue = 0, ulServices = 0, i = 0;
SC_HANDLE hSCManager = NULL, hService = NULL;
LPENUM_SERVICE_STATUS pServices = NULL;
try {
if (IsRootDeviceID(pszDeviceID)) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
//
// open the registry enum key for this device id
//
if (RegOpenKeyEx(ghEnumKey, pszDeviceID, 0, KEY_READ,
&hKey) != ERROR_SUCCESS) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
//
// Has this device id been moved (fail, don't forward, if it has)?
//
if (IsDeviceMoved(pszDeviceID, hKey)) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
szService[0] = 0x0;
szActiveService[0] = 0x0;
//
// retrieve the original service name
//
ulSize = MAX_PATH * sizeof(WCHAR);
RegStatus = RegQueryValueEx(hKey, pszRegValueService, NULL, NULL,
(LPBYTE)szService, &ulSize);
RegCloseKey(hKey);
hKey = NULL;
//
// retrieve the controlling service name
//
GetActiveService(pszDeviceID, szActiveService);
if (szService[0] == 0x0 && szActiveService[0] == 0x0) {
//
// no service specified in either location, just stop
// the device itself (mark as stopped)
//
Status = QueryStopDevice(pszDeviceID);
goto Clean0;
}
//
// open the Service Control Manager
//
hSCManager = OpenSCManager(NULL, NULL, GENERIC_READ);
if (hSCManager == NULL) {
Status = CR_FAILURE;
goto Clean0;
}
//-------------------------------------------------------------
// First process active service (if exists)
//-------------------------------------------------------------
if (*szActiveService) {
hService = OpenService(hSCManager, szActiveService,
SERVICE_QUERY_STATUS);
if (hService == NULL) {
goto Clean0; // not active, so okay to stop
}
//
// Can the controlling service can be stopped?
//
if ((Status = QueryStopService(hService,
szActiveService)) != CR_SUCCESS) {
goto Clean0;
}
//
// If a dependent service is stopped, there's no way to reload it
// during reenumeration as I'm not currently saving any kind of
// state information between CM calls. For now, I'll simply fail
// any query remove request to a device whose controlling service
// has dependent services.
//
if (EnumDependentServices(
hService, SERVICE_ACTIVE, pServices, ulSize, &ulSize,
&ulServices) && ulServices > 0) {
Status = CR_REMOVE_VETOED;
goto Clean0;
}
CloseServiceHandle(hService);
hService = NULL;
}
//-------------------------------------------------------------
// Second, process the original service name
//-------------------------------------------------------------
if (*szService && lstrcmpi(szService, szActiveService) != 0) {
hService = OpenService(hSCManager, szService,
SERVICE_QUERY_STATUS);
if (hService == NULL) {
goto Clean0; // not active, so okay to stop
}
//
// Can the controlling service can be stopped?
//
if ((Status = QueryStopService(hService,
szService)) != CR_SUCCESS) {
goto Clean0;
}
//
// If a dependent service is stopped, there's no way to reload it
// during reenumeration as I'm not currently saving any kind of
// state information between CM calls. For now, I'll simply fail
// any query remove request to a device whose controlling service
// has dependent services.
//
if (EnumDependentServices(hService, SERVICE_ACTIVE, pServices,
ulSize, &ulSize, &ulServices) &&
ulServices > 0) {
Status = CR_REMOVE_VETOED;
goto Clean0;
}
CloseServiceHandle(hService);
hService = NULL;
}
#if 0
//
// If the specified device and controlling service can be stopped, then
// check whether any dependent services (and device they control) can
// be stopped
//
//
// enumerate any services that depend on this service
//
if (!EnumDependentServices(
hService, SERVICE_ACTIVE, pServices, ulSize, &ulSize,
&ulServices)) {
if (GetLastError() == ERROR_MORE_DATA) {
pServices = realloc(pServices, ulSize);
if (pServices == NULL) {
Status = CR_OUT_OF_MEMORY;
goto Clean0;
}
if (!EnumDependentServices(
hService, SERVICE_ACTIVE, pServices, ulSize, &ulSize,
&ulServices)) {
Status = CR_FAILURE;
goto Clean0;
}
}
else {
Status = CR_FAILURE;
goto Clean0;
}
}
CloseServiceHandle(hService);
hService = NULL;
//
// check each dependent service, see if it can be removed
//
for (i=0; i < ulServices; i++) {
hService = OpenService(hSCManager, pServices[i].lpServiceName,
SERVICE_QUERY_STATUS);
//
// if service isn't active, okay to skip it
//
if (hService != NULL) {
Status = QueryStopService(hService, pServices[i].lpServiceName);
if (Status != CR_SUCCESS) {
goto Clean0;
}
}
}
#endif
// if we got to here, must be okay to remove the device
Clean0:
;
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
if (hService != NULL) {
CloseServiceHandle(hService);
}
if (hSCManager != NULL) {
CloseServiceHandle(hSCManager);
}
return Status;
} // QueryRemoveSubTree
CONFIGRET
RemoveSubTree(
IN PCWSTR pszDeviceID,
IN ULONG ulFlags
)
/*++
Routine Description:
This routine performs the server-side work for CM_Remove_Subtree. It
removes the specified subtree.
Arguments:
pszDeviceID String that contains the device id to remove
ulFlags Either CM_REMOVE_UI_OK or CM_REMOVE_UI_NOT_OK
Return value:
The return value is CR_SUCCESS if the function suceeds and one of the
CR_* values if it fails.
--*/
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
HKEY hKey = NULL;
PWSTR pList = NULL, pNext = NULL;
WCHAR szService[2][MAX_PATH];
BOOL bStopped = FALSE;
ULONG Index = 0, ulValue = 0, ulLength = 0, ulServices = 0,
ControllingServices = 2, i = 0;
SC_HANDLE hSCManager = NULL, hService = NULL, hDependentService = NULL;
LPENUM_SERVICE_STATUS pServices = NULL;
SERVICE_STATUS ServiceStatus, DependStatus;
//
// in a full pnp implementation, I will walk the subtree, sending
// remove and remove complete messages (and process the ulFlags param)
//
// SUR assumptions: QueryRemove should have failed any requests to
// remove a device that has children or if any other
// device controlled by that service are not marked
// as removable.
UNREFERENCED_PARAMETER(ulFlags);
try {
if (IsRootDeviceID(pszDeviceID)) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
//
// open the registry enum key for this device id
//
if (RegOpenKeyEx(ghEnumKey, pszDeviceID, 0, KEY_READ | KEY_WRITE,
&hKey) != ERROR_SUCCESS) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
//
// Has this device id been moved (fail, don't forward, if it has)?
//
if (IsDeviceMoved(pszDeviceID, hKey)) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
//
// First, mark the specified device as being removed (no longer
// present). Other devices (children or devices of dependent
// services) will be marked as not started but won't be marked as
// removed.
//
ulValue = FALSE;
RegSetValueEx(hKey, pszRegValueFoundAtEnum, 0, REG_DWORD,
(LPBYTE)&ulValue, sizeof(ULONG));
szService[0][0] = 0x0;
szService[1][0] = 0x0;
//
// get the active service
//
GetActiveService(pszDeviceID, szService[0]);
//
// get the original service
//
ulLength = MAX_PATH * sizeof(WCHAR);
if (RegQueryValueEx(hKey, pszRegValueService, NULL, NULL,
(LPBYTE)szService[1], &ulLength) != ERROR_SUCCESS) {
szService[1][0] = 0x0;
}
if (szService[0][0] == 0x0 && szService[1][0] == 0x0) {
//
// No service has been registered for this device yet so I only
// need to stop the specified device id
//
if ((Status = StopDevice(pszDeviceID)) != CR_SUCCESS) {
goto Clean0;
}
goto Clean0; // done
}
//
// allocate a buffer to hold dependent services in
//
ulLength = 4096;
pServices = (LPENUM_SERVICE_STATUS)malloc(ulLength);
if (pServices == NULL) {
Status = CR_OUT_OF_MEMORY;
goto Clean0;
}
//
// open the Service Control Manager
//
hSCManager = OpenSCManager(NULL, NULL, GENERIC_READ);
if (hSCManager == NULL) {
Status = CR_FAILURE;
goto Clean0;
}
if (lstrcmpi(szService[0], szService[1]) == 0) {
ControllingServices = 1;
}
//
// process both the active and original service
//
for (Index = 0; Index < ControllingServices; Index++) {
if (*szService[Index]) {
memset(pServices, 0, ulLength);
//
// open a handle to this service via the Service Control Manager
//
hService = OpenService(hSCManager, szService[Index],
SERVICE_ENUMERATE_DEPENDENTS |
SERVICE_QUERY_STATUS | SERVICE_STOP);
if (hService == NULL) {
goto NextService; // nothing to stop
}
//
// Is the service running?
//
if (!QueryServiceStatus(hService, &ServiceStatus) ||
ServiceStatus.dwCurrentState == SERVICE_STOPPED) {
//
// Service isn't running, so don't need to worry about
// dependent services
//
goto NextService;
}
//
// enumerate any services that depend on this service
//
if (!EnumDependentServices(hService, SERVICE_ACTIVE,
pServices, ulLength, &ulLength,
&ulServices)) {
if (GetLastError() == ERROR_MORE_DATA) {
pServices = realloc(pServices, ulLength);
if (pServices == NULL) {
Status = CR_OUT_OF_MEMORY;
goto Clean0;
}
if (!EnumDependentServices(hService, SERVICE_ACTIVE,
pServices, ulLength,
&ulLength, &ulServices)) {
goto NextService;
}
} else {
goto NextService;
}
}
//
// stop the service and any dependent services
//
for (i=0; i < ulServices; i++) {
//
// Review - PaulaT
//
// Current thinking (as of 5/30/96) is to not stop
// the service if it's already started, whether it's
// a service or a kernel-mode driver. (shouldn't
// really happen though because the query should
// have failed).
//
#if 0
//
// open a handle to this service
//
hDependentService = OpenService(hSCManager,
pServices[i].lpServiceName, SERVICE_STOP);
if (hDependentService == NULL) {
goto NextService;
}
//
// attempt to stop the service
//
ControlService(hDependentService,
SERVICE_CONTROL_STOP,
&ServiceStatus);
CloseServiceHandle(hDependentService);
#endif
//
// now stop the devices controlled by this service
//
if ((Status = StopService(pServices[i].lpServiceName))
!= CR_SUCCESS) {
goto NextService;
}
}
// Review - PaulaT
//
// Current thinking is not to stop drivers on remove
//
#if 0
//
// now attempt to stop the main service (dependents have been
// stopped)
//
ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus);
#endif
//
// stop the devices controlled by the main service
//
if ((Status = StopService(szService[Index])) != CR_SUCCESS) {
goto NextService;
}
bStopped = TRUE;
}
NextService:
;
}
if (!bStopped) {
//
// no services were stopped - at least mark device as stopped
//
Status = StopDevice(pszDeviceID);
}
Clean0:
; // do nothing
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hDependentService != NULL) {
CloseServiceHandle(hDependentService);
}
if (hService != NULL) {
CloseServiceHandle(hService);
}
if (hSCManager != NULL) {
CloseServiceHandle(hSCManager);
}
if (pServices != NULL) {
free(pServices);
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
return Status;
} // RemoveSubTree
CONFIGRET
CreateDefaultDeviceInstance(
IN PCWSTR pszDeviceID,
IN PCWSTR pszParentID,
IN BOOL bPhantom
)
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
HKEY hKey1 = NULL, hKey2 = NULL;
WCHAR szBase[MAX_DEVICE_ID_LEN];
WCHAR szDevice[MAX_DEVICE_ID_LEN];
WCHAR szInstance[MAX_DEVICE_ID_LEN];
ULONG ulValue=0, ulDisposition=0, ulSize=0, i=0;
LPWSTR pChildren = NULL;
SplitDeviceInstanceString(
pszDeviceID,
szBase,
szDevice,
szInstance);
//
// open a key to base enumerator (create if doesn't already exist)
//
RegStatus = RegCreateKeyEx(
ghEnumKey, szBase, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
NULL, &hKey2, NULL);
if (RegStatus != ERROR_SUCCESS) {
return CR_REGISTRY_ERROR;
}
//
// open a key to device (create if doesn't already exist)
//
RegStatus = RegCreateKeyEx(
hKey2, szDevice, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
NULL, &hKey1, NULL);
if (RegStatus != ERROR_SUCCESS) {
RegCloseKey(hKey2);
return CR_REGISTRY_ERROR;
}
RegCloseKey(hKey2); // done with Base Key
//
// set default device values
//
if (ulDisposition == REG_CREATED_NEW_KEY) {
ulValue = TRUE;
RegSetValueEx(
hKey1, pszRegValueNewDevice, 0, REG_DWORD,
(LPBYTE)&ulValue, sizeof(ulValue));
}
//
// open a key to device (create if doesn't already exist), find an
// unused instance value if necessary
//
RegStatus = RegOpenKeyEx(
hKey1, szInstance, 0, KEY_SET_VALUE, &hKey2);
if (RegStatus == ERROR_SUCCESS) {
//
// find a new instance id to use
//
RegCloseKey(hKey2);
i = 0;
while (i <= 9999) {
wsprintf(szInstance, TEXT("%04u"), i);
RegStatus = RegOpenKeyEx(
hKey1, szInstance, 0, KEY_SET_VALUE, &hKey2);
if (RegStatus != ERROR_SUCCESS) {
// instance key does not exist, use this instance
break;
}
// instance key exists, try next one
i++;
}
if (i > 9999) {
RegCloseKey(hKey2);
RegCloseKey(hKey1);
return CR_FAILURE; // we ran out of instances (unlikely)
}
}
//
// we have an instance now, open the device instance key
//
RegStatus = RegCreateKeyEx(
hKey1, szInstance, 0, NULL, REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS, NULL, &hKey2, &ulDisposition);
if (RegStatus != ERROR_SUCCESS) {
RegCloseKey(hKey1);
return CR_REGISTRY_ERROR;
}
RegCloseKey(hKey1); // done with device key
//
// set the default device instance values
//
// BUGBUG : revisit these default values
//
RegSetValueEx(
hKey2, pszRegValueBaseDevicePath, 0, REG_SZ,
(LPBYTE)pszParentID, (lstrlen(pszParentID)+1) * sizeof(WCHAR));
#if 0
ulValue = TRUE;
RegSetValueEx(
hKey2, pszRegValueNewInstance, 0, REG_DWORD,
(LPBYTE)&ulValue, sizeof(ulValue));
#endif
if (bPhantom) {
//
// phantoms are not present by definition
//
MarkDevicePresent(hKey2, FALSE);
MarkDevicePhantom(hKey2, TRUE);
}
else {
//
// a normal devinst is considered present
//
MarkDevicePresent(hKey2, TRUE);
}
RegCloseKey(hKey2); // done with instance key
//
// set the attachcomponents value of the parent to include this new child
//
if (RegOpenKeyEx(ghEnumKey, pszParentID, 0, KEY_READ | KEY_WRITE,
&hKey1) != ERROR_SUCCESS) {
return CR_REGISTRY_ERROR;
}
RegStatus = RegQueryValueEx(
hKey1, pszRegValueAttachedComponents, NULL, NULL, NULL, &ulValue);
if (RegStatus != ERROR_SUCCESS) {
//
// most likely the attached components just hasn't been created
// yet, so set this value to just this new device instance
//
ulSize = (lstrlen(pszDeviceID) + 1) * sizeof(WCHAR);
RegSetValueEx(
hKey1, pszRegValueAttachedComponents, 0, REG_MULTI_SZ,
(LPBYTE)pszDeviceID, ulSize);
RegCloseKey(hKey1);
return CR_SUCCESS;
}
//
// the attached components value already exists, we'll need to
// append this device to the list of device ids.
//
ulSize = ulValue + (lstrlen(pszDeviceID) + 2) * sizeof(WCHAR);
pChildren = LocalAlloc(LPTR, ulSize);
if (pChildren == NULL) {
RegCloseKey(hKey1);
return CR_OUT_OF_MEMORY;
}
RegStatus = RegQueryValueEx(
hKey1, pszRegValueAttachedComponents, NULL, NULL,
(LPBYTE)pChildren, &ulValue);
if (RegStatus != ERROR_SUCCESS) {
RegCloseKey(hKey1);
LocalFree(pChildren);
return CR_REGISTRY_ERROR;
}
if (!MultiSzSearchStringW((LPCWSTR)pChildren, pszDeviceID)) {
MultiSzAppendW(pChildren, &ulSize, pszDeviceID);
RegSetValueEx(
hKey1, pszRegValueAttachedComponents, 0, REG_MULTI_SZ,
(LPBYTE)pChildren, ulSize);
}
LocalFree(pChildren);
RegCloseKey(hKey1);
return Status;
} // CreateDefaultDeviceInstance
BOOL
IsDevicePresent(
IN OUT HKEY hKey
)
{
ULONG ulSize = 0, ulPresent = FALSE;
//
// retrieve the FoundAtEnum value
//
ulSize = sizeof(ulPresent);
if (RegQueryValueEx(
hKey, pszRegValueFoundAtEnum, NULL, NULL,
(LPBYTE)&ulPresent, &ulSize) != ERROR_SUCCESS) {
//
// status flags not set yet, assume zero
//
ulPresent = FALSE;
}
return (BOOL)ulPresent;
} // IsDevicePresent
BOOL
IsDeviceInstalled(
IN OUT HKEY hKey
)
{
ULONG ulSize = 0, ulInstalled = FALSE;
//
// retrieve the NewInstance value
//
ulSize = sizeof(ulInstalled);
if (RegQueryValueEx(
hKey, pszRegValueNewInstance, NULL, NULL,
(LPBYTE)&ulInstalled, &ulSize) != ERROR_SUCCESS) {
//
// status flags not set yet, assume zero
//
ulInstalled = FALSE;
}
return (BOOL)ulInstalled;
} // IsDeviceInstalled
#if 0
CONFIGRET
CleanupMovedDevNode(
IN PCWSTR pszMovedID,
IN HKEY hMovedKey,
IN PCWSTR pszNewID,
IN HKEY hNewKey
)
/*++
Routine Description:
This routine currently cleans up a moved device instance id by marking it
with a problem and setting the MovedTo value.
Arguments:
pszMovedID Pointer to string containing the moved device ID string.
hMovedKey Open registry key to the moved device ID.
pszNewID Pointer to string containing the new device ID string.
hNewKey Open registry key to the new device ID.
Return value:
The return value is CR_SUCCESS if the function suceeds and one of the
CR_* values if it fails.
--*/
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
ULONG ulValue = CM_PROB_MOVED;
try {
//
// mark the moved device instance with a problem
//
RegStatus = RegSetValueEx(
hMovedKey, pszRegValueProblem, 0, REG_DWORD,
(LPBYTE)&ulValue, sizeof(ulValue));
if (RegStatus != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
//
// mark the new device instance this one has been moved to
//
RegStatus = RegSetValueEx(
hMovedKey, pszRegValueMovedTo, 0, REG_SZ,
(LPBYTE)pszNewID, (lstrlen(pszNewID) + 1) * sizeof(WCHAR));
if (RegStatus != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
Clean0:
; // do nothing
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
return Status;
} // CleanupMovedDevNode
#endif
ULONG
GetDeviceStatusFlag(
IN HKEY hKey
)
{
ULONG ulSize = 0, ulStatusFlag = 0;
//
// retrieve the status flag value
//
ulSize = sizeof(ulStatusFlag);
if (RegQueryValueEx(
hKey, pszRegValueStatusFlags, NULL, NULL,
(LPBYTE)&ulStatusFlag, &ulSize) != ERROR_SUCCESS) {
//
// status flags not set yet, assume zero
//
ulStatusFlag = 0;
}
return ulStatusFlag;
} // GetDeviceStatusFlag
ULONG
GetDeviceConfigFlag(
IN HKEY hKey
)
{
ULONG ulSize = 0, ulConfigFlag = 0;
//
// retrieve the device instance config flag
//
ulSize = sizeof(ulConfigFlag);
if (RegQueryValueEx(
hKey, pszRegValueStatusFlags, NULL, NULL,
(LPBYTE)&ulConfigFlag, &ulSize) != ERROR_SUCCESS) {
//
// status flags not set yet, assume zero
//
ulConfigFlag = 0;
}
return ulConfigFlag;
} // GetDeviceConfigFlag
ULONG
GetCurrentConfigFlag(
IN PCWSTR pDeviceID
)
{
HKEY hKey;
WCHAR RegStr[MAX_PATH];
ULONG ulSize = 0, ulCSConfigFlag = 0;
//
// open a key to the current hardware profile for this device instance
//
// BUGBUG: handle moved device id case?
//
wsprintf(RegStr, TEXT("%s\\%s\\%s\\%s"),
pszRegPathHwProfiles, // System\CCC\Hardware Profiles
pszRegKeyCurrent, // Current
pszRegPathEnum, // System\Enum
pDeviceID);
if (RegOpenKeyEx(
HKEY_LOCAL_MACHINE, RegStr, 0, KEY_QUERY_VALUE, &hKey)
!= ERROR_SUCCESS) {
return 0;
}
//
// retrieve the config specific flag
//
ulSize = sizeof(ulCSConfigFlag);
if (RegQueryValueEx(
hKey, pszRegValueCSConfigFlags, NULL, NULL,
(LPBYTE)&ulCSConfigFlag, &ulSize) != ERROR_SUCCESS) {
//
// status flags not set yet, assume zero
//
ulCSConfigFlag = 0;
}
RegCloseKey(hKey);
return ulCSConfigFlag;
} // GetCurrentConfigFlag
BOOL
MarkDevicePresent(
IN HKEY hKey,
IN ULONG ulValue
)
{
//
// a present device should have a FoundAtEnum value of TRUE
//
RegSetValueEx(
hKey, pszRegValueFoundAtEnum, 0, REG_DWORD,
(LPBYTE)&ulValue, sizeof(ULONG));
return TRUE;
} // MarkDevicePresent
BOOL
MarkDevicePhantom(
IN HKEY hKey,
IN ULONG ulValue
)
{
//
// a phantom device should have a Phantom value of TRUE
//
RegSetValueEx(
hKey, pszRegValuePhantom, 0, REG_DWORD,
(LPBYTE)&ulValue, sizeof(ULONG));
return TRUE;
} // MarkDevicePhantom
CONFIGRET
GenerateDeviceInstance(
LPWSTR pszFullDeviceID,
LPWSTR pszDeviceID
)
{
LONG RegStatus = ERROR_SUCCESS;
WCHAR RegStr[MAX_PATH];
HKEY hKey;
ULONG ulInstanceID = 0;
LPWSTR p;
//
// validate the device id component (can't have invalid character or a
// backslash)
//
for (p = pszDeviceID; *p; p++) {
if (*p <= TEXT(' ') ||
*p > (WCHAR)0x7F ||
*p == TEXT('\\')) {
return CR_INVALID_DEVICE_ID;
}
}
lstrcpy(pszFullDeviceID, pszRegKeyRootEnum); // Root
lstrcat(pszFullDeviceID, TEXT("\\"));
lstrcat(pszFullDeviceID, pszDeviceID);
//
// trying opening instance ids until we find one that fails (doesn't
// already exist)
//
while (RegStatus == ERROR_SUCCESS && ulInstanceID < 10000) {
wsprintf(RegStr, TEXT("%s\\%04u"),
pszFullDeviceID,
ulInstanceID);
RegStatus = RegOpenKeyEx(ghEnumKey, RegStr, 0, KEY_QUERY_VALUE, &hKey);
if (RegStatus == ERROR_SUCCESS) {
RegCloseKey(hKey);
ulInstanceID++;
}
}
if (ulInstanceID > 9999) {
return CR_FAILURE; // instances all used up, seems unlikely
}
wsprintf(pszFullDeviceID, TEXT("%s\\%s\\%04u"),
pszRegKeyRootEnum, // Root
pszDeviceID, // Device ID
ulInstanceID); // Instance ID
return CR_SUCCESS;
} // GenerateDeviceInstance
CONFIGRET
BuildServiceList(
IN PCWSTR pDeviceID,
OUT PWSTR *pDeviceList,
OUT PWSTR *pServiceList
)
{
LONG RegStatus = ERROR_SUCCESS;
HKEY hKey = NULL;
WCHAR RegStr[MAX_PATH], szBuffer[MAX_PATH];
PWSTR pCurrent = NULL, pNext = NULL, pTemp = NULL;
ULONG ulDeviceLen=0, ulDeviceFree=0, ulLength=0, ulCount=0,
ulServiceLen=0, ulServiceFree=0, i=0;
//
// Allocate a 2K buffer to start with, for holding subtree (the list
// of attached componentes)
//
ulDeviceLen = ulDeviceFree = 2048;
*pDeviceList = malloc(ulDeviceLen * sizeof(WCHAR));
if (*pDeviceList == NULL) {
return CR_OUT_OF_MEMORY;
}
memset(*pDeviceList, 0, ulDeviceLen * sizeof(WCHAR));
//
// Allocate a 2K buffer to start with, for holding the list of services
// that control the device list I'm building
//
ulServiceLen = ulServiceFree = 2048;
*pServiceList = malloc(ulServiceLen * sizeof(WCHAR));
if (*pServiceList == NULL) {
return CR_OUT_OF_MEMORY;
}
memset(*pServiceList, 0, ulServiceLen * sizeof(WCHAR));
//
// pNext always points to free space at end of buffer, pCurrent always
// points to device instance that we're finding attached components on
//
pNext = pCurrent = *pDeviceList;
//
// put the base device instance at the start of the list
//
lstrcpy(pNext, pDeviceID);
ulLength = lstrlen(pDeviceID) + 1;
pNext += ulLength;
ulDeviceFree -= ulLength;
//
// cycle through, getting attached components, starting from bottom and
// working my way up each leaf
//
for(; *pCurrent; pCurrent += lstrlen(pCurrent) + 1) {
//
// open a handle to the registry key for this device instance
//
if (RegOpenKeyEx(ghEnumKey, pCurrent, 0, KEY_READ, &hKey)
!= ERROR_SUCCESS) {
free(*pDeviceList);
free(*pServiceList);
return CR_INVALID_DEVINST;
}
//
// if not a valid device instance, skip to next device id
//
if (!IsValidDeviceID(pCurrent, hKey, PNP_NOT_MOVED | PNP_PRESENT)) {
goto NextComponent;
}
//--------------------------------------------------------
// Query components attached to this device instance
//--------------------------------------------------------
ulLength = ulDeviceFree * sizeof(WCHAR); // convert to bytes
RegStatus = RegQueryValueEx(
hKey, pszRegValueAttachedComponents, NULL, NULL,
(LPBYTE)pNext, &ulLength);
if (RegStatus == ERROR_MORE_DATA) {
//
// realloc a bigger buffer and try again
//
ulDeviceLen += 2048;
ulDeviceFree += 2048;
pTemp = *pDeviceList;
*pDeviceList = realloc(*pDeviceList, ulDeviceLen * sizeof(WCHAR));
if (*pDeviceList == NULL) {
RegCloseKey(hKey);
free(pTemp);
free(*pServiceList);
return CR_OUT_OF_MEMORY;
}
// BUGBUG: can get duplicates here
ulLength = ulDeviceFree * sizeof(WCHAR);
if (RegQueryValueEx(
hKey, pszRegValueAttachedComponents, NULL, NULL,
(LPBYTE)pNext, &ulLength) != ERROR_SUCCESS) {
RegCloseKey(hKey);
free(*pDeviceList);
free(*pServiceList);
return CR_REGISTRY_ERROR;
}
}
//
// if additional attached components were added to the list,
// update pointers and sizes (even if there were no attached
// components for this device id, still want to check out it's
// service list)
//
if (RegStatus == ERROR_SUCCESS) {
ulLength--; // multi_sz ends in double null term
ulLength /= sizeof(WCHAR); // convert back to chars
pNext += ulLength;
ulDeviceFree -= ulLength;
}
//--------------------------------------------------------
// Query any other devices attached to this service
//--------------------------------------------------------
#if 0
ulLength = MAX_PATH * sizeof(WCHAR); // convert to bytes
RegStatus = RegQueryValueEx(
hKey, pszRegValueService, NULL, NULL,
(LPBYTE)szBuffer, &ulLength);
#endif
RegCloseKey(hKey);
hKey = NULL;
GetActiveService(pCurrent, szBuffer);
//
// while I've got the service name, save it in the service list
//
if (!MultiSzSearchStringW(*pServiceList, szBuffer)) {
ulLength = ulServiceFree;
MultiSzAppendW(*pServiceList, &ulLength, szBuffer);
ulServiceFree -= lstrlen(szBuffer) + 1;
// BUGBUG: multisz takes bytes not chars
// BUGBUG: check memory and realloc if necessary
}
//
// open a handle to the "Enum" registry key for this service
//
wsprintf(RegStr, TEXT("%s\\%s"),
szBuffer,
pszRegKeyEnum);
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegStr, 0, KEY_READ, &hKey)
!= ERROR_SUCCESS) {
goto NextComponent;
}
if (RegOpenKeyEx(ghServicesKey, RegStr, 0, KEY_READ, &hKey)
!= ERROR_SUCCESS) {
goto NextComponent;
}
//
// query the number of device instances controlled by this service
//
ulLength = sizeof(ULONG);
RegStatus = RegQueryValueEx(
hKey, pszRegValueCount, NULL, NULL,
(LPBYTE)&ulCount, &ulLength);
//
// if there's only one device instance, it's the one we just visited,
// so go to the next component (or if couldn't read the count value)
//
if (RegStatus != ERROR_SUCCESS || ulCount <= 1) {
goto NextComponent;
}
//
// read in all the device instances controlled by this service but check
// for duplicates before adding to the service list
//
for (i = 0; i < ulCount; i++) {
wsprintf(RegStr, TEXT("%d"), i);
ulLength = MAX_PATH * sizeof(WCHAR);
RegStatus = RegQueryValueEx(
hKey, RegStr, NULL, NULL,
(LPBYTE)szBuffer, &ulLength);
if (RegStatus != ERROR_SUCCESS) {
goto NextComponent;
}
if (!MultiSzSearchStringW(*pDeviceList, szBuffer)) {
ulLength /= sizeof(WCHAR); // convert back to chars
if (ulDeviceFree < ulLength) {
//
// if not enough room in device list buffer, realloc the buffer
//
ulDeviceLen += 2048;
ulDeviceFree += 2048;
pTemp = *pDeviceList;
*pDeviceList = realloc(*pDeviceList, ulDeviceLen * sizeof(WCHAR));
if (*pDeviceList == NULL) {
RegCloseKey(hKey);
free(pTemp);
free(*pServiceList);
return CR_OUT_OF_MEMORY;
}
}
lstrcpy(pNext, szBuffer);
pNext += ulLength;
ulDeviceFree -= ulLength;
}
}
NextComponent:
if (hKey != NULL) {
RegCloseKey(hKey);
}
}
//
// this is reg_multi_sz format, so tack on a second null terminator
//
*(++pNext) = '\0';
return CR_SUCCESS;
} // BuildServiceList
#if 0
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// DEAD CODE, LEAVE TEMPORARILY FOR REFERENCE
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
CONFIGRET
StartDevice(
IN LPCWSTR pszDeviceID,
SC_HANDLE hSCManager
)
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
WCHAR RegStr[MAX_CM_PATH], szService[MAX_PATH];
HKEY hKey = NULL;
ULONG ulLength = 0, ulValue = 0;
LPWSTR pChildList = NULL, pszChild = NULL;
SC_HANDLE hService = NULL;
SERVICE_STATUS ServiceStatus;
NTSTATUS NtStatus = STATUS_SUCCESS;
PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA ControlData;
//
// assume this device id has already been validated, so just open it
//
wsprintf(RegStr, TEXT("%s\\%s"),
pszRegPathEnum,
pszDeviceID);
RegStatus = RegOpenKeyEx(
HKEY_LOCAL_MACHINE, RegStr, 0, KEY_READ | KEY_SET_VALUE, &hKey);
if (RegStatus != ERROR_SUCCESS) {
goto Clean0; // nothing to start
}
//
// if it's got a problem, skip it [Could check status for DN_HAS_PROBLEM instead]
//
ulLength = sizeof(ULONG);
RegStatus = RegQueryValueEx(
hKey, pszRegValueProblem, NULL, NULL,
(LPBYTE)&ulValue, &ulLength);
if (RegStatus == ERROR_SUCCESS && ulValue != 0x0) {
goto Clean0;
}
//
// if it's an explicit phantom, skip it
//
ulLength = sizeof(ULONG);
RegStatus = RegQueryValueEx(
hKey, pszRegValuePhantom, NULL, NULL,
(LPBYTE)&ulValue, &ulLength);
if (RegStatus == ERROR_SUCCESS && ulValue == TRUE) {
goto Clean0;
}
//
// if configflags indicate it can't be started then skip it,
// otherwise mark device as present.
//
ulValue = GetCurrentConfigFlag(pszDeviceID);
if (ulValue & (CSCONFIGFLAG_DISABLED | CSCONFIGFLAG_DO_NOT_START)) {
goto Clean0;
}
ulValue = TRUE;
RegSetValueEx(
hKey, pszRegValueFoundAtEnum, 0, REG_DWORD,
(LPBYTE)&ulValue, sizeof(ULONG));
//
// query the name of the service that controls this device id
//
#if 0
ulLength = MAX_PATH * sizeof(WCHAR);
RegStatus = RegQueryValueEx(
hKey, pszRegValueService, NULL, NULL,
(LPBYTE)szService, &ulLength);
if (RegStatus != ERROR_SUCCESS) {
goto Clean0; // nothing to start
}
#endif
if (!GetActiveService(pszDeviceID, szService)) {
goto Clean0;
}
//
// Open a handle to the controlling service
//
hService = OpenService(
hSCManager, szService, SERVICE_QUERY_STATUS | SERVICE_START);
if (hService == NULL) {
goto Clean0; // nothing to start
}
//
// Is the service already running? We can't start any device ids if
// the service is already running.
//
if (!QueryServiceStatus(hService, &ServiceStatus)) {
goto Clean0; // nothing to start
}
if (ServiceStatus.dwCurrentState == SERVICE_RUNNING) {
goto Clean0; // already started, do nothing
}
//
// The service isn't running, register the new device with
// this service BEFORE starting the service
//
RtlInitUnicodeString(&ControlData.DeviceInstance, pszDeviceID);
NtStatus = NtPlugPlayControl(
PlugPlayControlRegisterNewDevice,
&ControlData,
sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA),
&ulLength);
if (NtStatus != STATUS_SUCCESS) {
Status = CR_FAILURE;
}
//
// attempt to start the service
//
if (!StartService(hService, 0, NULL)) {
//
// start failed, mark the problem
//
MarkDeviceProblem(hKey, pszDeviceID, CM_PROB_FAILED_START);
goto Clean0; // start failed.
}
CloseServiceHandle(hService);
hService = NULL;
//
// The service started, so mark the device as started
//
ulLength = sizeof(ULONG);
RegStatus = RegQueryValueEx(
hKey, pszRegValueStatusFlags, NULL, NULL,
(LPBYTE)&ulValue, &ulLength);
if (RegStatus != ERROR_SUCCESS) {
ulValue = 0;
}
SET_FLAG(ulValue, DN_STARTED);
RegSetValueEx(
hKey, pszRegValueStatusFlags, 0, REG_DWORD,
(LPBYTE)&ulValue, sizeof(ULONG));
//----------------------------------------------------------------
// Process the children of this device id
//----------------------------------------------------------------
//
// query the children attached to that device
//
RegStatus = RegQueryValueEx(
hKey, pszRegValueAttachedComponents, NULL, NULL,
NULL, &ulLength);
if (RegStatus != ERROR_SUCCESS || ulLength == 0) {
goto Clean0; // nothing to start, go to next device id
}
pChildList = malloc(ulLength);
if (pChildList == NULL) {
return CR_OUT_OF_MEMORY;
}
memset(pChildList, 0, ulLength);
RegStatus = RegQueryValueEx(
hKey, pszRegValueAttachedComponents, NULL, NULL,
(LPBYTE)pChildList, &ulLength);
RegCloseKey(hKey);
hKey = NULL;
//
// for each child, attempt to start that device id
//
for (pszChild = pChildList; *pszChild; pszChild += lstrlen(pszChild)+1) {
Status = StartDevice((LPCWSTR)pszChild, hSCManager);
if (Status != CR_SUCCESS) {
goto Clean0;
}
}
Clean0:
if (hKey != NULL) {
RegCloseKey(hKey);
}
if (pChildList != NULL) {
free(pChildList);
}
if (hService != NULL) {
CloseServiceHandle(hService);
}
return Status;
} // StartDevice
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// DEAD CODE, LEAVE TEMPORARILY FOR REFERENCE
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#endif
CONFIGRET
StartDeviceList(
IN LPCWSTR pszDeviceList,
IN LPCWSTR pszService,
SC_HANDLE hSCManager
)
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
HKEY hKey = NULL;
LPWSTR pszDevice = NULL;
ULONG ulLength = 0, ulValue = 0;
SC_HANDLE hService = NULL;
SERVICE_STATUS ServiceStatus;
QUERY_SERVICE_CONFIG Config;
NTSTATUS NtStatus = STATUS_SUCCESS;
PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA ControlData;
BOOL bStarted = FALSE, bManualStart = FALSE;
// NOTE, I'm assuming that none of these devices will have children
//-----------------------------------------------------------
// Can the service be started?
//-----------------------------------------------------------
//
// Open a handle to the controlling service
//
hService = OpenService(hSCManager, pszService,
SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS | SERVICE_START);
if (hService == NULL) {
goto Clean0; // nothing to start
}
//
// Is the service already running? We can't start any device ids if
// the service is already running.
//
if (!QueryServiceStatus(hService, &ServiceStatus)) {
goto Clean0; // nothing to start
}
if (ServiceStatus.dwCurrentState == SERVICE_RUNNING) {
goto Clean0; // already started, do nothing
}
// Review - PaulaT
//
// If service is manual-start, then do not attempt to start it
//
Config.dwStartType = 0;
QueryServiceConfig(hService, &Config, sizeof(QUERY_SERVICE_CONFIG),
&ulLength);
if (Config.dwStartType == SERVICE_DEMAND_START) {
bManualStart = TRUE;
}
//-----------------------------------------------------------
// Start any devices that can be started
//-----------------------------------------------------------
for (pszDevice = (LPWSTR)pszDeviceList;
*pszDevice;
pszDevice += lstrlen(pszDevice) + 1) {
if (StartDevice(pszDevice, pszService) == CR_SUCCESS) {
bStarted = TRUE;
}
}
//-----------------------------------------------------------
// Start the service
//-----------------------------------------------------------
//
// if any devices in the device list started (and it is not a
// manual start service), then start the service now
//
if (bStarted && !bManualStart) {
if (!StartService(hService, 0, NULL)) {
//
// start failed, mark the problem for each device
//
for (pszDevice = (LPWSTR)pszDeviceList;
*pszDevice;
pszDevice += lstrlen(pszDevice) + 1) {
//
// open a key to the device instance
//
RegStatus = RegOpenKeyEx(ghEnumKey, pszDevice, 0,
KEY_READ | KEY_SET_VALUE, &hKey);
if (RegStatus == ERROR_SUCCESS) {
MarkDeviceProblem(hKey, pszDevice, CM_PROB_FAILED_START);
ulValue = 0;
ulLength = sizeof(ULONG);
RegQueryValueEx(
hKey, pszRegValueStatusFlags, NULL, NULL,
(LPBYTE)&ulValue, &ulLength);
CLEAR_FLAG(ulValue, DN_STARTED);
RegSetValueEx(
hKey, pszRegValueStatusFlags, 0, REG_DWORD,
(LPBYTE)&ulValue, sizeof(ULONG));
RegCloseKey(hKey);
hKey = NULL;
}
}
}
}
Clean0:
if (hKey != NULL) {
RegCloseKey(hKey);
}
if (hService != NULL) {
CloseServiceHandle(hService);
}
return Status;
} // StartDeviceList
CONFIGRET
StartDevice(
IN LPCWSTR pszDevice,
IN LPCWSTR pszService
)
{
LONG RegStatus = ERROR_SUCCESS;
HKEY hKey = NULL;
ULONG ulLength = 0, ulValue = 0;
NTSTATUS NtStatus = STATUS_SUCCESS;
PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA ControlData;
//
// open a key to the device instance
//
if (RegOpenKeyEx(ghEnumKey, pszDevice, 0, KEY_READ | KEY_SET_VALUE,
&hKey) != ERROR_SUCCESS) {
return CR_NO_SUCH_DEVINST; // nothing to start
}
//
// if it's got a problem, skip it
// [alternatively could check status for DN_HAS_PROBLEM instead]
//
ulLength = sizeof(ULONG);
RegStatus = RegQueryValueEx(
hKey, pszRegValueProblem, NULL, NULL,
(LPBYTE)&ulValue, &ulLength);
if (RegStatus == ERROR_SUCCESS && ulValue != 0x0) {
RegCloseKey(hKey);
return CR_DEFAULT;
}
//
// if it's an explicit phantom, skip it
//
ulLength = sizeof(ULONG);
RegStatus = RegQueryValueEx(
hKey, pszRegValuePhantom, NULL, NULL,
(LPBYTE)&ulValue, &ulLength);
if (RegStatus == ERROR_SUCCESS && ulValue == TRUE) {
RegCloseKey(hKey);
return CR_DEFAULT;
}
//
// if special private status flag set, device is "not" present, so skip it
//
ulLength = sizeof(ULONG);
ulValue = 0;
if (RegQueryValueEx(hKey, pszRegValueStatusFlags, NULL, NULL,
(LPBYTE)&ulValue, &ulLength) == ERROR_SUCCESS) {
if (ulValue & DN_CSDISABLED) {
RegCloseKey(hKey);
return CR_DEFAULT;
}
}
//
// if configflags indicate it can't be started then skip it,
// otherwise mark device as present.
//
ulValue = GetCurrentConfigFlag(pszDevice);
if (ulValue & (CSCONFIGFLAG_DISABLED | CSCONFIGFLAG_DO_NOT_START)) {
RegCloseKey(hKey);
return CR_DEFAULT;
}
ulValue = TRUE;
RegSetValueEx(
hKey, pszRegValueFoundAtEnum, 0, REG_DWORD,
(LPBYTE)&ulValue, sizeof(ULONG));
//
// If a service was specified, register the device with this
// service (this MUST be done BEFORE starting the service). If
// no service specified (null service), then just skip this
// step.
//
if (pszService != NULL) {
RtlInitUnicodeString(&ControlData.DeviceInstance, pszDevice);
NtStatus = NtPlugPlayControl(
PlugPlayControlRegisterNewDevice,
&ControlData,
sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA),
&ulLength);
if (NtStatus != STATUS_SUCCESS) {
RegCloseKey(hKey);
return CR_DEFAULT;
}
}
#if 0
//
// Assume the service will start successfully and mark the
// device as started. If the service fails to start later,
// I'll go back and unmark these then.
//
ulLength = sizeof(ULONG);
RegStatus = RegQueryValueEx(
hKey, pszRegValueStatusFlags, NULL, NULL,
(LPBYTE)&ulValue, &ulLength);
if (RegStatus != ERROR_SUCCESS) {
ulValue = 0;
}
SET_FLAG(ulValue, DN_STARTED);
RegSetValueEx(
hKey, pszRegValueStatusFlags, 0, REG_DWORD,
(LPBYTE)&ulValue, sizeof(ULONG));
#endif
RegCloseKey(hKey);
return CR_SUCCESS;
} // StartDevice
CONFIGRET
StopService(
IN LPCWSTR pszService
)
// Common code for processing any stop of a service
{
CONFIGRET Status = CR_SUCCESS;
LPWSTR pDeviceList = NULL, pszDevice = NULL;
ULONG ulLength = 0;
//
// build up a multisz list of all the devices controlled by this service
//
if ((Status = GetServiceDeviceListSize(
pszService, &ulLength)) != CR_SUCCESS) {
goto Clean0;
}
pDeviceList = malloc(ulLength * sizeof(WCHAR));
if (pDeviceList == NULL) {
Status = CR_OUT_OF_MEMORY;
goto Clean0;
}
memset(pDeviceList, 0, ulLength * sizeof(WCHAR));
if ((Status = GetServiceDeviceList(pszService, pDeviceList, &ulLength,
CM_GETIDLIST_DONOTGENERATE)) != CR_SUCCESS) {
goto Clean0;
}
//----------------------------------------------------------
// process each device controlled by this service
//----------------------------------------------------------
for (pszDevice = pDeviceList; *pszDevice; pszDevice += lstrlen(pszDevice)+1) {
if ((Status = StopDevice(pszDevice)) != CR_SUCCESS) {
goto Clean0;
}
}
Clean0:
if (pDeviceList != NULL) {
free(pDeviceList);
}
return Status;
} // StopService
CONFIGRET
StopDevice(
IN LPCWSTR pszDeviceID
)
{
HKEY hKey = NULL;
ULONG ulLength = 0, ulValue = 0;
//
// open a registry key to the device id
//
if (RegOpenKeyEx(ghEnumKey, pszDeviceID, 0, KEY_READ | KEY_SET_VALUE,
&hKey) != ERROR_SUCCESS) {
return CR_REGISTRY_ERROR;
}
//
// set StatusFlags to not include DN_STARTED bit
//
ulLength = sizeof(ULONG);
if (RegQueryValueEx(
hKey, pszRegValueStatusFlags, NULL, NULL,
(LPBYTE)&ulValue, &ulLength) != ERROR_SUCCESS) {
ulValue = 0; // default value
}
CLEAR_FLAG(ulValue, DN_STARTED);
RegSetValueEx(
hKey, pszRegValueStatusFlags, 0, REG_DWORD,
(LPBYTE)&ulValue, sizeof(ULONG));
#if 0
//
// set ConfigFlags to include CONFIGFLAG_REMOVED
//
ulLength = sizeof(ULONG);
if (RegQueryValueEx(
hKey, pszRegValueConfigFlags, NULL, NULL,
(LPBYTE)&ulValue, &ulLength) != ERROR_SUCCESS) {
ulValue = 0; // default value
}
SET_FLAG(ulValue, CONFIGFLAG_REMOVED);
RegSetValueEx(
hKey, pszRegValueConfigFlags, 0, REG_DWORD,
(LPBYTE)&ulValue, sizeof(ULONG));
#endif
//
// Remove phantom status
//
RegDeleteValue(hKey, pszRegValuePhantom);
#if 0
//
// Set device instance to not present
//
ulValue = FALSE;
RegSetValueEx(
hKey, pszRegValueFoundAtEnum, 0, REG_DWORD,
(LPBYTE)&ulValue, sizeof(ULONG));
#endif
RegCloseKey(hKey);
return CR_SUCCESS;
} // StopDevice
CONFIGRET
QueryStopService(
IN SC_HANDLE hService,
IN LPCWSTR pszService
)
{
CONFIGRET Status = CR_SUCCESS;
ULONG ulLength = 0;
LPWSTR pszDevice = NULL, pDeviceList = NULL;
SERVICE_STATUS ServiceStatus;
//----------------------------------------------------------------
// Criteria 1: Can the service be stopped?
//----------------------------------------------------------------
//
// Query the status of this service
//
if (!QueryServiceStatus(hService, &ServiceStatus)) {
goto Clean0; // nothing to start
}
// REVIEW - PaulaT
//
// Current thinking (as of 5/30/96) is to not stop the service if it's
// already started, whether it's a service or a kernel-mode driver.
//
//
// If the service is not currently running, then criteria 1 passes
//
if (ServiceStatus.dwCurrentState != SERVICE_STOPPED &&
ServiceStatus.dwCurrentState != SERVICE_STOP_PENDING) {
//
// The service is running: fail the query remove (can't stop services
// for SUR, causes problems for apps with open handles to driver).
//
Status = CR_REMOVE_VETOED;
goto Clean0; // Criteria 1 failed
}
#if 0
//
// If the service is currently active (not stopped) and it doesn't
// accept being stopped, then I'll veto the query remove. If the
// service isn't running then I don't need to stop it so I can
// accept the query remove. Note, there may be a problem if the
// service starts just after I called QueryServiceStatus??
//
if (ServiceStatus.dwCurrentState != SERVICE_STOPPED &&
ServiceStatus.dwControlsAccepted != SERVICE_ACCEPT_STOP) {
Status = CR_REMOVE_VETOED;
goto Clean0;
}
#endif
//-------------------------------------------------------------
// Criteria 2: Can the devices be stopped?
// - only if none of them have children
// - only if all of them are marked as removable
//-------------------------------------------------------------
//
// build up a multisz list of all the devices controlled by this service
//
Status = GetServiceDeviceListSize(pszService, &ulLength);
if (Status != CR_SUCCESS) {
goto Clean0;
}
pDeviceList = malloc(ulLength * sizeof(WCHAR));
if (pDeviceList == NULL) {
Status = CR_OUT_OF_MEMORY;
goto Clean0;
}
memset(pDeviceList, 0, ulLength * sizeof(WCHAR));
Status = GetServiceDeviceList(pszService, pDeviceList, &ulLength,
CM_GETIDLIST_DONOTGENERATE);
if (Status != CR_SUCCESS) {
goto Clean0;
}
//
// walk through the list of controlled device ids, if any have children
// or any can't be stopped, then I can't stop the service
//
for (pszDevice = pDeviceList; *pszDevice; pszDevice += lstrlen(pszDevice)+1) {
if ((Status = QueryStopDevice(pszDevice)) != CR_SUCCESS) {
goto Clean0;
}
}
Clean0:
if (pDeviceList != NULL) {
free(pDeviceList);
}
return Status;
} // QueryStopService
CONFIGRET
QueryStopDevice(
IN LPCWSTR pszDeviceID
)
{
LONG RegStatus = ERROR_SUCCESS;
HKEY hKey = NULL;
ULONG ulLength = 0;
//
// open the registry enum key for this device id
//
if (RegOpenKeyEx(ghEnumKey, pszDeviceID, 0, KEY_QUERY_VALUE | KEY_SET_VALUE,
&hKey) != ERROR_SUCCESS) {
return CR_INVALID_DEVINST;
}
//
// Is this device ID stoppable?
//
#if 0
ulLength = sizeof(ULONG);
RegStatus = RegQueryValueEx(
hKey, pszRegValueStatusFlags, NULL, NULL,
(LPBYTE)&ulValue, &ulLength);
if (RegStatus != ERROR_SUCCESS || !(ulValue & DN_REMOVABLE)) {
RegCloseKey(hKey);
return CR_REMOVE_VETOED;
}
#endif
//
// Does this device ID have children? (SUR)
//
RegStatus = RegQueryValueEx(
hKey, pszRegValueAttachedComponents, NULL, NULL,
NULL, &ulLength);
if (RegStatus == ERROR_SUCCESS && ulLength > 0) {
RegCloseKey(hKey);
return CR_REMOVE_VETOED;
}
RegCloseKey(hKey);
return CR_SUCCESS;
} // QueryStopDevice
CONFIGRET
UninstallRealDevice(
IN LPCWSTR pszDeviceID
)
{
CONFIGRET Status = CR_SUCCESS;
WCHAR RegStr[MAX_CM_PATH];
ULONG ulCount = 0, ulProfile = 0;
HKEY hKey = NULL;
//---------------------------------------------------------------------
// This is the case where a real device id couldn't be stopped, so we
// cannot really safely delete the device id at this point since the
// service may still try to use it. Instead, I'll make this device
// id registry key volatile so that it will eventually go away when
// the system is shutdown. To make the key volatile, I have to copy
// it to a temporary spot, delete the original key and recreate it
// as a volatile key and copy everything back.
//---------------------------------------------------------------------
//
// first, convert the device instance key under the main Enum
// branch to volatile
//
Status = MakeKeyVolatile(pszRegPathEnum, pszDeviceID);
if (Status != CR_SUCCESS) {
goto Clean0;
}
//
// next, check each hardware profile and delete any entries for this
// device instance.
//
Status = GetProfileCount(&ulCount);
if (Status != CR_SUCCESS) {
goto Clean0;
}
for (ulProfile = 1; ulProfile <= ulCount; ulProfile++) {
wsprintf(RegStr, TEXT("%s\\%04u\\%s"),
pszRegPathHwProfiles,
ulProfile,
pszRegPathEnum);
//
// Ignore the status for profile-specific keys since they may
// not exist.
//
MakeKeyVolatile(RegStr, pszDeviceID);
}
//
// finally, mark the device as being removed
//
if (RegOpenKeyEx(ghEnumKey, pszDeviceID, 0, KEY_QUERY_VALUE | KEY_SET_VALUE,
&hKey) == ERROR_SUCCESS) {
ULONG ulValue = 0, ulLength = sizeof(ULONG);
if (RegQueryValueEx(hKey, pszRegValueStatusFlags, NULL, NULL,
(LPBYTE)&ulValue, &ulLength) != ERROR_SUCCESS) {
ulValue = 0;
}
ulValue |= DN_WILL_BE_REMOVED;
RegSetValueEx(hKey, pszRegValueStatusFlags, 0, REG_DWORD,
(LPBYTE)&ulValue, sizeof(ULONG));
RegCloseKey(hKey);
}
Clean0:
return Status;
} // UninstallRealDevice
BOOL
IsDeviceRegistered(
IN LPCWSTR pszDeviceID,
IN LPCWSTR pszService
)
{
WCHAR RegStr[MAX_PATH], szData[MAX_DEVICE_ID_LEN], szValue[MAX_PATH];
HKEY hKey = NULL;
LONG RegStatus = ERROR_SUCCESS;
ULONG ulIndex = 0, ulDataSize = 0, ulValueSize = 0, i = 0;
BOOL Status = FALSE;
//
// open the service's volatile enum registry key
//
wsprintf(RegStr, TEXT("%s\\%s"),
pszService,
pszRegKeyEnum);
if (RegOpenKeyEx(ghServicesKey, RegStr, 0, KEY_READ, &hKey)
== ERROR_SUCCESS) {
//
// Enumerate all the values under this key
//
while (RegStatus == ERROR_SUCCESS) {
ulDataSize = MAX_DEVICE_ID_LEN * sizeof(WCHAR);
ulValueSize = MAX_PATH * sizeof(WCHAR);
RegStatus = RegEnumValue(hKey, ulIndex, szValue, &ulValueSize,
NULL, &i, (LPBYTE)szData, &ulDataSize);
if (RegStatus == ERROR_SUCCESS) {
ulIndex++;
if (lstrcmpi(pszDeviceID, szData) == 0) {
Status = TRUE;
break;
}
}
}
RegCloseKey(hKey);
}
return Status;
} // IsDeviceRegistered