mirror of https://github.com/tongzx/nt5src
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.
2081 lines
54 KiB
2081 lines
54 KiB
/*++
|
|
|
|
Copyright (c) 1995-2001 Microsoft Corporation
|
|
|
|
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"
|
|
#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 Not used.
|
|
|
|
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;
|
|
ULONG ulSize, ulValue, ulStatus = 0, ulProblem = 0;
|
|
|
|
UNREFERENCED_PARAMETER(hBinding);
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
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 nonvoatile 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 ulProfile = 0, ulCount = 0;
|
|
WCHAR RegStr[MAX_CM_PATH];
|
|
|
|
//
|
|
// 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!
|
|
//
|
|
wsprintf(RegStr, TEXT("%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
|
|
//
|
|
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.
|
|
//
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_REGISTRY,
|
|
"UMPNPMGR: PNP_ValidateDeviceInstance make key %ws non-volatile\n",
|
|
pDeviceID));
|
|
|
|
MakeKeyNonVolatile(RegStr, pDeviceID);
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
|
|
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
|
|
//
|
|
if (ulLength < (ULONG)lstrlen(pszRegRootEnumerator)+1) {
|
|
Status = CR_BUFFER_SMALL;
|
|
goto Clean0;
|
|
}
|
|
|
|
lstrcpy(pDeviceID, pszRegRootEnumerator);
|
|
|
|
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;
|
|
|
|
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)) {
|
|
|
|
//
|
|
// Check if this is a phantom. 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.
|
|
//
|
|
|
|
if ((ULONG)(lstrlen(pszRegRootEnumerator) + 1) > *pulLength) {
|
|
lstrcpyn(pRelatedDeviceID, pszRegRootEnumerator,*pulLength);
|
|
Status = CR_BUFFER_SMALL;
|
|
} else {
|
|
lstrcpy(pRelatedDeviceID, pszRegRootEnumerator);
|
|
}
|
|
*pulLength = lstrlen(pszRegRootEnumerator) + 1;
|
|
goto Clean0;
|
|
}
|
|
|
|
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++) {
|
|
|
|
wsprintf(RegStr, TEXT("%d"), i);
|
|
|
|
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;
|
|
|
|
|
|
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.
|
|
//
|
|
*pulLength = ulCount * (lstrlen(pszDevice) + 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;
|
|
|
|
|
|
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) {
|
|
|
|
wsprintf(RegStr, TEXT("%s\\%s"),
|
|
pszDevice,
|
|
szInstance);
|
|
|
|
if (IsValidDeviceID(RegStr, NULL, 0)) {
|
|
|
|
ulSize = lstrlen(RegStr) + 1; // size of new element
|
|
*pulLength += ulSize; // size copied so far
|
|
|
|
if (*pulLength + 1 > ulBufferLen) {
|
|
*pulLength = 0;
|
|
Status = CR_BUFFER_SMALL;
|
|
goto Clean0;
|
|
}
|
|
|
|
lstrcpy(*pBuffer, RegStr); // copy the element
|
|
*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) {
|
|
//
|
|
// Retreive the size of the instance list for this device
|
|
//
|
|
wsprintf(RegStr, TEXT("%s\\%s"),
|
|
pszEnumerator,
|
|
szDevice);
|
|
|
|
if ((Status = GetInstanceListSize(RegStr, &ulSize)) != 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) {
|
|
//
|
|
// Enumerate the Instance keys
|
|
//
|
|
wsprintf(RegStr, TEXT("%s\\%s"),
|
|
pszEnumerator,
|
|
szDevice);
|
|
|
|
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
|
|
|
|
|
|
|