2082 lines
54 KiB
C
2082 lines
54 KiB
C
/*++
|
||
|
||
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
|
||
|
||
|
||
|