Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2273 lines
63 KiB

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
rtravers.c
Abstract:
This module contains the server-side hardware tree traversal APIs.
PNP_ValidateDeviceInstance
PNP_GetRootDeviceInstance
PNP_GetRelatedDeviceInstance
PNP_EnumerateSubKeys
PNP_GetDeviceList
PNP_GetDeviceListSize
Author:
Paula Tomlinson (paulat) 6-19-1995
Environment:
User-mode only.
Revision History:
19-June-1995 paulat
Creation and initial implementation.
--*/
//
// includes
//
#include "precomp.h"
#pragma hdrstop
#include "umpnpi.h"
#include "umpnpdat.h"
//
// private prototypes
//
CONFIGRET
GetInstanceListSize(
IN LPCWSTR pszDevice,
OUT PULONG pulLength
);
CONFIGRET
GetInstanceList(
IN LPCWSTR pszDevice,
IN OUT LPWSTR *pBuffer,
IN OUT PULONG pulLength
);
CONFIGRET
GetDeviceInstanceListSize(
IN LPCWSTR pszEnumerator,
OUT PULONG pulLength
);
CONFIGRET
GetDeviceInstanceList(
IN LPCWSTR pszEnumerator,
IN OUT LPWSTR *pBuffer,
IN OUT PULONG pulLength
);
PNP_QUERY_RELATION
QueryOperationCode(
ULONG ulFlags
);
//
// 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
extern HKEY ghClassKey; // Key to HKLM\CCC\System\Class - NO NOT MODIFY
CONFIGRET
PNP_ValidateDeviceInstance(
IN handle_t hBinding,
IN LPWSTR pDeviceID,
IN ULONG ulFlags
)
/*++
Routine Description:
This the server-side of an RPC remote call. This routine verifies whether
the specificed device instance is a valid device instance.
Arguments:
hBinding RPC binding handle.
DeviceInstance Null-terminated string that contains a device instance
to be validated.
ulFlags One of the CM_LOCATE_DEVNODE_* flags.
Return Value:
If the specified device instance is valid, it returns CR_SUCCESS,
otherwise it returns CR_ error code.
--*/
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
HKEY hKey = NULL, hKeyHwProfiles = NULL;
ULONG ulSize, ulValue, ulStatus = 0, ulProblem = 0;
//
// assume that the device instance string was checked for proper form
// before being added to the registry Enum tree
//
try {
//
// validate parameters
//
if (INVALID_FLAGS(ulFlags, CM_LOCATE_DEVNODE_BITS)) {
Status = CR_INVALID_FLAG;
goto Clean0;
}
//
// CM_LOCATE_DEVNODE_NOVALIDATION is not supported on NT
//
if (IS_FLAG_SET(ulFlags, CM_LOCATE_DEVNODE_NOVALIDATION)) {
Status = CR_INVALID_FLAG;
goto Clean0;
}
if (!IsLegalDeviceId(pDeviceID)) {
Status = CR_INVALID_DEVNODE;
goto Clean0;
}
//
// 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;
}
//
// Will specify for now that a moved devinst cannot be located (we
// could allow this if we wanted to).
//
if (IsDeviceMoved(pDeviceID, hKey)) {
Status = CR_NO_SUCH_DEVINST;
goto Clean0;
}
//
// if we're locating a phantom devnode, it just has to exist
// in the registry (the above check) and not already be a
// phantom (private) devnode
//
if (ulFlags & CM_LOCATE_DEVNODE_PHANTOM) {
//
// verify that it's not a private phantom
//
ulSize = sizeof(ULONG);
RegStatus = RegQueryValueEx(hKey, pszRegValuePhantom, NULL, NULL,
(LPBYTE)&ulValue, &ulSize);
if ((RegStatus == ERROR_SUCCESS) && ulValue) {
Status = CR_NO_SUCH_DEVINST;
goto Clean0;
}
} else if (ulFlags & CM_LOCATE_DEVNODE_CANCELREMOVE) {
//
// In the CANCEL-REMOVE case, if the devnode has been removed,
// (made volatile) then convert it back to nonvolatile so it
// can be installed again without disappearing on the next
// boot. If it's not removed, then just verify that it is
// present.
//
//
// verify that the device id is actually present
//
if (!IsDeviceIdPresent(pDeviceID)) {
Status = CR_NO_SUCH_DEVINST;
goto Clean0;
}
//
// Is this a device that is being removed on the next reboot?
//
if (GetDeviceStatus(pDeviceID, &ulStatus, &ulProblem) == CR_SUCCESS) {
if (ulStatus & DN_WILL_BE_REMOVED) {
ULONG ulIndex = 0, ulLen = 0;
WCHAR RegStr[MAX_CM_PATH];
WCHAR szProfile[MAX_PROFILE_ID_LEN];
//
// Verify client "write" access
//
if (!VerifyClientAccess(hBinding,
PLUGPLAY_WRITE)) {
Status = CR_ACCESS_DENIED;
goto Clean0;
}
//
// This device will be removed on the next reboot,
// convert to nonvolatile.
//
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_REGISTRY,
"UMPNPMGR: PNP_ValidateDeviceInstance make key %ws non-volatile\n",
pDeviceID));
Status = MakeKeyNonVolatile(pszRegPathEnum, pDeviceID);
if (Status != CR_SUCCESS) {
goto Clean0;
}
//
// Now make any keys that were "supposed" to be volatile
// back to volatile again!
//
if (SUCCEEDED(StringCchPrintf(
RegStr,
SIZECHARS(RegStr),
L"%s\\%s",
pszRegPathEnum,
pDeviceID))) {
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_REGISTRY,
"UMPNPMGR: PNP_ValidateDeviceInstance make key %ws\\%ws volatile\n",
RegStr,
pszRegKeyDeviceControl));
MakeKeyVolatile(RegStr, pszRegKeyDeviceControl);
}
//
// Also, convert any profile specific keys to nonvolatile
//
RegStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, pszRegPathHwProfiles,
0, KEY_READ,
&hKeyHwProfiles);
if (RegStatus == ERROR_SUCCESS) {
//
// Enumerate all existing profile-specific Enum branches.
//
ulIndex = 0;
for ( ; ; ) {
ulLen = SIZECHARS(szProfile);
RegStatus = RegEnumKeyEx(hKeyHwProfiles, ulIndex++,
szProfile, &ulLen,
NULL, NULL, NULL, NULL);
if (RegStatus == ERROR_NO_MORE_ITEMS) {
//
// No more keys to enumerate, stop enumerating.
//
break;
} else if (RegStatus == ERROR_MORE_DATA) {
//
// Key is not a valid profile key, skip to the next.
//
continue;
} else if (RegStatus != ERROR_SUCCESS) {
//
// Some other error, stop enumerating.
//
break;
} else {
//
// Form the profile-specific registry key path.
//
if (SUCCEEDED(StringCchPrintf(
RegStr,
SIZECHARS(RegStr),
L"%s\\%s\\%s",
pszRegPathHwProfiles,
szProfile,
pszRegPathEnum))) {
//
// Attempt to make the profile-specific
// device instance key volatile. Ignore the
// status for profile-specific keys since
// they may not exist.
//
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_REGISTRY,
"UMPNPMGR: PNP_ValidateDeviceInstance make key %ws non-volatile\n",
pDeviceID));
MakeKeyNonVolatile(RegStr, pDeviceID);
}
}
}
RegCloseKey(hKeyHwProfiles);
}
//
// clear the DN_WILL_BE_REMOVED flag
//
ClearDeviceStatus(pDeviceID, DN_WILL_BE_REMOVED, 0);
}
}
}
//
// in the normal (non-phantom case), verify that the device id is
// actually present
//
else {
//
// verify that the device id is actually present
//
if (!IsDeviceIdPresent(pDeviceID)) {
Status = CR_NO_SUCH_DEVINST;
goto Clean0;
}
}
Clean0:
NOTHING;
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
return Status;
} // PNP_ValidateDeviceInstance
CONFIGRET
PNP_GetRootDeviceInstance(
IN handle_t hBinding,
OUT LPWSTR pDeviceID,
IN ULONG ulLength
)
/*++
Routine Description:
This the server-side of an RPC remote call. This routine returns the
root device instance for the hardware tree.
Arguments:
hBinding Not used.
pDeviceID Pointer to a buffer that will hold the root device
instance ID string.
ulLength Size of pDeviceID buffer in characters.
Return Value:
If the function succeeds, it returns CR_SUCCESS, otherwise it returns
a CR_* error code.
--*/
{
CONFIGRET Status = CR_SUCCESS;
HKEY hKey = NULL;
HRESULT hr;
size_t DeviceIdLen = 0;
UNREFERENCED_PARAMETER(hBinding);
try {
//
// first validate that the root device instance exists
//
if (RegOpenKeyEx(ghEnumKey, pszRegRootEnumerator, 0, KEY_QUERY_VALUE,
&hKey) != ERROR_SUCCESS) {
//
// root doesn't exist, create root devinst
//
if (!CreateDeviceIDRegKey(ghEnumKey, pszRegRootEnumerator)) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
}
//
// return the root device instance id
//
hr = StringCchLength(pszRegRootEnumerator,
MAX_DEVICE_ID_LEN,
&DeviceIdLen);
ASSERT(SUCCEEDED(hr));
if (FAILED(hr)) {
Status = CR_FAILURE;
goto Clean0;
}
if (ulLength < (ULONG)(DeviceIdLen + 1)) {
Status = CR_BUFFER_SMALL;
goto Clean0;
}
hr = StringCchCopyEx(pDeviceID,
ulLength,
pszRegRootEnumerator,
NULL, NULL,
STRSAFE_IGNORE_NULLS);
if (HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER) {
Status = CR_BUFFER_SMALL;
} else if (FAILED(hr)) {
Status = CR_FAILURE;
}
Clean0:
NOTHING;
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
return Status;
} // PNP_GetRootDeviceInstance
CONFIGRET
PNP_GetRelatedDeviceInstance(
IN handle_t hBinding,
IN ULONG ulRelationship,
IN LPWSTR pDeviceID,
OUT LPWSTR pRelatedDeviceID,
IN OUT PULONG pulLength,
IN ULONG ulFlags
)
/*++
Routine Description:
This the server-side of an RPC remote call. This routine returns a
device instance that is related to the specified device instance.
Arguments:
hBinding Not used.
ulRelationship Specifies the relationship of the device instance to
be retrieved (can be PNP_GET_PARENT_DEVICE_INSTANCE,
PNP_GET_CHILD_DEVICE_INSTANCE, or
PNP_GET_SIBLING_DEVICE_INSTANCE).
pDeviceID Pointer to a buffer that contains the base device
instance string.
pRelatedDeviceID Pointer to a buffer that will receive the related
device instance string.
pulLength Length (in characters) of the RelatedDeviceInstance
buffer.
ulFlags Not used, must be zero.
Return Value:
If the function succeeds, it returns CR_SUCCESS, otherwise it returns
a CR_* error code.
--*/
{
PLUGPLAY_CONTROL_RELATED_DEVICE_DATA ControlData;
CONFIGRET Status = CR_SUCCESS;
NTSTATUS ntStatus;
HRESULT hr;
size_t DeviceIdLen;
UNREFERENCED_PARAMETER(hBinding);
try {
//
// validate patameters
//
if (INVALID_FLAGS(ulFlags, 0)) {
Status = CR_INVALID_FLAG;
goto Clean0;
}
if ((!ARGUMENT_PRESENT(pulLength)) ||
(!ARGUMENT_PRESENT(pRelatedDeviceID) && (*pulLength != 0))) {
Status = CR_INVALID_POINTER;
goto Clean0;
}
if (*pulLength > 0) {
*pRelatedDeviceID = L'\0';
}
if (!IsLegalDeviceId(pDeviceID)) {
Status = CR_INVALID_DEVNODE;
if (*pulLength > 0) {
*pulLength = 1;
}
goto Clean0;
}
//
// initialize control data block
//
memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_RELATED_DEVICE_DATA));
//
// special case behavior for certain devices and relationships
//
switch (ulRelationship) {
case PNP_GET_PARENT_DEVICE_INSTANCE:
if (IsRootDeviceID(pDeviceID)) {
//
// This is the root (which has no parent by definition)
//
Status = CR_NO_SUCH_DEVINST;
} else if (IsDevicePhantom(pDeviceID)) {
//
// Phantom devices don't have a kernel-mode device node
// allocated yet, but during manual install, the process calls
// for retrieving the parent. So we just fake it out by
// returning the root in this case. For all other cases, we only
// return the parent that the kernel-mode device node indicates.
//
hr = StringCchCopyEx(pRelatedDeviceID,
*pulLength,
pszRegRootEnumerator,
NULL, NULL,
STRSAFE_IGNORE_NULLS);
if (HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER) {
Status = CR_BUFFER_SMALL;
} else if (FAILED(hr)) {
Status = CR_FAILURE;
goto Clean0;
}
//
// For either CR_SUCCESS or CR_BUFFER_SMALL, we return the
// length of the root device id as either the amount of data
// copied, or the size required.
//
DeviceIdLen = 0;
hr = StringCchLength(pszRegRootEnumerator,
MAX_DEVICE_ID_LEN,
&DeviceIdLen);
ASSERT(SUCCEEDED(hr));
if (FAILED(hr)) {
Status = CR_FAILURE;
goto Clean0;
}
ASSERT(DeviceIdLen > 0);
*pulLength = (ULONG)(DeviceIdLen + 1);
goto Clean0;
}
//
// For all other devnodes, ask the kernel-mode pnp manager for the
// parent device.
//
ControlData.Relation = PNP_RELATION_PARENT;
break;
case PNP_GET_CHILD_DEVICE_INSTANCE:
ControlData.Relation = PNP_RELATION_CHILD;
break;
case PNP_GET_SIBLING_DEVICE_INSTANCE:
//
// first verify it isn't the root (which has no siblings by definition)
//
if (IsRootDeviceID(pDeviceID)) {
Status = CR_NO_SUCH_DEVINST;
}
ControlData.Relation = PNP_RELATION_SIBLING;
break;
default:
Status = CR_FAILURE;
}
if (Status == CR_SUCCESS) {
//
// Try to locate the relation from the kernel-mode in-memory
// devnode tree.
//
RtlInitUnicodeString(&ControlData.TargetDeviceInstance, pDeviceID);
ControlData.RelatedDeviceInstance = pRelatedDeviceID;
ControlData.RelatedDeviceInstanceLength = *pulLength;
ntStatus = NtPlugPlayControl(PlugPlayControlGetRelatedDevice,
&ControlData,
sizeof(ControlData));
if (NT_SUCCESS(ntStatus)) {
*pulLength = ControlData.RelatedDeviceInstanceLength + 1;
} else {
Status = MapNtStatusToCmError(ntStatus);
}
} else if (*pulLength > 0) {
*pulLength = 1;
}
Clean0:
NOTHING;
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
return Status;
} // PNP_GetRelatedDeviceInstance
CONFIGRET
PNP_EnumerateSubKeys(
IN handle_t hBinding,
IN ULONG ulBranch,
IN ULONG ulIndex,
OUT PWSTR Buffer,
IN ULONG ulLength,
OUT PULONG pulRequiredLen,
IN ULONG ulFlags
)
/*++
Routine Description:
This is the RPC server entry point for the CM_Enumerate_Enumerators and
CM_Enumerate_Classes. It provides generic subkey enumeration based on
the specified registry branch.
Arguments:
hBinding Not used.
ulBranch Specifies which keys to enumerate.
ulIndex Index of the subkey key to retrieve.
Buffer Supplies the address of the buffer that receives the
subkey name.
ulLength Specifies the max size of the Buffer in characters.
pulRequired On output it contains the number of characters actually
copied to Buffer if it was successful, or the number of
characters required if the buffer was too small.
ulFlags Not used, must be zero.
Return Value:
If the function succeeds, it returns CR_SUCCESS, otherwise it returns
a CR_* error code.
--*/
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
HKEY hKey = NULL;
UNREFERENCED_PARAMETER(hBinding);
try {
//
// validate parameters
//
if (INVALID_FLAGS(ulFlags, 0)) {
Status = CR_INVALID_FLAG;
goto Clean0;
}
if ((!ARGUMENT_PRESENT(pulRequiredLen)) ||
(!ARGUMENT_PRESENT(Buffer) && (ulLength != 0))) {
Status = CR_INVALID_POINTER;
goto Clean0;
}
if (ulLength > 0) {
*Buffer = L'\0';
}
if (ulBranch == PNP_CLASS_SUBKEYS) {
//
// Use the global base CLASS registry key
//
hKey = ghClassKey;
}
else if (ulBranch == PNP_ENUMERATOR_SUBKEYS) {
//
// Use the global base ENUM registry key
//
hKey = ghEnumKey;
}
else {
Status = CR_FAILURE;
goto Clean0;
}
//
// enumerate a subkey based on the passed in index value
//
*pulRequiredLen = ulLength;
RegStatus = RegEnumKeyEx(hKey, ulIndex, Buffer, pulRequiredLen,
NULL, NULL, NULL, NULL);
*pulRequiredLen += 1; // returned count doesn't include null terminator
if (RegStatus == ERROR_MORE_DATA) {
//
// This is a special case, the RegEnumKeyEx routine doesn't return
// the number of characters required to hold this string (just how
// many characters were copied to the buffer (how many fit). I have
// to use a different means to return that info back to the caller.
//
ULONG ulMaxLen = 0;
PWSTR p = NULL;
if (RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, &ulMaxLen,
NULL, NULL, NULL, NULL, NULL,
NULL) == ERROR_SUCCESS) {
ulMaxLen += 1; // returned count doesn't include null terminator
p = HeapAlloc(ghPnPHeap, 0, ulMaxLen * sizeof(WCHAR));
if (p == NULL) {
Status = CR_OUT_OF_MEMORY;
goto Clean0;
}
if (RegEnumKeyEx(hKey, ulIndex, p, &ulMaxLen, NULL, NULL, NULL,
NULL) == ERROR_SUCCESS) {
*pulRequiredLen = ulMaxLen + 1;
}
HeapFree(ghPnPHeap, 0, p);
}
Status = CR_BUFFER_SMALL;
goto Clean0;
}
else if (RegStatus == ERROR_NO_MORE_ITEMS) {
*pulRequiredLen = 0;
Status = CR_NO_SUCH_VALUE;
goto Clean0;
}
else if (RegStatus != ERROR_SUCCESS) {
*pulRequiredLen = 0;
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
Clean0:
NOTHING;
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
return Status;
} // PNP_EnumerateSubKeys
CONFIGRET
PNP_GetDeviceList(
IN handle_t hBinding,
IN LPCWSTR pszFilter,
OUT LPWSTR Buffer,
IN OUT PULONG pulLength,
IN ULONG ulFlags
)
/*++
Routine Description:
This the server-side of an RPC remote call. This routine returns a
list of device instances.
Arguments:
hBinding Not used.
pszFilter Optional parameter, controls which device ids are
returned.
Buffer Pointer to a buffer that will contain the multi_sz list
of device instance strings.
pulLength Size in characters of Buffer on input, size (in chars)
transferred on output
ulFlags Flag specifying which devices ids to return.
Return Value:
If the function succeeds, it returns CR_SUCCESS, otherwise it returns
a CR_* error code.
--*/
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
ULONG ulBufferLen=0, ulSize=0, ulIndex=0, ulLen=0;
WCHAR RegStr[MAX_CM_PATH];
WCHAR szEnumerator[MAX_DEVICE_ID_LEN],
szDevice[MAX_DEVICE_ID_LEN],
szInstance[MAX_DEVICE_ID_LEN];
LPWSTR ptr = NULL;
NTSTATUS ntStatus = STATUS_SUCCESS;
PLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA ControlData;
UNREFERENCED_PARAMETER(hBinding);
try {
//
// validate parameters
//
if (INVALID_FLAGS(ulFlags, CM_GETIDLIST_FILTER_BITS)) {
Status = CR_INVALID_FLAG;
goto Clean0;
}
if ((!ARGUMENT_PRESENT(pulLength)) ||
(!ARGUMENT_PRESENT(Buffer) && (*pulLength != 0))) {
Status = CR_INVALID_POINTER;
goto Clean0;
}
if (*pulLength > 0) {
*Buffer = L'\0';
}
//-----------------------------------------------------------
// Query Device Relations filter - go through kernel-mode
//-----------------------------------------------------------
if ((ulFlags & CM_GETIDLIST_FILTER_EJECTRELATIONS) ||
(ulFlags & CM_GETIDLIST_FILTER_REMOVALRELATIONS) ||
(ulFlags & CM_GETIDLIST_FILTER_POWERRELATIONS) ||
(ulFlags & CM_GETIDLIST_FILTER_BUSRELATIONS)) {
memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_RELATED_DEVICE_DATA));
RtlInitUnicodeString(&ControlData.DeviceInstance, pszFilter);
ControlData.Operation = QueryOperationCode(ulFlags);
ControlData.BufferLength = *pulLength;
ControlData.Buffer = Buffer;
ntStatus = NtPlugPlayControl(PlugPlayControlQueryDeviceRelations,
&ControlData,
sizeof(ControlData));
if (NT_SUCCESS(ntStatus)) {
*pulLength = ControlData.BufferLength;
} else if (ntStatus == STATUS_BUFFER_TOO_SMALL) {
*pulLength = 0;
Status = MapNtStatusToCmError(ntStatus);
}
goto Clean0;
}
//---------------------------------------------------
// Service filter
//---------------------------------------------------
else if (ulFlags & CM_GETIDLIST_FILTER_SERVICE) {
if (!ARGUMENT_PRESENT(pszFilter)) {
//
// the filter string is required for this flag
//
Status = CR_INVALID_POINTER;
goto Clean0;
}
Status = GetServiceDeviceList(pszFilter, Buffer, pulLength, ulFlags);
goto Clean0;
}
//---------------------------------------------------
// Enumerator filter
//---------------------------------------------------
else if (ulFlags & CM_GETIDLIST_FILTER_ENUMERATOR) {
if (!ARGUMENT_PRESENT(pszFilter)) {
//
// the filter string is required for this flag
//
Status = CR_INVALID_POINTER;
goto Clean0;
}
SplitDeviceInstanceString(
pszFilter, szEnumerator, szDevice, szInstance);
//
// if both the enumerator and device were specified, retrieve
// the device instances for this device
//
if (*szEnumerator != L'\0' && *szDevice != L'\0') {
ptr = Buffer;
Status = GetInstanceList(pszFilter, &ptr, pulLength);
}
//
// if just the enumerator was specified, retrieve all the device
// instances under this enumerator
//
else {
ptr = Buffer;
Status = GetDeviceInstanceList(pszFilter, &ptr, pulLength);
}
}
//------------------------------------------------
// No filtering
//-----------------------------------------------
else {
//
// return device instances for all enumerators (by enumerating
// the enumerators)
//
// Open a key to the Enum branch
//
ulSize = ulBufferLen = *pulLength; // total Buffer size
*pulLength = 0; // nothing copied yet
ptr = Buffer; // tail of the buffer
ulIndex = 0;
//
// Enumerate all the enumerators
//
while (RegStatus == ERROR_SUCCESS) {
ulLen = MAX_DEVICE_ID_LEN; // size in chars
RegStatus = RegEnumKeyEx(ghEnumKey, ulIndex, RegStr, &ulLen,
NULL, NULL, NULL, NULL);
ulIndex++;
if (RegStatus == ERROR_SUCCESS) {
Status = GetDeviceInstanceList(RegStr, &ptr, &ulSize);
if (Status != CR_SUCCESS) {
*pulLength = 0;
goto Clean0;
}
*pulLength += ulSize - 1; // length copied so far
ulSize = ulBufferLen - *pulLength; // buffer length left
}
}
*pulLength += 1; // now count the double-null
}
Clean0:
NOTHING;
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_SUCCESS;
}
return Status;
} // PNP_GetDeviceList
CONFIGRET
PNP_GetDeviceListSize(
IN handle_t hBinding,
IN LPCWSTR pszFilter,
OUT PULONG pulLen,
IN ULONG ulFlags
)
/*++
Routine Description:
This the server-side of an RPC remote call. This routine returns the
size of a list of device instances.
Arguments:
hBinding Not used.
pszEnumerator Optional parameter, if specified the size will only
include device instances of this enumerator.
pulLen Returns the worst case estimate of the size of a
device instance list.
ulFlags Flag specifying which devices ids to return.
Return Value:
If the function succeeds, it returns CR_SUCCESS, otherwise it returns
a CR_* error code.
--*/
{
CONFIGRET Status = CR_SUCCESS;
ULONG ulSize = 0, ulIndex = 0;
WCHAR RegStr[MAX_CM_PATH];
ULONG RegStatus = ERROR_SUCCESS;
WCHAR szEnumerator[MAX_DEVICE_ID_LEN],
szDevice[MAX_DEVICE_ID_LEN],
szInstance[MAX_DEVICE_ID_LEN];
NTSTATUS ntStatus = STATUS_SUCCESS;
PLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA ControlData;
UNREFERENCED_PARAMETER(hBinding);
try {
//
// validate parameters
//
if (INVALID_FLAGS(ulFlags, CM_GETIDLIST_FILTER_BITS)) {
Status = CR_INVALID_FLAG;
goto Clean0;
}
if (!ARGUMENT_PRESENT(pulLen)) {
Status = CR_INVALID_POINTER;
goto Clean0;
}
//
// initialize output length param
//
*pulLen = 0;
//-----------------------------------------------------------
// Query Device Relations filter - go through kernel-mode
//-----------------------------------------------------------
if ((ulFlags & CM_GETIDLIST_FILTER_EJECTRELATIONS) ||
(ulFlags & CM_GETIDLIST_FILTER_REMOVALRELATIONS) ||
(ulFlags & CM_GETIDLIST_FILTER_POWERRELATIONS) ||
(ulFlags & CM_GETIDLIST_FILTER_BUSRELATIONS)) {
memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA));
RtlInitUnicodeString(&ControlData.DeviceInstance, pszFilter);
ControlData.Operation = QueryOperationCode(ulFlags);
ControlData.BufferLength = 0;
ControlData.Buffer = NULL;
ntStatus = NtPlugPlayControl(PlugPlayControlQueryDeviceRelations,
&ControlData,
sizeof(ControlData));
if (NT_SUCCESS(ntStatus)) {
//
// Note - we get here because kernel mode special cases
// Buffer==NULL and is careful not to return
// STATUS_BUFFER_TOO_SMALL.
//
*pulLen = ControlData.BufferLength;
} else {
//
// ADRIAO ISSUE 02/06/2001 - We aren't returning the proper code
// here. We should fix this in XP+1,
// once we have time to verify no one
// will get an app compat break.
//
//Status = MapNtStatusToCmError(ntStatus);
Status = CR_SUCCESS;
}
goto Clean0;
}
//---------------------------------------------------
// Service filter
//---------------------------------------------------
else if (ulFlags & CM_GETIDLIST_FILTER_SERVICE) {
if (!ARGUMENT_PRESENT(pszFilter)) {
//
// the filter string is required for this flag
//
Status = CR_INVALID_POINTER;
goto Clean0;
}
Status = GetServiceDeviceListSize(pszFilter, pulLen);
goto Clean0;
}
//---------------------------------------------------
// Enumerator filter
//---------------------------------------------------
else if (ulFlags & CM_GETIDLIST_FILTER_ENUMERATOR) {
if (!ARGUMENT_PRESENT(pszFilter)) {
//
// the filter string is required for this flag
//
Status = CR_INVALID_POINTER;
goto Clean0;
}
SplitDeviceInstanceString(
pszFilter, szEnumerator, szDevice, szInstance);
//
// if both the enumerator and device were specified, retrieve
// the device instance list size for this device only
//
if (*szEnumerator != L'\0' && *szDevice != L'\0') {
Status = GetInstanceListSize(pszFilter, pulLen);
}
//
// if just the enumerator was specified, retrieve the size of
// all the device instances under this enumerator
//
else {
Status = GetDeviceInstanceListSize(pszFilter, pulLen);
}
}
//---------------------------------------------------
// No filtering
//---------------------------------------------------
else {
//
// no enumerator was specified, return device instance size
// for all enumerators (by enumerating the enumerators)
//
ulIndex = 0;
while (RegStatus == ERROR_SUCCESS) {
ulSize = MAX_DEVICE_ID_LEN; // size in chars
RegStatus = RegEnumKeyEx(ghEnumKey, ulIndex, RegStr, &ulSize,
NULL, NULL, NULL, NULL);
ulIndex++;
if (RegStatus == ERROR_SUCCESS) {
Status = GetDeviceInstanceListSize(RegStr, &ulSize);
if (Status != CR_SUCCESS) {
goto Clean0;
}
*pulLen += ulSize;
}
}
}
*pulLen += 1; // add extra char for double null term
Clean0:
NOTHING;
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
return Status;
} // PNP_GetDeviceListSize
CONFIGRET
PNP_GetDepth(
IN handle_t hBinding,
IN LPCWSTR pszDeviceID,
OUT PULONG pulDepth,
IN ULONG ulFlags
)
/*++
Routine Description:
This the server-side of an RPC remote call. This routine returns the
depth of a device instance.
Arguments:
hBinding Not used.
pszDeviceID Device instance to find the depth of.
pulDepth Returns the depth of pszDeviceID.
ulFlags Not used, must be zero.
Return Value:
If the function succeeds, it returns CR_SUCCESS, otherwise it returns
a CR_* error code.
--*/
{
CONFIGRET Status = CR_SUCCESS;
NTSTATUS ntStatus = STATUS_SUCCESS;
PLUGPLAY_CONTROL_DEPTH_DATA ControlData;
UNREFERENCED_PARAMETER(hBinding);
try {
//
// validate parameters
//
if (INVALID_FLAGS(ulFlags, 0)) {
Status = CR_INVALID_FLAG;
goto Clean0;
}
if (!ARGUMENT_PRESENT(pulDepth)) {
Status = CR_INVALID_POINTER;
goto Clean0;
}
//
// initialize output depth param
//
*pulDepth = 0;
if (!IsLegalDeviceId(pszDeviceID)) {
Status = CR_INVALID_DEVNODE;
goto Clean0;
}
//
// Retrieve the device depth via kernel-mode.
//
memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_DEPTH_DATA));
RtlInitUnicodeString(&ControlData.DeviceInstance, pszDeviceID);
ControlData.DeviceDepth = 0;
ntStatus = NtPlugPlayControl(PlugPlayControlGetDeviceDepth,
&ControlData,
sizeof(ControlData));
if (!NT_SUCCESS(ntStatus)) {
Status = MapNtStatusToCmError(ntStatus);
} else {
*pulDepth = ControlData.DeviceDepth;
}
Clean0:
NOTHING;
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
return Status;
} // PNP_GetDepth
//-------------------------------------------------------------------
// Private functions
//-------------------------------------------------------------------
CONFIGRET
GetServiceDeviceListSize(
IN LPCWSTR pszService,
OUT PULONG pulLength
)
/*++
Routine Description:
This routine returns the a list of device instances for the specificed
enumerator.
Arguments:
pszService service whose device instances are to be listed
pulLength On output, specifies the size in characters required to hold
the device instance list.
Return Value:
If the function succeeds, it returns CR_SUCCESS, otherwise it returns
a CR_* error code.
--*/
{
CONFIGRET Status = CR_SUCCESS;
ULONG ulType = 0, ulCount = 0, ulMaxValueData = 0, ulSize = 0;
HKEY hKey = NULL, hEnumKey = NULL;
try {
//
// validate parameters
//
if ((!ARGUMENT_PRESENT(pszService)) ||
(!ARGUMENT_PRESENT(pulLength))) {
Status = CR_INVALID_POINTER;
}
//
// Open a key to the service branch
//
if (RegOpenKeyEx(ghServicesKey, pszService, 0, KEY_READ,
&hKey) != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
//
// check if the service is specialy marked as type
// PlugPlayServiceSoftware, in which case I will not
// generate any madeup device ids and fail the call.
//
ulSize = sizeof(ulType);
if (RegQueryValueEx(hKey, pszRegValuePlugPlayServiceType, NULL, NULL,
(LPBYTE)&ulType, &ulSize) == ERROR_SUCCESS) {
if (ulType == PlugPlayServiceSoftware) {
Status = CR_NO_SUCH_VALUE;
*pulLength = 0;
goto Clean0;
}
}
//
// open the Enum key
//
if (RegOpenKeyEx(hKey, pszRegKeyEnum, 0, KEY_READ,
&hEnumKey) != ERROR_SUCCESS) {
//
// Enum key doesn't exist so one will be generated, estimate
// worst case device id size for the single generated device id
//
*pulLength = MAX_DEVICE_ID_LEN;
goto Clean0;
}
//
// retrieve the count of device instances controlled by this service
//
ulSize = sizeof(ulCount);
if (RegQueryValueEx(hEnumKey, pszRegValueCount, NULL, NULL,
(LPBYTE)&ulCount, &ulSize) != ERROR_SUCCESS) {
ulCount = 1; // if empty, I'll generate one
}
if (ulCount == 0) {
ulCount++; // if empty, I'll generate one
}
if (RegQueryInfoKey(hEnumKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, &ulMaxValueData, NULL, NULL) != ERROR_SUCCESS) {
*pulLength = ulCount * MAX_DEVICE_ID_LEN;
goto Clean0;
}
//
// worst case estimate is multiply number of device instances time
// length of the longest one + 2 null terminators
//
*pulLength = ulCount * (ulMaxValueData+1)/sizeof(WCHAR) + 2;
Clean0:
NOTHING;
} except (EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hEnumKey != NULL) {
RegCloseKey(hEnumKey);
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
return Status;
} // GetServiceDeviceListSize
CONFIGRET
GetServiceDeviceList(
IN LPCWSTR pszService,
OUT LPWSTR pBuffer,
IN OUT PULONG pulLength,
IN ULONG ulFlags
)
/*++
Routine Description:
This routine returns the a list of device instances for the specificed
enumerator.
Arguments:
pszService Service whose device instances are to be listed
pBuffer Pointer to a buffer that will hold the list in multi-sz
format
pulLength On input, specifies the size in characters of Buffer, on
Output, specifies the size in characters actually copied
to the buffer.
ulFlags Specifies CM_GETIDLIST_* flags supplied to
PNP_GetDeviceList (CM_GETIDLIST_FILTER_SERVICE
must be specified). This routine only checks for the
presence of the CM_GETIDLIST_DONOTGENERATE flag.
Return Value:
If the function succeeds, it returns CR_SUCCESS, otherwise it returns
a CR_* error code.
--*/
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
WCHAR RegStr[MAX_CM_PATH], szDeviceID[MAX_DEVICE_ID_LEN+1];
ULONG ulType=0, ulBufferLen=0, ulSize=0, ulCount=0, i=0;
HKEY hKey = NULL, hEnumKey = NULL;
PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA ControlData;
NTSTATUS NtStatus = STATUS_SUCCESS;
BOOL ServiceIsPlugPlay = FALSE;
ASSERT(ulFlags & CM_GETIDLIST_FILTER_SERVICE);
try {
//
// validate parameters
//
if ((!ARGUMENT_PRESENT(pszService)) ||
(!ARGUMENT_PRESENT(pulLength)) ||
(!ARGUMENT_PRESENT(pBuffer) && (*pulLength != 0))) {
Status = CR_INVALID_POINTER;
goto Clean0;
}
//
// the buffer must be at least large enough for a NULL multi-sz list
//
if (*pulLength == 0) {
Status = CR_BUFFER_SMALL;
goto Clean0;
}
*pBuffer = L'\0';
ulBufferLen = *pulLength;
//
// Open a key to the service branch
//
if (RegOpenKeyEx(ghServicesKey, pszService, 0, KEY_READ,
&hKey) != ERROR_SUCCESS) {
*pulLength = 0;
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
//
// check if the service is specialy marked as type
// PlugPlayServiceSoftware, in which case I will not
// generate any madeup device ids and fail the call.
//
ulSize = sizeof(ulType);
if (RegQueryValueEx(hKey, pszRegValuePlugPlayServiceType, NULL, NULL,
(LPBYTE)&ulType, &ulSize) == ERROR_SUCCESS) {
if (ulType == PlugPlayServiceSoftware) {
//
// for PlugPlayServiceSoftware value, fail the call
//
*pulLength = 0;
Status = CR_NO_SUCH_VALUE;
goto Clean0;
}
ServiceIsPlugPlay = TRUE;
}
//
// open the Enum key
//
RegStatus = RegOpenKeyEx(hKey, pszRegKeyEnum, 0, KEY_READ,
&hEnumKey);
if (RegStatus == ERROR_SUCCESS) {
//
// retrieve count of device instances controlled by this service
//
ulSize = sizeof(ulCount);
if (RegQueryValueEx(hEnumKey, pszRegValueCount, NULL, NULL,
(LPBYTE)&ulCount, &ulSize) != ERROR_SUCCESS) {
ulCount = 0;
}
}
//
// if there are no device instances, create a default one
//
if (RegStatus != ERROR_SUCCESS || ulCount == 0) {
if (ulFlags & CM_GETIDLIST_DONOTGENERATE) {
//
// If I'm calling this routine privately, don't generate
// a new device instance, just give me an empty list
//
*pBuffer = L'\0';
*pulLength = 0;
goto Clean0;
}
if (ServiceIsPlugPlay) {
//
// Also, if plugplayservice type set, don't generate a
// new device instance, just return success with an empty list
//
*pBuffer = L'\0';
*pulLength = 0;
goto Clean0;
}
memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA));
RtlInitUnicodeString(&ControlData.ServiceName, pszService);
ControlData.DeviceInstance = pBuffer;
ControlData.DeviceInstanceLength = *pulLength - 1;
NtStatus = NtPlugPlayControl(PlugPlayControlGenerateLegacyDevice,
&ControlData,
sizeof(ControlData));
if (NtStatus == STATUS_SUCCESS) {
*pulLength = ControlData.DeviceInstanceLength;
pBuffer[*pulLength] = L'\0'; // 1st NUL terminator
(*pulLength)++; // +1 for 1st NUL terminator
pBuffer[*pulLength] = L'\0'; // double NUL terminate
(*pulLength)++; // +1 for 2nd NUL terminator
} else {
*pBuffer = L'\0';
*pulLength = 0;
Status = CR_FAILURE;
}
goto Clean0;
}
//
// retrieve each device instance
//
for (i = 0; i < ulCount; i++) {
if (FAILED(StringCchPrintf(
RegStr,
SIZECHARS(RegStr),
L"%d",
i))) {
Status = CR_FAILURE;
goto Clean0;
}
ulSize = MAX_DEVICE_ID_LEN * sizeof(WCHAR);
RegStatus = RegQueryValueEx(hEnumKey, RegStr, NULL, NULL,
(LPBYTE)szDeviceID, &ulSize);
if (RegStatus != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
//
// this string is not always null-terminated when I read it from the
// registry, even though it's REG_SZ.
//
ulSize /= sizeof(WCHAR);
if (szDeviceID[ulSize-1] != L'\0') {
szDeviceID[ulSize] = L'\0';
}
ulSize = ulBufferLen * sizeof(WCHAR); // total buffer size in bytes
if (!MultiSzAppendW(pBuffer, &ulSize, szDeviceID)) {
Status = CR_BUFFER_SMALL;
*pulLength = 0;
goto Clean0;
}
*pulLength = ulSize/sizeof(WCHAR); // chars to transfer
}
Clean0:
NOTHING;
} except (EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hEnumKey != NULL) {
RegCloseKey(hEnumKey);
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
return Status;
} // GetServiceDeviceList
CONFIGRET
GetInstanceListSize(
IN LPCWSTR pszDevice,
OUT PULONG pulLength
)
/*++
Routine Description:
This routine returns the a list of device instances for the specificed
enumerator.
Arguments:
pszDevice device whose instances are to be listed
pulLength On output, specifies the size in characters required to hold
the device istance list.
Return Value:
If the function succeeds, it returns CR_SUCCESS, otherwise it returns
a CR_* error code.
--*/
{
CONFIGRET Status = CR_SUCCESS;
ULONG ulCount = 0, ulMaxKeyLen = 0;
HKEY hKey = NULL;
size_t DeviceIdLen = 0;
try {
//
// validate parameters
//
if ((!ARGUMENT_PRESENT(pszDevice)) ||
(!ARGUMENT_PRESENT(pulLength))) {
Status = CR_INVALID_POINTER;
goto Clean0;
}
//
// Open a key to the device instance
//
if (RegOpenKeyEx(ghEnumKey, pszDevice, 0, KEY_READ,
&hKey) != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
//
// how many instance keys are under this device?
//
if (RegQueryInfoKey(hKey, NULL, NULL, NULL, &ulCount, &ulMaxKeyLen,
NULL, NULL, NULL, NULL, NULL, NULL)
!= ERROR_SUCCESS) {
ulCount = 0;
ulMaxKeyLen = 0;
}
//
// do worst case estimate:
// length of the <enumerator>\<root> string +
// 1 char for the back slash before the instance +
// the length of the longest instance key + null term +
// multiplied by the number of instances under this device.
//
if (FAILED(StringCchLength(
pszDevice,
MAX_DEVICE_ID_LEN,
&DeviceIdLen))) {
Status = CR_FAILURE;
goto Clean0;
}
ASSERT(DeviceIdLen > 0);
ASSERT(DeviceIdLen < MAX_DEVICE_ID_LEN);
*pulLength = ulCount * (ULONG)(DeviceIdLen + ulMaxKeyLen + 2) + 1;
Clean0:
NOTHING;
} except (EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
return Status;
} // GetInstanceListSize
CONFIGRET
GetInstanceList(
IN LPCWSTR pszDevice,
IN OUT LPWSTR *pBuffer,
IN OUT PULONG pulLength
)
/*++
Routine Description:
This routine returns the a list of device instances for the specificed
enumerator.
Arguments:
hEnumKey Handle to open Enum registry key
pszDevice device whose instances are to be listed
pBuffer On input, this points to place where the next element
should be copied (the buffer tail), on output, it also
points to the end of the buffer.
pulLength On input, specifies the size in characters of Buffer, on
Output, specifies how many characters actually copied to
the buffer. Includes an extra byte for the double-null term.
Return Value:
If the function succeeds, it returns CR_SUCCESS, otherwise it returns
a CR_* error code.
--*/
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
WCHAR RegStr[MAX_CM_PATH], szInstance[MAX_DEVICE_ID_LEN];
ULONG ulBufferLen=0, ulSize=0, ulIndex=0, ulLen=0;
HKEY hKey = NULL;
HRESULT hr;
size_t DeviceIdLen = 0;
try {
//
// validate parameters
//
if ((!ARGUMENT_PRESENT(pszDevice)) ||
(!ARGUMENT_PRESENT(*pBuffer)) ||
(!ARGUMENT_PRESENT(pulLength))) {
Status = CR_INVALID_POINTER;
goto Clean0;
}
//
// Open a key for this Enumerator\Device branch
//
if (RegOpenKeyEx(ghEnumKey, pszDevice, 0, KEY_ENUMERATE_SUB_KEYS,
&hKey) != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
ulBufferLen = *pulLength; // total size of pBuffer
*pulLength = 0; // no data copied yet
ulIndex = 0;
//
// enumerate the instance keys
//
while (RegStatus == ERROR_SUCCESS) {
ulLen = MAX_DEVICE_ID_LEN; // size in chars
RegStatus = RegEnumKeyEx(hKey, ulIndex, szInstance, &ulLen,
NULL, NULL, NULL, NULL);
ulIndex++;
if (RegStatus == ERROR_SUCCESS) {
hr = StringCchPrintf(RegStr,
SIZECHARS(RegStr),
L"%s\\%s",
pszDevice,
szInstance);
if ((SUCCEEDED(hr)) &&
(IsValidDeviceID(RegStr, NULL, 0))) {
hr = StringCchLength(RegStr,
MAX_DEVICE_ID_LEN,
&DeviceIdLen);
ASSERT(SUCCEEDED(hr));
if (FAILED(hr)) {
*pulLength = 0;
Status = CR_FAILURE;
goto Clean0;
}
ulSize = (ULONG)(DeviceIdLen + 1); // size of new element
*pulLength += ulSize; // size copied so far
if ((*pulLength + 1) > ulBufferLen) {
*pulLength = 0;
Status = CR_BUFFER_SMALL;
goto Clean0;
}
hr = StringCchCopyEx(*pBuffer,
ulBufferLen,
RegStr,
NULL, NULL,
STRSAFE_NULL_ON_FAILURE);
ASSERT(SUCCEEDED(hr));
if (FAILED(hr)) {
ASSERT(HRESULT_CODE(hr) != ERROR_INSUFFICIENT_BUFFER);
*pulLength = 0;
Status = CR_FAILURE;
goto Clean0;
}
*pBuffer += ulSize; // move to tail of buffer
}
}
}
**pBuffer = 0x0; // double-null terminate it
*pulLength += 1; // include room for double-null terminator
Clean0:
NOTHING;
} except (EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
return Status;
} // GetInstanceList
CONFIGRET
GetDeviceInstanceListSize(
IN LPCWSTR pszEnumerator,
OUT PULONG pulLength
)
/*++
Routine Description:
This routine returns the a list of device instances for the specificed
enumerator.
Arguments:
pszEnumerator Enumerator whose device instances are to be listed
pulLength On output, specifies how many characters required to hold
the device instance list.
Return Value:
If the function succeeds, it returns CR_SUCCESS, otherwise it returns
a CR_* error code.
--*/
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
ULONG ulSize = 0, ulIndex = 0;
WCHAR RegStr[MAX_CM_PATH], szDevice[MAX_DEVICE_ID_LEN];
HKEY hKey = NULL;
try {
//
// validate parameters
//
if ((!ARGUMENT_PRESENT(pszEnumerator)) ||
(!ARGUMENT_PRESENT(pulLength))) {
Status = CR_INVALID_POINTER;
goto Clean0;
}
//
// initialize output length param
//
*pulLength = 0;
//
// Open a key for this Enumerator branch
//
if (RegOpenKeyEx(ghEnumKey, pszEnumerator, 0, KEY_ENUMERATE_SUB_KEYS,
&hKey) != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
//
// Enumerate the device keys
//
ulIndex = 0;
while (RegStatus == ERROR_SUCCESS) {
ulSize = MAX_DEVICE_ID_LEN; // size in chars
RegStatus = RegEnumKeyEx(hKey, ulIndex, szDevice, &ulSize,
NULL, NULL, NULL, NULL);
ulIndex++;
if (RegStatus == ERROR_SUCCESS) {
//
// Retrieve the size of the instance list for this device
//
if (FAILED(StringCchPrintf(
RegStr,
SIZECHARS(RegStr),
L"%s\\%s",
pszEnumerator,
szDevice))) {
Status = CR_FAILURE;
*pulLength = 0;
goto Clean0;
}
Status = GetInstanceListSize(RegStr, &ulSize);
if (Status != CR_SUCCESS) {
*pulLength = 0;
goto Clean0;
}
*pulLength += ulSize;
}
}
Clean0:
NOTHING;
} except (EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
return Status;
} // GetDeviceInstanceListSize
CONFIGRET
GetDeviceInstanceList(
IN LPCWSTR pszEnumerator,
IN OUT LPWSTR *pBuffer,
IN OUT PULONG pulLength
)
/*++
Routine Description:
This routine returns the a list of device instances for the specificed
enumerator.
Arguments:
hEnumKey Handle of open Enum (parent) registry key
pszEnumerator Enumerator whose device instances are to be listed
pBuffer On input, this points to place where the next element
should be copied (the buffer tail), on output, it also
points to the end of the buffer.
pulLength On input, specifies the size in characters of Buffer, on
Output, specifies how many characters actuall copied to
the buffer. Includes an extra byte for the double-null
term.
Return Value:
If the function succeeds, it returns CR_SUCCESS, otherwise it returns
a CR_* error code.
--*/
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
ULONG ulBufferLen=0, ulSize=0, ulIndex=0, ulLen=0;
WCHAR RegStr[MAX_CM_PATH], szDevice[MAX_DEVICE_ID_LEN];
HKEY hKey = NULL;
try {
//
// validate parameters
//
if ((!ARGUMENT_PRESENT(pszEnumerator)) ||
(!ARGUMENT_PRESENT(*pBuffer)) ||
(!ARGUMENT_PRESENT(pulLength))) {
Status = CR_INVALID_POINTER;
goto Clean0;
}
//
// Open a key for this Enumerator branch
//
if (RegOpenKeyEx(ghEnumKey, pszEnumerator, 0, KEY_ENUMERATE_SUB_KEYS,
&hKey) != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
ulIndex = 0;
ulSize = ulBufferLen = *pulLength; // total size of pBuffer
*pulLength = 0;
//
// Enumerate the device keys
//
while (RegStatus == ERROR_SUCCESS) {
ulLen = MAX_DEVICE_ID_LEN; // size in chars
RegStatus = RegEnumKeyEx(hKey, ulIndex, szDevice, &ulLen,
NULL, NULL, NULL, NULL);
ulIndex++;
if (RegStatus == ERROR_SUCCESS) {
//
// Retrieve the instance list for this device
//
if (FAILED(StringCchPrintf(
RegStr,
SIZECHARS(RegStr),
L"%s\\%s",
pszEnumerator,
szDevice))) {
Status = CR_FAILURE;
*pulLength = 0;
goto Clean0;
}
Status = GetInstanceList(RegStr, pBuffer, &ulSize);
if (Status != CR_SUCCESS) {
*pulLength = 0;
goto Clean0;
}
*pulLength += ulSize - 1; // data copied so far
ulSize = ulBufferLen - *pulLength; // buffer size left over
}
}
*pulLength += 1; // now add room for second null term
Clean0:
NOTHING;
} except (EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
return Status;
} // GetDeviceInstanceList
PNP_QUERY_RELATION
QueryOperationCode(
ULONG ulFlags
)
/*++
Routine Description:
This routine converts the CM_GETIDLIST_FILTER_Xxx query relation type
flags into the corresponding enum value that NtPlugPlayControl understands.
Arguments:
ulFlags CM API CM_GETIDLIST_FILTER_Xxx value
Return Value:
One of the enum PNP_QUERY_RELATION values.
--*/
{
switch (ulFlags) {
case CM_GETIDLIST_FILTER_EJECTRELATIONS:
return PnpQueryEjectRelations;
case CM_GETIDLIST_FILTER_REMOVALRELATIONS:
return PnpQueryRemovalRelations;
case CM_GETIDLIST_FILTER_POWERRELATIONS:
return PnpQueryPowerRelations;
case CM_GETIDLIST_FILTER_BUSRELATIONS:
return PnpQueryBusRelations;
default:
return (ULONG)-1;
}
} // QueryOperationCode