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.
4735 lines
155 KiB
4735 lines
155 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
rregprop.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the server-side registry property routines.
|
|
|
|
PNP_GetDeviceRegProp
|
|
PNP_SetDeviceRegProp
|
|
PNP_GetClassRegProp
|
|
PNP_SetClassRegProp
|
|
PNP_GetClassInstance
|
|
PNP_CreateKey
|
|
PNP_DeleteRegistryKey
|
|
PNP_GetClassCount
|
|
PNP_GetClassName
|
|
PNP_DeleteClassKey
|
|
PNP_GetInterfaceDeviceAlias
|
|
PNP_GetInterfaceDeviceList
|
|
PNP_GetInterfaceDeviceListSize
|
|
PNP_RegisterDeviceClassAssociation
|
|
PNP_UnregisterDeviceClassAssociation
|
|
PNP_GetCustomDevProp
|
|
|
|
This module contains the privately exported registry property routines.
|
|
|
|
DeleteServicePlugPlayRegKeys
|
|
|
|
Author:
|
|
|
|
Paula Tomlinson (paulat) 6-23-1995
|
|
|
|
Environment:
|
|
|
|
User-mode only.
|
|
|
|
Revision History:
|
|
|
|
23-June-1995 paulat
|
|
|
|
Creation and initial implementation.
|
|
|
|
--*/
|
|
|
|
|
|
//
|
|
// includes
|
|
//
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include "umpnpi.h"
|
|
#include "umpnpdat.h"
|
|
|
|
#include <accctrl.h>
|
|
#include <aclapi.h>
|
|
|
|
|
|
//
|
|
// private prototypes
|
|
//
|
|
|
|
LPWSTR
|
|
MapPropertyToString(
|
|
ULONG ulProperty
|
|
);
|
|
|
|
ULONG
|
|
MapPropertyToNtProperty(
|
|
ULONG ulProperty
|
|
);
|
|
|
|
HKEY
|
|
FindMostAppropriatePerHwIdSubkey(
|
|
IN HKEY hDevKey,
|
|
IN REGSAM samDesired,
|
|
OUT LPWSTR PerHwIdSubkeyName,
|
|
OUT LPDWORD PerHwIdSubkeyLen
|
|
);
|
|
|
|
//
|
|
// global data
|
|
//
|
|
extern HKEY ghEnumKey; // Key to HKLM\CCC\System\Enum - DO NOT MODIFY
|
|
extern HKEY ghClassKey; // Key to HKLM\CCC\System\Class - DO NOT MODIFY
|
|
extern HKEY ghPerHwIdKey; // Key to HKLM\Software\Microsoft\Windows NT\CurrentVersion\PerHwIdStorage - DO NOT MODIFY
|
|
|
|
|
|
BYTE bDeviceReadPropertyFlags[] = {
|
|
0, // zero-index not used
|
|
1, // CM_DRP_DEVICEDESC
|
|
1, // CM_DRP_HARDWAREID
|
|
1, // CM_DRP_COMPATIBLEIDS
|
|
0, // CM_DRP_UNUSED0
|
|
1, // CM_DRP_SERVICE
|
|
0, // CM_DRP_UNUSED1
|
|
0, // CM_DRP_UNUSED2
|
|
1, // CM_DRP_CLASS
|
|
1, // CM_DRP_CLASSGUID
|
|
1, // CM_DRP_DRIVER
|
|
1, // CM_DRP_CONFIGFLAGS
|
|
1, // CM_DRP_MFG
|
|
1, // CM_DRP_FRIENDLYNAME
|
|
1, // CM_DRP_LOCATION_INFORMATION
|
|
1, // CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME
|
|
1, // CM_DRP_CAPABILITIES
|
|
1, // CM_DRP_UI_NUMBER
|
|
1, // CM_DRP_UPPERFILTERS
|
|
1, // CM_DRP_LOWERFILTERS
|
|
1, // CM_DRP_BUSTYPEGUID
|
|
1, // CM_DRP_LEGACYBUSTYPE
|
|
1, // CM_DRP_BUSNUMBER
|
|
1, // CM_DRP_ENUMERATOR_NAME
|
|
1, // CM_DRP_SECURITY
|
|
0, // CM_DRP_SECURITY_SDS - client property only (converted from CM_DRP_SECURITY)
|
|
1, // CM_DRP_DEVTYPE
|
|
1, // CM_DRP_EXCLUSIVE
|
|
1, // CM_DRP_CHARACTERISTICS
|
|
1, // CM_DRP_ADDRESS
|
|
1, // CM_DRP_UI_NUMBER_DESC_FORMAT
|
|
1, // CM_DRP_DEVICE_POWER_DATA
|
|
1, // CM_DRP_REMOVAL_POLICY
|
|
1, // CM_DRP_REMOVAL_POLICY_HW_DEFAULT
|
|
1, // CM_DRP_REMOVAL_POLICY_OVERRIDE
|
|
1, // CM_DRP_INSTALL_STATE
|
|
1, // CM_DRP_LOCATION_PATHS
|
|
};
|
|
|
|
BYTE bDeviceWritePropertyFlags[] = {
|
|
0, // zero-index not used
|
|
1, // CM_DRP_DEVICEDESC
|
|
1, // CM_DRP_HARDWAREID
|
|
1, // CM_DRP_COMPATIBLEIDS
|
|
0, // CM_DRP_UNUSED0
|
|
1, // CM_DRP_SERVICE
|
|
0, // CM_DRP_UNUSED1
|
|
0, // CM_DRP_UNUSED2
|
|
1, // CM_DRP_CLASS
|
|
1, // CM_DRP_CLASSGUID
|
|
1, // CM_DRP_DRIVER
|
|
1, // CM_DRP_CONFIGFLAGS
|
|
1, // CM_DRP_MFG
|
|
1, // CM_DRP_FRIENDLYNAME
|
|
1, // CM_DRP_LOCATION_INFORMATION
|
|
0, // CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME
|
|
0, // CM_DRP_CAPABILITIES
|
|
0, // CM_DRP_UI_NUMBER
|
|
1, // CM_DRP_UPPERFILTERS
|
|
1, // CM_DRP_LOWERFILTERS
|
|
0, // CM_DRP_BUSTYPEGUID
|
|
0, // CM_DRP_LEGACYBUSTYPE
|
|
0, // CM_DRP_BUSNUMBER
|
|
0, // CM_DRP_ENUMERATOR_NAME
|
|
1, // CM_DRP_SECURITY
|
|
0, // CM_DRP_SECURITY_SDS - client property only (converted to CM_DRP_SECURITY)
|
|
1, // CM_DRP_DEVTYPE
|
|
1, // CM_DRP_EXCLUSIVE
|
|
1, // CM_DRP_CHARACTERISTICS
|
|
0, // CM_DRP_ADDRESS
|
|
1, // CM_DRP_UI_NUMBER_DESC_FORMAT
|
|
0, // CM_DRP_DEVICE_POWER_DATA
|
|
0, // CM_DRP_REMOVAL_POLICY
|
|
0, // CM_DRP_REMOVAL_POLICY_HW_DEFAULT
|
|
1, // CM_DRP_REMOVAL_POLICY_OVERRIDE
|
|
0, // CM_DRP_INSTALL_STATE
|
|
0, // CM_DRP_LOCATION_PATHS
|
|
};
|
|
|
|
BYTE bClassReadPropertyFlags[] = {
|
|
0, // zero-index not used
|
|
0, // (CM_DRP_DEVICEDESC)
|
|
0, // (CM_DRP_HARDWAREID)
|
|
0, // (CM_DRP_COMPATIBLEIDS)
|
|
0, // (CM_DRP_UNUSED0)
|
|
0, // (CM_DRP_SERVICE)
|
|
0, // (CM_DRP_UNUSED1)
|
|
0, // (CM_DRP_UNUSED2)
|
|
0, // (CM_DRP_CLASS)
|
|
0, // (CM_DRP_CLASSGUID)
|
|
0, // (CM_DRP_DRIVER)
|
|
0, // (CM_DRP_CONFIGFLAGS)
|
|
0, // (CM_DRP_MFG)
|
|
0, // (CM_DRP_FRIENDLYNAME)
|
|
0, // (CM_DRP_LOCATION_INFORMATION)
|
|
0, // (CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME)
|
|
0, // (CM_DRP_CAPABILITIES)
|
|
0, // (CM_DRP_UI_NUMBER)
|
|
0, // (CM_DRP_UPPERFILTERS)
|
|
0, // (CM_DRP_LOWERFILTERS)
|
|
0, // (CM_DRP_BUSTYPEGUID)
|
|
0, // (CM_DRP_LEGACYBUSTYPE)
|
|
0, // (CM_DRP_BUSNUMBER)
|
|
0, // (CM_DRP_ENUMERATOR_NAME)
|
|
1, // CM_CRP_SECURITY
|
|
0, // CM_CRP_SECURITY_SDS - client property only (converted from CM_CRP_SECURITY)
|
|
1, // CM_CRP_DEVTYPE
|
|
1, // CM_CRP_EXCLUSIVE
|
|
1, // CM_CRP_CHARACTERISTICS
|
|
0, // (CM_DRP_ADDRESS)
|
|
0, // (CM_DRP_UI_NUMBER_DESC_FORMAT)
|
|
0, // (CM_DRP_DEVICE_POWER_DATA)
|
|
0, // (CM_DRP_REMOVAL_POLICY)
|
|
0, // (CM_DRP_REMOVAL_POLICY_HW_DEFAULT)
|
|
0, // (CM_DRP_REMOVAL_POLICY_OVERRIDE)
|
|
0, // (CM_DRP_INSTALL_STATE)
|
|
0, // (CM_DRP_LOCATION_PATHS)
|
|
};
|
|
|
|
BYTE bClassWritePropertyFlags[] = {
|
|
0, // zero-index not used
|
|
0, // (CM_DRP_DEVICEDESC)
|
|
0, // (CM_DRP_HARDWAREID)
|
|
0, // (CM_DRP_COMPATIBLEIDS)
|
|
0, // (CM_DRP_UNUSED0)
|
|
0, // (CM_DRP_SERVICE)
|
|
0, // (CM_DRP_UNUSED1)
|
|
0, // (CM_DRP_UNUSED2)
|
|
0, // (CM_DRP_CLASS)
|
|
0, // (CM_DRP_CLASSGUID)
|
|
0, // (CM_DRP_DRIVER)
|
|
0, // (CM_DRP_CONFIGFLAGS)
|
|
0, // (CM_DRP_MFG)
|
|
0, // (CM_DRP_FRIENDLYNAME)
|
|
0, // (CM_DRP_LOCATION_INFORMATION)
|
|
0, // (CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME)
|
|
0, // (CM_DRP_CAPABILITIES)
|
|
0, // (CM_DRP_UI_NUMBER)
|
|
0, // (CM_DRP_UPPERFILTERS)
|
|
0, // (CM_DRP_LOWERFILTERS)
|
|
0, // (CM_DRP_BUSTYPEGUID)
|
|
0, // (CM_DRP_LEGACYBUSTYPE)
|
|
0, // (CM_DRP_BUSNUMBER)
|
|
0, // (CM_DRP_ENUMERATOR_NAME)
|
|
1, // CM_CRP_SECURITY
|
|
0, // CM_CRP_SECURITY_SDS - client property only (converted to CM_CRP_SECURITY)
|
|
1, // CM_CRP_DEVTYPE
|
|
1, // CM_CRP_EXCLUSIVE
|
|
1, // CM_CRP_CHARACTERISTICS
|
|
0, // (CM_DRP_ADDRESS)
|
|
0, // (CM_DRP_UI_NUMBER_DESC_FORMAT)
|
|
0, // (CM_DRP_DEVICE_POWER_DATA)
|
|
0, // (CM_DRP_REMOVAL_POLICY)
|
|
0, // (CM_DRP_REMOVAL_POLICY_HW_DEFAULT)
|
|
0, // (CM_DRP_REMOVAL_POLICY_OVERRIDE)
|
|
0, // (CM_DRP_INSTALL_STATE)
|
|
0, // (CM_DRP_LOCATION_PATHS)
|
|
};
|
|
|
|
|
|
//
|
|
// compile-time property-array consistancy checks
|
|
//
|
|
|
|
C_ASSERT(CM_DRP_MIN == CM_CRP_MIN);
|
|
C_ASSERT(CM_DRP_MAX == CM_CRP_MAX);
|
|
|
|
C_ASSERT(ARRAY_SIZE(bDeviceReadPropertyFlags) == (CM_DRP_MAX + 1));
|
|
C_ASSERT(ARRAY_SIZE(bDeviceWritePropertyFlags) == (CM_DRP_MAX + 1));
|
|
C_ASSERT(ARRAY_SIZE(bClassReadPropertyFlags) == (CM_CRP_MAX + 1));
|
|
C_ASSERT(ARRAY_SIZE(bClassWritePropertyFlags) == (CM_CRP_MAX + 1));
|
|
|
|
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PNP_GetDeviceRegProp(
|
|
IN handle_t hBinding,
|
|
IN LPCWSTR pDeviceID,
|
|
IN ULONG ulProperty,
|
|
OUT PULONG pulRegDataType,
|
|
OUT LPBYTE Buffer,
|
|
IN OUT PULONG pulTransferLen,
|
|
IN OUT PULONG pulLength,
|
|
IN ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the RPC server entry point for the CM_Get_DevNode_Registry_Property
|
|
routine.
|
|
|
|
Arguments:
|
|
|
|
hBinding RPC binding handle, not used.
|
|
|
|
pDeviceID Supplies a string containing the device instance
|
|
whose property will be read from.
|
|
|
|
ulProperty ID specifying which property (the registry value)
|
|
to get.
|
|
|
|
pulRegDataType Supplies the address of a variable that will receive
|
|
the registry data type for this property (i.e., the REG_*
|
|
constants).
|
|
|
|
Buffer Supplies the address of the buffer that receives the
|
|
registry data. Can be NULL when simply retrieving
|
|
data size.
|
|
|
|
pulTransferLen Used by stubs, indicates how much data to copy back
|
|
into user buffer.
|
|
|
|
pulLength Parameter passed in by caller, on entry it contains
|
|
the size, in bytes, of the buffer, on exit it contains
|
|
either the amount of data copied to the caller's buffer
|
|
(if a transfer occured) or else the size of buffer
|
|
required to hold the property data.
|
|
|
|
ulFlags Not used, must be zero.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is CR_SUCCESS.
|
|
If the function fails, the return value is one of the following:
|
|
CR_INVALID_DEVNODE,
|
|
CR_INVALID_FLAG,
|
|
CR_INVALID_POINTER,
|
|
CR_NO_SUCH_VALUE,
|
|
CR_REGISTRY_ERROR, or
|
|
CR_BUFFER_SMALL.
|
|
|
|
Remarks:
|
|
|
|
The pointer passed in as the pulTransferLen argument must *NOT* be the same
|
|
as the pointer passed in for the pulLength argument.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
LONG RegStatus = ERROR_SUCCESS;
|
|
ULONG ulSize = 0;
|
|
HKEY hKey = NULL;
|
|
LPWSTR pPropertyName;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PLUGPLAY_CONTROL_PROPERTY_DATA ControlData;
|
|
LPCWSTR pSeparatorChar;
|
|
ULONG bufferLength;
|
|
HRESULT hr;
|
|
|
|
UNREFERENCED_PARAMETER(hBinding);
|
|
|
|
try {
|
|
//
|
|
// Validate parameters
|
|
//
|
|
ASSERT(pulTransferLen != pulLength);
|
|
|
|
if ((!ARGUMENT_PRESENT(pulTransferLen)) ||
|
|
(!ARGUMENT_PRESENT(pulLength))) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (INVALID_FLAGS(ulFlags, 0)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (!IsLegalDeviceId(pDeviceID)) {
|
|
Status = CR_INVALID_DEVNODE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Make sure we use no more than either what the caller specified or
|
|
// what was allocated by RPC, based on the transfer length.
|
|
//
|
|
*pulLength = min(*pulLength, *pulTransferLen);
|
|
*pulTransferLen = 0;
|
|
|
|
//
|
|
// validate property is valid, and readable
|
|
//
|
|
if ((ulProperty < CM_DRP_MIN) ||
|
|
(ulProperty > CM_DRP_MAX) ||
|
|
(!bDeviceReadPropertyFlags[ulProperty])) {
|
|
Status = CR_INVALID_PROPERTY;
|
|
goto Clean0;
|
|
}
|
|
|
|
switch (ulProperty) {
|
|
//
|
|
// for some fields, we need to ask from kernel-mode
|
|
//
|
|
case CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME:
|
|
|
|
//
|
|
// This property has special checking in kernel-mode to make
|
|
// sure the supplied buffer length is even so round it down.
|
|
//
|
|
|
|
*pulLength &= ~1;
|
|
// fall through
|
|
|
|
case CM_DRP_BUSTYPEGUID:
|
|
case CM_DRP_LEGACYBUSTYPE:
|
|
case CM_DRP_BUSNUMBER:
|
|
case CM_DRP_ADDRESS:
|
|
case CM_DRP_DEVICE_POWER_DATA:
|
|
case CM_DRP_REMOVAL_POLICY:
|
|
case CM_DRP_REMOVAL_POLICY_HW_DEFAULT:
|
|
case CM_DRP_REMOVAL_POLICY_OVERRIDE:
|
|
case CM_DRP_INSTALL_STATE:
|
|
case CM_DRP_LOCATION_PATHS:
|
|
|
|
if ((ulProperty == CM_DRP_DEVICE_POWER_DATA) ||
|
|
(ulProperty == CM_DRP_BUSTYPEGUID)) {
|
|
|
|
*pulRegDataType = REG_BINARY;
|
|
|
|
} else if (ulProperty == CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME) {
|
|
|
|
*pulRegDataType = REG_SZ;
|
|
|
|
} else if (ulProperty == CM_DRP_LOCATION_PATHS) {
|
|
|
|
*pulRegDataType = REG_MULTI_SZ;
|
|
|
|
} else {
|
|
//
|
|
// CM_DRP_LEGACYBUSTYPE, CM_DRP_BUSNUMBER, CM_DRP_ADDRESS,
|
|
// removal policy properties, and install state are all DWORDs
|
|
//
|
|
*pulRegDataType = REG_DWORD;
|
|
}
|
|
|
|
//
|
|
// For these properties, we zero out unfilled space. This ensures
|
|
// deterministic downlevel behavior if we expand any returned
|
|
// structures in a later release.
|
|
//
|
|
bufferLength = *pulLength;
|
|
|
|
//
|
|
// Fill in a control structure for the device list info.
|
|
//
|
|
|
|
memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_PROPERTY_DATA));
|
|
RtlInitUnicodeString(&ControlData.DeviceInstance, pDeviceID);
|
|
ControlData.PropertyType = MapPropertyToNtProperty(ulProperty);
|
|
ControlData.Buffer = Buffer;
|
|
ControlData.BufferSize = bufferLength;
|
|
|
|
//
|
|
// Call kernel-mode to get the device property.
|
|
//
|
|
|
|
ntStatus = NtPlugPlayControl(PlugPlayControlProperty,
|
|
&ControlData,
|
|
sizeof(ControlData));
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
|
|
ASSERT(bufferLength >= ControlData.BufferSize);
|
|
if (bufferLength > ControlData.BufferSize) {
|
|
|
|
RtlZeroMemory(
|
|
Buffer + ControlData.BufferSize,
|
|
bufferLength - ControlData.BufferSize
|
|
);
|
|
}
|
|
|
|
*pulLength = ControlData.BufferSize; // size in bytes
|
|
*pulTransferLen = bufferLength; // size in bytes
|
|
|
|
} else if (ntStatus == STATUS_BUFFER_TOO_SMALL) {
|
|
|
|
*pulLength = ControlData.BufferSize;
|
|
*pulTransferLen = 0;
|
|
Status = CR_BUFFER_SMALL;
|
|
} else {
|
|
*pulLength = 0;
|
|
*pulTransferLen = 0;
|
|
Status = MapNtStatusToCmError(ntStatus);
|
|
}
|
|
break;
|
|
|
|
case CM_DRP_ENUMERATOR_NAME:
|
|
|
|
*pulRegDataType = REG_SZ;
|
|
|
|
pSeparatorChar = wcschr(pDeviceID, L'\\');
|
|
|
|
ASSERT(pSeparatorChar != NULL);
|
|
|
|
if (pSeparatorChar == NULL) {
|
|
//
|
|
// Couldn't find a path separator char in the device id.
|
|
//
|
|
Status=CR_INVALID_DATA;
|
|
|
|
} else {
|
|
//
|
|
// Determine the number of bytes in the enumerator part.
|
|
//
|
|
ulSize = (ULONG)((PBYTE)pSeparatorChar - (PBYTE)pDeviceID) + sizeof(WCHAR);
|
|
|
|
//
|
|
// Fill in the caller's buffer, if it's large enough.
|
|
//
|
|
hr = StringCbCopyNEx((LPWSTR)Buffer,
|
|
*pulLength,
|
|
pDeviceID,
|
|
ulSize - sizeof(WCHAR),
|
|
NULL, NULL,
|
|
STRSAFE_NULL_ON_FAILURE);
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
//
|
|
// Marshall the amount of data copied to the buffer
|
|
//
|
|
*pulTransferLen = ulSize;
|
|
|
|
} else if (HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER) {
|
|
//
|
|
// Buffer too small, marshall no data
|
|
//
|
|
Status = CR_BUFFER_SMALL;
|
|
*pulTransferLen = 0;
|
|
|
|
} else {
|
|
//
|
|
// Some other failire, marshall no data.
|
|
//
|
|
Status = CR_FAILURE;
|
|
*pulTransferLen = 0;
|
|
}
|
|
|
|
//
|
|
// Whether any data was transfered or not, return the size
|
|
// required.
|
|
//
|
|
*pulLength = ulSize;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// for all the other fields, just get them from the registry
|
|
// open a key to the specified device id
|
|
//
|
|
if (RegOpenKeyEx(ghEnumKey, pDeviceID, 0, KEY_READ,
|
|
&hKey) != ERROR_SUCCESS) {
|
|
|
|
hKey = NULL; // ensure hKey stays NULL so we don't
|
|
// erroneously try to close it.
|
|
*pulLength = 0; // no size info for caller
|
|
Status = CR_INVALID_DEVINST;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// retrieve the string form of the property
|
|
//
|
|
pPropertyName = MapPropertyToString(ulProperty);
|
|
|
|
if (pPropertyName) {
|
|
//
|
|
// retrieve property setting
|
|
//
|
|
if (*pulLength == 0) {
|
|
//
|
|
// if length of buffer passed in is zero, just looking
|
|
// for how big a buffer is needed to read the property
|
|
//
|
|
if (RegQueryValueEx(hKey, pPropertyName, NULL, pulRegDataType,
|
|
NULL, pulLength) != ERROR_SUCCESS) {
|
|
|
|
*pulLength = 0;
|
|
Status = CR_NO_SUCH_VALUE;
|
|
goto Clean0;
|
|
}
|
|
Status = CR_BUFFER_SMALL; // According to spec
|
|
} else {
|
|
//
|
|
// retrieve the real property value, not just the size
|
|
//
|
|
RegStatus = RegQueryValueEx(hKey, pPropertyName, NULL,
|
|
pulRegDataType, Buffer, pulLength);
|
|
|
|
if (RegStatus != ERROR_SUCCESS) {
|
|
|
|
if (RegStatus == ERROR_MORE_DATA) {
|
|
|
|
Status = CR_BUFFER_SMALL;
|
|
goto Clean0;
|
|
} else {
|
|
|
|
*pulLength = 0; // no size info for caller
|
|
Status = CR_NO_SUCH_VALUE;
|
|
goto Clean0;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
|
|
Status = CR_NO_SUCH_VALUE;
|
|
goto Clean0;
|
|
}
|
|
}
|
|
|
|
Clean0:
|
|
//
|
|
// Data only needs to be transferred on CR_SUCCESS.
|
|
//
|
|
if (Status == CR_SUCCESS) {
|
|
*pulTransferLen = *pulLength;
|
|
} else if (ARGUMENT_PRESENT(pulTransferLen)) {
|
|
*pulTransferLen = 0;
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
//
|
|
// force compiler to respect statement ordering w.r.t. assignments
|
|
// for these variables...
|
|
//
|
|
hKey = hKey;
|
|
}
|
|
|
|
if (hKey != NULL) {
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PNP_GetDeviceRegProp
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PNP_SetDeviceRegProp(
|
|
IN handle_t hBinding,
|
|
IN LPCWSTR pDeviceID,
|
|
IN ULONG ulProperty,
|
|
IN ULONG ulDataType,
|
|
IN LPBYTE Buffer,
|
|
IN ULONG ulLength,
|
|
IN ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the RPC server entry point for the CM_Set_DevNode_Registry_Property
|
|
routine.
|
|
|
|
Arguments:
|
|
|
|
hBinding RPC binding handle.
|
|
|
|
pDeviceID Supplies a string containing the device instance
|
|
whose property will be written to.
|
|
|
|
ulProperty ID specifying which property (the registry value)
|
|
to set.
|
|
|
|
ulDataType Supplies the registry data type for the specified
|
|
property (i.e., REG_SZ, etc).
|
|
|
|
Buffer Supplies the address of the buffer that receives the
|
|
registry data. Can be NULL when simply retrieving
|
|
data size.
|
|
|
|
pulLength Parameter passed in by caller, on entry it contains
|
|
the size, in bytes, of the buffer, on exit it contains
|
|
either the amount of data copied to the caller's buffer
|
|
(if a transfer occured) or else the size of buffer
|
|
required to hold the property data.
|
|
|
|
ulFlags Not used, must be zero.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is CR_SUCCESS.
|
|
If the function fails, the return value is one of the following:
|
|
CR_ACCESS_DENIED,
|
|
CR_INVALID_DEVNODE,
|
|
CR_INVALID_FLAG,
|
|
CR_INVALID_POINTER,
|
|
CR_NO_SUCH_VALUE,
|
|
CR_REGISTRY_ERROR, or
|
|
CR_BUFFER_SMALL.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
LONG RegStatus = ERROR_SUCCESS;
|
|
HKEY hKey = NULL;
|
|
LPWSTR pPropertyName;
|
|
|
|
try {
|
|
//
|
|
// Verify client "write" access
|
|
//
|
|
if (!VerifyClientAccess(hBinding,
|
|
PLUGPLAY_WRITE)) {
|
|
Status = CR_ACCESS_DENIED;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if (INVALID_FLAGS(ulFlags, 0)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (!IsLegalDeviceId(pDeviceID)) {
|
|
Status = CR_INVALID_DEVNODE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// validate property is valid, and writeable
|
|
//
|
|
if ((ulProperty < CM_DRP_MIN) ||
|
|
(ulProperty > CM_DRP_MAX) ||
|
|
(!bDeviceWritePropertyFlags[ulProperty])) {
|
|
Status = CR_INVALID_PROPERTY;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Currently the only writable fields are in the registry
|
|
// however do any validation of bits
|
|
// this isn't foolproof but can catch some common errors
|
|
//
|
|
|
|
switch(ulProperty) {
|
|
case CM_DRP_CONFIGFLAGS: {
|
|
|
|
DWORD flags = 0;
|
|
ULONG ulStatus = 0;
|
|
ULONG ulProblem = 0;
|
|
|
|
//
|
|
// DWORD value
|
|
// try to catch setting CSCONFIGFLAG_DISABLED on a non-disableable device
|
|
// although we should have validated the size stuff elsewhere, it was at
|
|
// client-side so double-check here
|
|
//
|
|
if (ulDataType != REG_DWORD || ulLength != sizeof(DWORD) || Buffer == NULL) {
|
|
Status = CR_INVALID_DATA;
|
|
goto Clean0;
|
|
}
|
|
flags = *(DWORD*)Buffer;
|
|
if(flags & CONFIGFLAG_DISABLED) {
|
|
//
|
|
// we're interested in checking this decision to disable device
|
|
//
|
|
|
|
if (IsRootDeviceID(pDeviceID)) {
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_ERRORS,
|
|
"UMPNPMGR: Cannot set CONFIGFLAG_DISABLED for root device - did caller try to disable device first?\n"));
|
|
|
|
Status = CR_NOT_DISABLEABLE;
|
|
goto Clean0;
|
|
}
|
|
|
|
if((GetDeviceStatus(pDeviceID, &ulStatus, &ulProblem)==CR_SUCCESS)
|
|
&& !(ulStatus & DN_DISABLEABLE)) {
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_ERRORS,
|
|
"UMPNPMGR: Cannot set CONFIGFLAG_DISABLED for non-disableable device - did caller try to disable device first?\n"));
|
|
|
|
Status = CR_NOT_DISABLEABLE;
|
|
goto Clean0;
|
|
}
|
|
//
|
|
// ok, looks like we can proceed to disable device
|
|
//
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
//
|
|
// No special handling on other properties
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// open a key to the specified device id
|
|
//
|
|
if (RegOpenKeyEx(ghEnumKey, pDeviceID, 0, KEY_READ | KEY_WRITE,
|
|
&hKey) != ERROR_SUCCESS) {
|
|
|
|
Status = CR_INVALID_DEVINST;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// retrieve the string form of the property
|
|
//
|
|
pPropertyName = MapPropertyToString(ulProperty);
|
|
|
|
if (pPropertyName) {
|
|
//
|
|
// set (or delete) the property value
|
|
//
|
|
if (ulLength == 0) {
|
|
|
|
RegStatus = RegDeleteValue(hKey, pPropertyName);
|
|
}
|
|
else {
|
|
|
|
RegStatus = RegSetValueEx(hKey, pPropertyName, 0, ulDataType,
|
|
Buffer, ulLength);
|
|
}
|
|
if (RegStatus != ERROR_SUCCESS) {
|
|
|
|
Status = CR_REGISTRY_ERROR;
|
|
goto Clean0;
|
|
}
|
|
} else {
|
|
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// note that changes do not get applied until a reboot / query-remove-remove
|
|
//
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
//
|
|
// force compiler to respect statement ordering w.r.t. assignments
|
|
// for these variables...
|
|
//
|
|
hKey = hKey;
|
|
}
|
|
|
|
if (hKey != NULL) {
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PNP_SetDeviceRegProp
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PNP_GetClassRegProp(
|
|
IN handle_t hBinding,
|
|
IN LPCWSTR ClassGuid,
|
|
IN ULONG ulProperty,
|
|
OUT PULONG pulRegDataType OPTIONAL,
|
|
OUT LPBYTE Buffer,
|
|
IN OUT PULONG pulTransferLen,
|
|
IN OUT PULONG pulLength,
|
|
IN ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the RPC server entry point for the CM_Get_DevNode_Registry_Property
|
|
routine.
|
|
|
|
Arguments:
|
|
|
|
hBinding RPC binding handle, not used.
|
|
|
|
ClassGuid Supplies a string containing the Class Guid
|
|
whose property will be read from (Get) or written
|
|
to (Set).
|
|
|
|
ulProperty ID specifying which property (the registry value)
|
|
to get or set.
|
|
|
|
pulRegDataType Optionally, supplies the address of a variable that
|
|
will receive the registry data type for this property
|
|
(i.e., the REG_* constants).
|
|
|
|
Buffer Supplies the address of the buffer that receives the
|
|
registry data. Can be NULL when simply retrieving
|
|
data size.
|
|
|
|
pulTransferLen Used by stubs, indicates how much data to copy back
|
|
into user buffer.
|
|
|
|
pulLength Parameter passed in by caller, on entry it contains
|
|
the size, in bytes, of the buffer, on exit it contains
|
|
either the amount of data copied to the caller's buffer
|
|
(if a transfer occured) or else the size of buffer
|
|
required to hold the property data.
|
|
|
|
ulFlags Not used, must be zero.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is CR_SUCCESS.
|
|
If the function fails, the return value is one of the following:
|
|
CR_INVALID_DEVNODE,
|
|
CR_INVALID_FLAG,
|
|
CR_INVALID_POINTER,
|
|
CR_NO_SUCH_VALUE,
|
|
CR_REGISTRY_ERROR, or
|
|
CR_BUFFER_SMALL.
|
|
|
|
Remarks:
|
|
|
|
The pointer passed in as the pulTransferLen argument must *NOT* be the same
|
|
as the pointer passed in for the pulLength argument.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
LONG RegStatus = ERROR_SUCCESS;
|
|
HKEY hKeyClass = NULL;
|
|
HKEY hKeyProps = NULL;
|
|
LPWSTR pPropertyName;
|
|
|
|
UNREFERENCED_PARAMETER(hBinding);
|
|
|
|
try {
|
|
//
|
|
// Validate parameters
|
|
//
|
|
ASSERT(pulTransferLen != pulLength);
|
|
|
|
if (!ARGUMENT_PRESENT(pulTransferLen) ||
|
|
!ARGUMENT_PRESENT(pulLength)) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (INVALID_FLAGS(ulFlags, 0)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Make sure we use no more than either what the caller specified or
|
|
// what was allocated by RPC, based on the transfer length.
|
|
//
|
|
*pulLength = min(*pulLength, *pulTransferLen);
|
|
*pulTransferLen = 0;
|
|
|
|
//
|
|
// validate property is valid, and readable
|
|
//
|
|
if ((ulProperty < CM_CRP_MIN) ||
|
|
(ulProperty > CM_CRP_MAX) ||
|
|
(!bClassReadPropertyFlags[ulProperty])) {
|
|
Status = CR_INVALID_PROPERTY;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// open a key to the specified GUID - this should have already been created
|
|
//
|
|
if (RegOpenKeyEx(ghClassKey, ClassGuid, 0, KEY_READ,
|
|
&hKeyClass) != ERROR_SUCCESS) {
|
|
|
|
*pulTransferLen = 0; // no output data to marshal
|
|
*pulLength = 0; // no size info for caller
|
|
|
|
Status = CR_NO_SUCH_REGISTRY_KEY;
|
|
goto Clean0;
|
|
}
|
|
//
|
|
// open a key to parameters - if not created, there's no params
|
|
//
|
|
if (RegOpenKeyEx(hKeyClass, pszRegKeyProperties, 0, KEY_READ,
|
|
&hKeyProps) != ERROR_SUCCESS) {
|
|
|
|
*pulTransferLen = 0; // no output data to marshal
|
|
*pulLength = 0; // no size info for caller
|
|
|
|
Status = CR_NO_SUCH_VALUE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// retrieve the string form of the property
|
|
//
|
|
pPropertyName = MapPropertyToString(ulProperty);
|
|
|
|
if (pPropertyName) {
|
|
//
|
|
// retrieve property setting
|
|
//
|
|
if (*pulLength == 0) {
|
|
//
|
|
// if length of buffer passed in is zero, just looking
|
|
// for how big a buffer is needed to read the property
|
|
//
|
|
*pulTransferLen = 0;
|
|
|
|
if (RegQueryValueEx(hKeyProps, pPropertyName, NULL, pulRegDataType,
|
|
NULL, pulLength) != ERROR_SUCCESS) {
|
|
*pulLength = 0;
|
|
Status = CR_NO_SUCH_VALUE;
|
|
goto Clean0;
|
|
}
|
|
|
|
Status = CR_BUFFER_SMALL; // According to spec
|
|
} else {
|
|
//
|
|
// retrieve the real property value, not just the size
|
|
//
|
|
RegStatus = RegQueryValueEx(hKeyProps, pPropertyName, NULL,
|
|
pulRegDataType, Buffer, pulLength);
|
|
|
|
if (RegStatus != ERROR_SUCCESS) {
|
|
|
|
if (RegStatus == ERROR_MORE_DATA) {
|
|
*pulTransferLen = 0; // no output data to marshal
|
|
Status = CR_BUFFER_SMALL;
|
|
goto Clean0;
|
|
}
|
|
else {
|
|
*pulTransferLen = 0; // no output data to marshal
|
|
*pulLength = 0; // no size info for caller
|
|
Status = CR_NO_SUCH_VALUE;
|
|
goto Clean0;
|
|
}
|
|
}
|
|
*pulTransferLen = *pulLength;
|
|
}
|
|
} else {
|
|
|
|
*pulTransferLen = 0; // no output data to marshal
|
|
*pulLength = 0; // no size info for caller
|
|
Status = CR_NO_SUCH_VALUE;
|
|
goto Clean0;
|
|
}
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
//
|
|
// force compiler to respect statement ordering w.r.t. assignments
|
|
// for these variables...
|
|
//
|
|
hKeyProps = hKeyProps;
|
|
hKeyClass = hKeyClass;
|
|
}
|
|
|
|
if (hKeyProps != NULL) {
|
|
RegCloseKey(hKeyProps);
|
|
}
|
|
|
|
if (hKeyClass != NULL) {
|
|
RegCloseKey(hKeyClass);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PNP_GetClassRegProp
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PNP_SetClassRegProp(
|
|
IN handle_t hBinding,
|
|
IN LPCWSTR ClassGuid,
|
|
IN ULONG ulProperty,
|
|
IN ULONG ulDataType,
|
|
IN LPBYTE Buffer,
|
|
IN ULONG ulLength,
|
|
IN ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the RPC server entry point for the CM_Set_DevNode_Registry_Property
|
|
routine.
|
|
|
|
Arguments:
|
|
|
|
hBinding RPC binding handle.
|
|
|
|
ClassGuid Supplies a string containing the Class Guid
|
|
whose property will be read from (Get) or written
|
|
to (Set).
|
|
|
|
ulProperty ID specifying which property (the registry value)
|
|
to get or set.
|
|
|
|
ulDataType Supplies the registry data type for the specified
|
|
property (i.e., REG_SZ, etc).
|
|
|
|
Buffer Supplies the address of the buffer that receives the
|
|
registry data. Can be NULL when simply retrieving
|
|
data size.
|
|
|
|
pulLength Parameter passed in by caller, on entry it contains
|
|
the size, in bytes, of the buffer, on exit it contains
|
|
either the amount of data copied to the caller's buffer
|
|
(if a transfer occured) or else the size of buffer
|
|
required to hold the property data.
|
|
|
|
ulFlags Not used, must be zero.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is CR_SUCCESS.
|
|
If the function fails, the return value is one of the following:
|
|
CR_ACCESS_DENIED,
|
|
CR_INVALID_DEVNODE,
|
|
CR_INVALID_FLAG,
|
|
CR_INVALID_POINTER,
|
|
CR_NO_SUCH_VALUE,
|
|
CR_REGISTRY_ERROR, or
|
|
CR_BUFFER_SMALL.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
LONG RegStatus = ERROR_SUCCESS;
|
|
HKEY hKeyClass = NULL;
|
|
HKEY hKeyProps = NULL;
|
|
LPWSTR pPropertyName;
|
|
DWORD dwError;
|
|
|
|
try {
|
|
//
|
|
// Verify client "write" access
|
|
//
|
|
if (!VerifyClientAccess(hBinding,
|
|
PLUGPLAY_WRITE)) {
|
|
Status = CR_ACCESS_DENIED;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if (INVALID_FLAGS(ulFlags, 0)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// validate property is valid, and writeable
|
|
//
|
|
if ((ulProperty < CM_CRP_MIN) ||
|
|
(ulProperty > CM_CRP_MAX) ||
|
|
(!bClassWritePropertyFlags[ulProperty])) {
|
|
Status = CR_INVALID_PROPERTY;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Currently the only writable fields are in the registry
|
|
//
|
|
|
|
//
|
|
// open a key to the specified GUID - this should have already been created
|
|
//
|
|
if (RegOpenKeyEx(ghClassKey, ClassGuid, 0, KEY_READ,
|
|
&hKeyClass) != ERROR_SUCCESS) {
|
|
|
|
Status = CR_NO_SUCH_REGISTRY_KEY;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// open a key to parameters - if not created, we need to create it with priv permissions
|
|
// this is harmless for a delete, since we "need" it anyway
|
|
//
|
|
if (RegOpenKeyEx(hKeyClass, pszRegKeyProperties, 0, KEY_ALL_ACCESS,
|
|
&hKeyProps) != ERROR_SUCCESS) {
|
|
|
|
//
|
|
// properties key doesn't exist
|
|
// we need to create it with secure access (system-only access)
|
|
// we don't expect to do this often
|
|
//
|
|
PSID pSystemSid = NULL;
|
|
PACL pSystemAcl = NULL;
|
|
SECURITY_DESCRIPTOR SecDesc;
|
|
SECURITY_ATTRIBUTES SecAttrib;
|
|
BOOL bSuccess;
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
|
EXPLICIT_ACCESS ExplicitAccess;
|
|
|
|
bSuccess = AllocateAndInitializeSid( &NtAuthority,
|
|
1, // one authority - SYSTEM
|
|
SECURITY_LOCAL_SYSTEM_RID, // access to system only
|
|
0, 0, 0, 0, 0, 0, 0, // unused authority locations
|
|
&pSystemSid);
|
|
|
|
if (bSuccess) {
|
|
ExplicitAccess.grfAccessMode = SET_ACCESS;
|
|
ExplicitAccess.grfInheritance = CONTAINER_INHERIT_ACE;
|
|
ExplicitAccess.Trustee.pMultipleTrustee = NULL;
|
|
ExplicitAccess.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
|
|
ExplicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
ExplicitAccess.Trustee.TrusteeType = TRUSTEE_IS_GROUP;
|
|
ExplicitAccess.grfAccessPermissions = KEY_ALL_ACCESS;
|
|
ExplicitAccess.Trustee.ptstrName = (LPWSTR)pSystemSid;
|
|
|
|
dwError = SetEntriesInAcl( 1,
|
|
&ExplicitAccess,
|
|
NULL,
|
|
&pSystemAcl );
|
|
if (dwError != ERROR_SUCCESS) {
|
|
bSuccess = FALSE;
|
|
}
|
|
}
|
|
|
|
if (bSuccess) {
|
|
bSuccess =
|
|
InitializeSecurityDescriptor(
|
|
&SecDesc, SECURITY_DESCRIPTOR_REVISION);
|
|
}
|
|
|
|
if (bSuccess) {
|
|
bSuccess =
|
|
SetSecurityDescriptorDacl(
|
|
&SecDesc,
|
|
TRUE,
|
|
pSystemAcl,
|
|
FALSE);
|
|
}
|
|
|
|
//
|
|
// mostly a setup requirement, but good to have
|
|
// effectively is a pruning point in the security tree
|
|
// child keys inherit our permissions, but not our parents permissions
|
|
//
|
|
if (bSuccess) {
|
|
if (!SetSecurityDescriptorControl(
|
|
&SecDesc,
|
|
SE_DACL_PROTECTED | SE_DACL_AUTO_INHERITED,
|
|
SE_DACL_PROTECTED | SE_DACL_AUTO_INHERITED)) {
|
|
//
|
|
// non fatal if this fails
|
|
//
|
|
NOTHING;
|
|
}
|
|
}
|
|
if (bSuccess) {
|
|
bSuccess = IsValidSecurityDescriptor( &SecDesc );
|
|
}
|
|
|
|
if (bSuccess) {
|
|
SecAttrib.nLength = sizeof(SecAttrib);
|
|
SecAttrib.bInheritHandle = FALSE;
|
|
SecAttrib.lpSecurityDescriptor = &SecDesc;
|
|
|
|
if(RegCreateKeyEx(hKeyClass, pszRegKeyProperties, 0, NULL, REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS, &SecAttrib, &hKeyProps, NULL) != ERROR_SUCCESS) {
|
|
bSuccess = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// now cleanup
|
|
//
|
|
if (pSystemAcl) {
|
|
LocalFree(pSystemAcl);
|
|
}
|
|
if (pSystemSid) {
|
|
FreeSid(pSystemSid);
|
|
}
|
|
|
|
if (bSuccess == FALSE) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// retrieve the string form of the property
|
|
//
|
|
pPropertyName = MapPropertyToString(ulProperty);
|
|
|
|
if (pPropertyName) {
|
|
//
|
|
// set (or delete) the property value
|
|
//
|
|
if (ulLength == 0) {
|
|
|
|
RegStatus = RegDeleteValue(hKeyProps, pPropertyName);
|
|
}
|
|
else {
|
|
RegStatus = RegSetValueEx(hKeyProps, pPropertyName, 0, ulDataType,
|
|
Buffer, ulLength);
|
|
}
|
|
if (RegStatus != ERROR_SUCCESS) {
|
|
|
|
Status = CR_REGISTRY_ERROR;
|
|
goto Clean0;
|
|
}
|
|
} else {
|
|
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
//
|
|
// note that changes do not get applied until a reboot / query-remove-remove
|
|
//
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
//
|
|
// force compiler to respect statement ordering w.r.t. assignments
|
|
// for these variables...
|
|
//
|
|
hKeyProps = hKeyProps;
|
|
hKeyClass = hKeyClass;
|
|
}
|
|
|
|
if (hKeyProps != NULL) {
|
|
RegCloseKey(hKeyProps);
|
|
}
|
|
|
|
if (hKeyClass != NULL) {
|
|
RegCloseKey(hKeyClass);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PNP_SetClassRegProp
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PNP_GetClassInstance(
|
|
IN handle_t hBinding,
|
|
IN LPCWSTR pDeviceID,
|
|
OUT LPWSTR pszClassInstance,
|
|
IN ULONG ulLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the RPC private server entry point, it doesn't not directly
|
|
map one-to-one to any CM routine.
|
|
|
|
Arguments:
|
|
|
|
hBinding RPC binding handle.
|
|
|
|
pDeviceID Supplies a string containing the device instance
|
|
|
|
pszClassInstance String to return the class instance in
|
|
|
|
ulLength Size of the pszClassInstance string in chars
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is CR_SUCCESS.
|
|
If the function fails, the return value is one of the following:
|
|
CR_INVALID_DEVNODE,
|
|
CR_INVALID_FLAG,
|
|
CR_INVALID_POINTER,
|
|
CR_NO_SUCH_VALUE,
|
|
CR_ACCESS_DENIED,
|
|
CR_REGISTRY_ERROR.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status;
|
|
WCHAR szInstanceStr[5], szClassGuid[GUID_STRING_LEN];
|
|
WCHAR szClassInstance[GUID_STRING_LEN + 5];
|
|
DWORD disposition;
|
|
ULONG ulType, ulTransferLength, ulTempLength, ulInstance;
|
|
HKEY hClassKey = NULL, hInstanceKey = NULL;
|
|
HRESULT hr;
|
|
size_t ClassInstanceLen;
|
|
|
|
|
|
try {
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if (!IsLegalDeviceId(pDeviceID)) {
|
|
Status = CR_INVALID_DEVNODE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Get the class instance key name, if one exists.
|
|
//
|
|
ulTempLength = ulLength * sizeof(WCHAR);
|
|
ulTransferLength = ulTempLength;
|
|
|
|
Status = PNP_GetDeviceRegProp(hBinding,
|
|
pDeviceID,
|
|
CM_DRP_DRIVER,
|
|
&ulType,
|
|
(LPBYTE)pszClassInstance,
|
|
&ulTransferLength,
|
|
&ulTempLength,
|
|
0);
|
|
|
|
//
|
|
// The only failure case we will handle beyond attempting to retrieve an
|
|
// existing CM_DRP_DRIVER property for this device is if no such value
|
|
// exists. Note that any failure to attempt to create a new class
|
|
// instance key below should always return CR_NO_SUCH_VALUE to the
|
|
// caller.
|
|
//
|
|
if (Status != CR_NO_SUCH_VALUE) {
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Create the class instance since one does not already exist. To do
|
|
// this, we'll need to create new registry keys, and set a new registry
|
|
// property for this device, both of which require "write" access. Up
|
|
// until now, only "read" access was required, so make the additional
|
|
// check now.
|
|
//
|
|
if (!VerifyClientAccess(hBinding,
|
|
PLUGPLAY_WRITE)) {
|
|
|
|
//
|
|
// The client does not have access to create a class instance value
|
|
// for this device. Although the initial request was only to
|
|
// retrieve an existing value, the PNP_GetClassInstance API is used
|
|
// only to attempt to explicitly create a new "Driver" value, and
|
|
// does not directly correlate to any client CM API. We can return
|
|
// "access denied" to the client, because it should be expected by
|
|
// the caller when modifying persistent state on the server.
|
|
//
|
|
Status = CR_ACCESS_DENIED;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Get the class GUID property for the key to create the instance under.
|
|
//
|
|
ulTempLength = sizeof(szClassGuid);
|
|
ulTransferLength = ulTempLength;
|
|
|
|
Status = PNP_GetDeviceRegProp(hBinding,
|
|
pDeviceID,
|
|
CM_DRP_CLASSGUID,
|
|
&ulType,
|
|
(LPBYTE)szClassGuid,
|
|
&ulTransferLength,
|
|
&ulTempLength,
|
|
0);
|
|
if (Status != CR_SUCCESS) {
|
|
Status = CR_NO_SUCH_VALUE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Open the class key.
|
|
//
|
|
if (RegOpenKeyEx(
|
|
ghClassKey, szClassGuid, 0,
|
|
KEY_READ | KEY_WRITE,
|
|
&hClassKey) != ERROR_SUCCESS) {
|
|
Status = CR_NO_SUCH_VALUE;
|
|
goto Clean0;
|
|
}
|
|
|
|
|
|
for (ulInstance = 0; ulInstance <= 9999; ulInstance++) {
|
|
//
|
|
// Find the first available class instance key.
|
|
//
|
|
hr = StringCchPrintfEx(szInstanceStr,
|
|
SIZECHARS(szInstanceStr),
|
|
NULL, NULL,
|
|
STRSAFE_NULL_ON_FAILURE,
|
|
L"%04u",
|
|
ulInstance);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
if (FAILED(hr)) {
|
|
Status = CR_NO_SUCH_VALUE;
|
|
break;
|
|
}
|
|
|
|
if (RegCreateKeyEx(
|
|
hClassKey, szInstanceStr, 0, NULL,
|
|
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
|
|
&hInstanceKey, &disposition) != ERROR_SUCCESS) {
|
|
Status = CR_NO_SUCH_VALUE;
|
|
break;
|
|
}
|
|
|
|
RegCloseKey(hInstanceKey);
|
|
hInstanceKey = NULL;
|
|
|
|
if (disposition != REG_CREATED_NEW_KEY) {
|
|
//
|
|
// Opened an existing class instance key. Try the next one.
|
|
//
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// We created a new class instance key. Set the CM_DRP_DRIVER value
|
|
// for this device, and return.
|
|
//
|
|
hr = StringCchPrintf(szClassInstance,
|
|
SIZECHARS(szClassInstance),
|
|
L"%s\\%s",
|
|
szClassGuid,
|
|
szInstanceStr);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
if (FAILED(hr)) {
|
|
RegDeleteKey(hClassKey, szInstanceStr);
|
|
Status = CR_NO_SUCH_VALUE;
|
|
break;
|
|
}
|
|
|
|
ClassInstanceLen = 0;
|
|
hr = StringCchLength(szClassInstance,
|
|
SIZECHARS(szClassInstance),
|
|
&ClassInstanceLen);
|
|
|
|
ulTempLength = (ULONG)((ClassInstanceLen + 1)*sizeof(WCHAR));
|
|
|
|
Status = PNP_SetDeviceRegProp(hBinding,
|
|
pDeviceID,
|
|
CM_DRP_DRIVER,
|
|
REG_SZ,
|
|
(LPBYTE)szClassInstance,
|
|
ulTempLength,
|
|
0);
|
|
|
|
//
|
|
// If we failed to set the devnode property, delete the registry key
|
|
// we just created, or else we'll end up orphaning it.
|
|
//
|
|
if (Status != CR_SUCCESS) {
|
|
RegDeleteKey(hClassKey, szInstanceStr);
|
|
Status = CR_NO_SUCH_VALUE;
|
|
break;
|
|
}
|
|
|
|
ASSERT(Status == CR_SUCCESS);
|
|
|
|
//
|
|
// Now that we've successfully set the new class instance value for
|
|
// this device, attempt to retreive it again, using the buffer
|
|
// supplied by the caller.
|
|
//
|
|
|
|
ulTempLength = ulLength * sizeof(WCHAR);
|
|
ulTransferLength = ulTempLength;
|
|
|
|
Status = PNP_GetDeviceRegProp(hBinding,
|
|
pDeviceID,
|
|
CM_DRP_DRIVER,
|
|
&ulType,
|
|
(LPBYTE)pszClassInstance,
|
|
&ulTransferLength,
|
|
&ulTempLength,
|
|
0);
|
|
|
|
//
|
|
// We just set the property, so we know the value exists.
|
|
//
|
|
ASSERT(Status != CR_NO_SUCH_VALUE);
|
|
ASSERT((Status == CR_SUCCESS) || (Status == CR_BUFFER_SMALL));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we exhausted all possibile class instance keys, report that no
|
|
// class instance exists for this device.
|
|
//
|
|
|
|
if (ulInstance > 9999) {
|
|
Status = CR_NO_SUCH_VALUE;
|
|
}
|
|
|
|
//
|
|
// Close the class key.
|
|
//
|
|
|
|
RegCloseKey(hClassKey);
|
|
hClassKey = NULL;
|
|
|
|
ASSERT(hInstanceKey == NULL);
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
//
|
|
// force compiler to respect statement ordering w.r.t. assignments
|
|
// for these variables...
|
|
//
|
|
hClassKey = hClassKey;
|
|
hInstanceKey = hInstanceKey;
|
|
}
|
|
|
|
if (hClassKey != NULL) {
|
|
RegCloseKey(hClassKey);
|
|
}
|
|
|
|
if (hInstanceKey != NULL) {
|
|
RegCloseKey(hInstanceKey);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PNP_GetClassInstance
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PNP_CreateKey(
|
|
IN handle_t hBinding,
|
|
IN LPCWSTR pszDeviceID,
|
|
IN REGSAM samDesired,
|
|
IN ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the RPC server entry point for the CM_Open_DevNode_Key_Ex
|
|
routine.
|
|
|
|
Arguments:
|
|
|
|
hBinding RPC binding handle.
|
|
|
|
pszDeviceID Supplies the device instance string.
|
|
|
|
samDesired Not used.
|
|
|
|
ulFlags Not used, must be zero.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is CR_SUCCESS.
|
|
If the function fails, the return value is one of the following:
|
|
CR_ACCESS_DENIED,
|
|
CR_INVALID_DEVNODE,
|
|
CR_INVALID_FLAG,
|
|
CR_REGISTRY_ERROR, or
|
|
CR_BUFFER_SMALL.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
LONG RegStatus = ERROR_SUCCESS;
|
|
HKEY hKeyDevice = NULL, hKey = NULL;
|
|
ULONG ulSize = 0, i = 0;
|
|
BOOL bHasDacl, bStatus;
|
|
SECURITY_DESCRIPTOR NewSecDesc;
|
|
ACL_SIZE_INFORMATION AclSizeInfo;
|
|
SID_IDENTIFIER_AUTHORITY Authority = SECURITY_NT_AUTHORITY;
|
|
PSECURITY_DESCRIPTOR pSecDesc = NULL;
|
|
PACL pDacl = NULL, pNewDacl = NULL;
|
|
PSID pAdminSid = NULL;
|
|
PACCESS_ALLOWED_ACE pAce = NULL;
|
|
|
|
UNREFERENCED_PARAMETER(samDesired);
|
|
|
|
try {
|
|
//
|
|
// Verify client "write" access
|
|
//
|
|
if (!VerifyClientAccess(hBinding,
|
|
PLUGPLAY_WRITE)) {
|
|
Status = CR_ACCESS_DENIED;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if (INVALID_FLAGS(ulFlags, 0)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (!IsLegalDeviceId(pszDeviceID)) {
|
|
Status = CR_INVALID_DEVNODE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// open a key to the specified device id
|
|
//
|
|
RegStatus = RegOpenKeyEx(ghEnumKey, pszDeviceID, 0, KEY_READ, &hKeyDevice);
|
|
|
|
if (RegStatus != ERROR_SUCCESS) {
|
|
|
|
Status = CR_INVALID_DEVINST;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// create the key with security inherited from parent key. Note
|
|
// that I'm not using passed in access mask, in order to set the
|
|
// security later, it must be created with KEY_ALL_ACCESS.
|
|
//
|
|
RegStatus = RegCreateKeyEx( hKeyDevice, pszRegKeyDeviceParam, 0,
|
|
NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
|
|
NULL, &hKey, NULL);
|
|
|
|
if (RegStatus != ERROR_SUCCESS) {
|
|
Status = CR_REGISTRY_ERROR;
|
|
goto Clean0;
|
|
}
|
|
|
|
//-------------------------------------------------------------
|
|
// add admin-full privilege to the inherited security info
|
|
//-------------------------------------------------------------
|
|
//
|
|
//
|
|
// NOTE: we don't need to do this unless the key was newly created. In
|
|
// theory we only get here when the key doesn't already exist. However
|
|
// there is a remote chance of two threads getting here simultaneously. If
|
|
// this happens we would end up with two admin full control ACEs.
|
|
//
|
|
|
|
|
|
//
|
|
// create the admin-full SID
|
|
//
|
|
if (!AllocateAndInitializeSid( &Authority, 2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&pAdminSid)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
|
|
//
|
|
// get the current security descriptor for the key
|
|
//
|
|
RegStatus = RegGetKeySecurity( hKey, DACL_SECURITY_INFORMATION,
|
|
NULL, &ulSize);
|
|
|
|
|
|
if (RegStatus != ERROR_INSUFFICIENT_BUFFER &&
|
|
RegStatus != ERROR_SUCCESS) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
pSecDesc = HeapAlloc(ghPnPHeap, 0, ulSize);
|
|
|
|
if (pSecDesc == NULL) {
|
|
Status = CR_OUT_OF_MEMORY;
|
|
goto Clean0;
|
|
}
|
|
|
|
RegStatus = RegGetKeySecurity( hKey, DACL_SECURITY_INFORMATION,
|
|
pSecDesc, &ulSize);
|
|
|
|
if (RegStatus != ERROR_SUCCESS) {
|
|
Status = CR_REGISTRY_ERROR;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// get the current DACL
|
|
//
|
|
if (!GetSecurityDescriptorDacl(pSecDesc, &bHasDacl, &pDacl, &bStatus)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// create a new absolute security descriptor and DACL
|
|
//
|
|
if (!InitializeSecurityDescriptor( &NewSecDesc,
|
|
SECURITY_DESCRIPTOR_REVISION)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// calculate the size of the new DACL
|
|
//
|
|
ZeroMemory(&AclSizeInfo, sizeof(AclSizeInfo));
|
|
|
|
if (bHasDacl) {
|
|
if (!GetAclInformation( pDacl, &AclSizeInfo,
|
|
sizeof(ACL_SIZE_INFORMATION),
|
|
AclSizeInformation)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
ulSize = AclSizeInfo.AclBytesInUse;
|
|
} else {
|
|
ulSize = sizeof(ACL);
|
|
}
|
|
|
|
ulSize += sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pAdminSid) - sizeof(DWORD);
|
|
|
|
//
|
|
// create and initialize the new DACL
|
|
//
|
|
pNewDacl = HeapAlloc(ghPnPHeap, 0, ulSize);
|
|
|
|
if (pNewDacl == NULL) {
|
|
Status = CR_OUT_OF_MEMORY;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (!InitializeAcl(pNewDacl, ulSize, ACL_REVISION2)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// copy the current (original) DACL into this new one
|
|
//
|
|
if (bHasDacl) {
|
|
|
|
for (i = 0; i < AclSizeInfo.AceCount; i++) {
|
|
|
|
if (!GetAce(pDacl, i, (LPVOID *)&pAce)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// We need to skip copying any ACEs which refer to the Administrator
|
|
// to ensure that our full control ACE is the one and only.
|
|
//
|
|
if ((pAce->Header.AceType != ACCESS_ALLOWED_ACE_TYPE &&
|
|
pAce->Header.AceType != ACCESS_DENIED_ACE_TYPE) ||
|
|
!EqualSid((PSID)&pAce->SidStart, pAdminSid)) {
|
|
|
|
if (!AddAce( pNewDacl, ACL_REVISION2, (DWORD)~0U, pAce,
|
|
pAce->Header.AceSize)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// and my new admin-full ace to this new DACL
|
|
//
|
|
if (!AddAccessAllowedAceEx( pNewDacl, ACL_REVISION2,
|
|
CONTAINER_INHERIT_ACE, KEY_ALL_ACCESS,
|
|
pAdminSid)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Set the new DACL in the absolute security descriptor
|
|
//
|
|
if (!SetSecurityDescriptorDacl(&NewSecDesc, TRUE, pNewDacl, FALSE)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
//
|
|
// validate the new security descriptor
|
|
//
|
|
if (!IsValidSecurityDescriptor(&NewSecDesc)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// apply the new security back to the registry key
|
|
//
|
|
RegStatus = RegSetKeySecurity( hKey, DACL_SECURITY_INFORMATION,
|
|
&NewSecDesc);
|
|
|
|
if (RegStatus != ERROR_SUCCESS) {
|
|
Status = CR_REGISTRY_ERROR;
|
|
goto Clean0;
|
|
}
|
|
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
//
|
|
// force compiler to respect statement ordering w.r.t. assignments
|
|
// for these variables...
|
|
//
|
|
hKeyDevice = hKeyDevice;
|
|
hKey = hKey;
|
|
pAdminSid = pAdminSid;
|
|
pNewDacl = pNewDacl;
|
|
pSecDesc = pSecDesc;
|
|
}
|
|
|
|
if (hKeyDevice != NULL) {
|
|
RegCloseKey(hKeyDevice);
|
|
}
|
|
|
|
if (hKey != NULL) {
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
if (pAdminSid != NULL) {
|
|
FreeSid(pAdminSid);
|
|
}
|
|
|
|
if (pNewDacl != NULL) {
|
|
HeapFree(ghPnPHeap, 0, pNewDacl);
|
|
}
|
|
|
|
if (pSecDesc != NULL) {
|
|
HeapFree(ghPnPHeap, 0, pSecDesc);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PNP_CreateKey
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PNP_DeleteRegistryKey(
|
|
IN handle_t hBinding,
|
|
IN LPCWSTR pszDeviceID,
|
|
IN LPCWSTR pszParentKey,
|
|
IN LPCWSTR pszChildKey,
|
|
IN ULONG ulFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the RPC server entry point for the CM_Delete_DevNode_Key
|
|
routine.
|
|
|
|
Arguments:
|
|
|
|
hBinding RPC binding handle.
|
|
|
|
pszDeviceID Supplies the device instance string.
|
|
|
|
pszParentKey Supplies the parent registry path of the key to be
|
|
deleted.
|
|
|
|
pszChildKey Supplies the subkey to be deleted.
|
|
|
|
ulFlags If 0xFFFFFFFF then delete for all profiles
|
|
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is CR_SUCCESS.
|
|
If the function fails, the return value is one of the following:
|
|
CR_ACCESS_DENIED,
|
|
CR_INVALID_DEVNODE,
|
|
CR_INVALID_FLAG,
|
|
CR_INVALID_POINTER,
|
|
CR_NO_SUCH_VALUE,
|
|
CR_REGISTRY_ERROR.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = ERROR_SUCCESS;
|
|
LONG RegStatus = ERROR_SUCCESS;
|
|
HKEY hKey = NULL;
|
|
WCHAR szProfile[MAX_PROFILE_ID_LEN];
|
|
PWCHAR pszRegStr = NULL, pszRegKey1 = NULL, pszRegKey2 = NULL;
|
|
ULONG ulIndex = 0, ulSize = 0;
|
|
BOOL bPhantom = FALSE;
|
|
ULONG ulStatus, ulProblem;
|
|
PWCHAR pszFormatString = NULL;
|
|
HRESULT hr;
|
|
|
|
|
|
//
|
|
// Note, the service currently cannot access the HKCU branch, so I
|
|
// assume the keys specified are under HKEY_LOCAL_MACHINE.
|
|
//
|
|
|
|
try {
|
|
//
|
|
// Verify client "write" access
|
|
//
|
|
if (!VerifyClientAccess(hBinding,
|
|
PLUGPLAY_WRITE)) {
|
|
Status = CR_ACCESS_DENIED;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// validate parameters
|
|
// (currently, 0 and -1 are the only accepted flags.)
|
|
//
|
|
if ((ulFlags != 0) &&
|
|
(ulFlags != 0xFFFFFFFF)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (!IsLegalDeviceId(pszDeviceID)) {
|
|
Status = CR_INVALID_DEVNODE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// pszParentKey is a registry path to the pszChildKey parameter.
|
|
// pszChildKey may be a single path or a compound path, a compound
|
|
// path is specified if all those subkeys should be deleted (or
|
|
// made volatile). Note that for real keys we never modify anything
|
|
// but the lowest level private key.
|
|
//
|
|
if (!ARGUMENT_PRESENT(pszParentKey) ||
|
|
!ARGUMENT_PRESENT(pszChildKey) ||
|
|
((lstrlen(pszParentKey) + 1) > MAX_CM_PATH) ||
|
|
((lstrlen(pszChildKey) + 1) > MAX_CM_PATH)) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Allocate registry path buffers.
|
|
//
|
|
pszRegStr = HeapAlloc(ghPnPHeap, 0, 2*MAX_CM_PATH * sizeof(WCHAR));
|
|
if (pszRegStr == NULL) {
|
|
Status = CR_OUT_OF_MEMORY;
|
|
goto Clean0;
|
|
}
|
|
|
|
pszRegKey1 = HeapAlloc(ghPnPHeap, 0, 2*MAX_CM_PATH * sizeof(WCHAR));
|
|
if (pszRegKey1 == NULL) {
|
|
Status = CR_OUT_OF_MEMORY;
|
|
goto Clean0;
|
|
}
|
|
|
|
pszRegKey2 = HeapAlloc(ghPnPHeap, 0, 2*MAX_CM_PATH * sizeof(WCHAR));
|
|
if (pszRegKey2 == NULL) {
|
|
Status = CR_OUT_OF_MEMORY;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Is the device a phantom?
|
|
//
|
|
bPhantom = IsDevicePhantom((LPWSTR)pszDeviceID) ||
|
|
GetDeviceStatus(pszDeviceID, &ulStatus, &ulProblem) != CR_SUCCESS ||
|
|
!(ulStatus & DN_DRIVER_LOADED);
|
|
|
|
if (!bPhantom) {
|
|
//
|
|
// for a real key, we never modify anything but the key
|
|
// where private info is so split if compound. This may
|
|
// end up leaving a dead device key around in some cases
|
|
// but another instance of that device could show at any
|
|
// time so we can't make it volatile.
|
|
//
|
|
if (SplitString(pszChildKey,
|
|
TEXT('\\'),
|
|
1,
|
|
pszRegStr,
|
|
2 * MAX_CM_PATH,
|
|
pszRegKey2,
|
|
2 * MAX_CM_PATH)) {
|
|
//
|
|
// compound key, only the last subkey will be affected,
|
|
// tack the rest on as part of the parent key
|
|
//
|
|
hr = StringCchPrintfEx(pszRegKey1,
|
|
2 * MAX_CM_PATH,
|
|
NULL, NULL,
|
|
STRSAFE_NULL_ON_FAILURE,
|
|
L"%s\\%s",
|
|
pszParentKey,
|
|
pszRegStr);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
} else {
|
|
//
|
|
// wasn't compound so use the whole child key
|
|
//
|
|
hr = StringCchCopyEx(pszRegKey1,
|
|
2 * MAX_CM_PATH,
|
|
pszParentKey,
|
|
NULL, NULL,
|
|
STRSAFE_NULL_ON_FAILURE);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
hr = StringCchCopyEx(pszRegKey2,
|
|
2 * MAX_CM_PATH,
|
|
pszChildKey,
|
|
NULL, NULL,
|
|
STRSAFE_NULL_ON_FAILURE);
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
// SPECIAL CASE: If ulHardwareProfile == -1, then need to
|
|
// delete the private key for all profiles.
|
|
//-------------------------------------------------------------
|
|
|
|
if (ulFlags == 0xFFFFFFFF) {
|
|
|
|
hr = StringCchPrintfEx(pszRegStr,
|
|
2 * MAX_CM_PATH,
|
|
NULL, NULL,
|
|
STRSAFE_NULL_ON_FAILURE,
|
|
L"%s\\%s",
|
|
pszRegPathIDConfigDB,
|
|
pszRegKeyKnownDockingStates);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
RegStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE, pszRegStr, 0,
|
|
KEY_ALL_ACCESS, &hKey);
|
|
|
|
//
|
|
// enumerate the hardware profile keys
|
|
//
|
|
for (ulIndex = 0; RegStatus == ERROR_SUCCESS; ulIndex++) {
|
|
|
|
ulSize = MAX_PROFILE_ID_LEN;
|
|
RegStatus = RegEnumKeyEx( hKey, ulIndex, szProfile, &ulSize,
|
|
NULL, NULL, NULL, NULL);
|
|
|
|
if (RegStatus == ERROR_SUCCESS) {
|
|
//
|
|
// if phantom, go ahead and delete it
|
|
//
|
|
if (bPhantom) {
|
|
//
|
|
// pszParentKey contains replacement symbol for the profile id, %s
|
|
//
|
|
pszFormatString = wcschr(pszParentKey, L'%');
|
|
|
|
ASSERT(pszFormatString && (pszFormatString[1] == L's'));
|
|
|
|
if (pszFormatString && (pszFormatString[1] == L's')) {
|
|
|
|
hr = StringCchPrintfEx(pszRegStr,
|
|
2 * MAX_CM_PATH,
|
|
NULL, NULL,
|
|
STRSAFE_NULL_ON_FAILURE,
|
|
pszParentKey,
|
|
szProfile);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
Status = DeletePrivateKey( HKEY_LOCAL_MACHINE, pszRegStr,
|
|
pszChildKey);
|
|
} else {
|
|
Status = CR_FAILURE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if real, just make it volatile
|
|
//
|
|
else {
|
|
//
|
|
// pszRegKey1 contains replacement symbol for the profile id, %s
|
|
//
|
|
pszFormatString = wcschr(pszRegKey1, L'%');
|
|
|
|
ASSERT(pszFormatString && (pszFormatString[1] == L's'));
|
|
|
|
if (pszFormatString && (pszFormatString[1] == L's')) {
|
|
|
|
hr = StringCchPrintfEx(pszRegStr,
|
|
2 * MAX_CM_PATH,
|
|
NULL, NULL,
|
|
STRSAFE_NULL_ON_FAILURE,
|
|
pszRegKey1,
|
|
szProfile);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_REGISTRY,
|
|
"UMPNPMGR: PNP_DeleteRegistryKey make key %ws\\%ws volatile\n",
|
|
pszRegStr,
|
|
pszRegKey2));
|
|
|
|
Status = MakeKeyVolatile(pszRegStr, pszRegKey2);
|
|
|
|
} else {
|
|
Status = CR_FAILURE;
|
|
}
|
|
}
|
|
|
|
if (Status != CR_SUCCESS) {
|
|
goto Clean0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------
|
|
// not deleting for all profiles, so just delete the specified key
|
|
//------------------------------------------------------------------
|
|
|
|
else {
|
|
|
|
if (bPhantom) {
|
|
//
|
|
// if phantom, go ahead and delete it
|
|
//
|
|
Status = DeletePrivateKey( HKEY_LOCAL_MACHINE, pszParentKey,
|
|
pszChildKey);
|
|
}
|
|
else {
|
|
//
|
|
// if real, just make it volatile
|
|
//
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_REGISTRY,
|
|
"UMPNPMGR: PNP_DeleteRegistryKey make key %ws\\%ws volatile\n",
|
|
pszRegKey1,
|
|
pszRegKey2));
|
|
|
|
Status = MakeKeyVolatile(pszRegKey1, pszRegKey2);
|
|
}
|
|
|
|
if (Status != CR_SUCCESS) {
|
|
goto Clean0;
|
|
}
|
|
}
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
//
|
|
// force compiler to respect statement ordering w.r.t. assignments
|
|
// for these variables...
|
|
//
|
|
hKey = hKey;
|
|
pszRegStr = pszRegStr;
|
|
pszRegKey1 = pszRegKey1;
|
|
pszRegKey2 = pszRegKey2;
|
|
}
|
|
|
|
if (hKey != NULL) {
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
if (pszRegStr != NULL) {
|
|
HeapFree(ghPnPHeap, 0, pszRegStr);
|
|
}
|
|
|
|
if (pszRegKey1 != NULL) {
|
|
HeapFree(ghPnPHeap, 0, pszRegKey1);
|
|
}
|
|
|
|
if (pszRegKey2 != NULL) {
|
|
HeapFree(ghPnPHeap, 0, pszRegKey2);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PNP_DeleteRegistryKey
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PNP_GetClassCount(
|
|
IN handle_t hBinding,
|
|
OUT PULONG pulClassCount,
|
|
IN ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the RPC server entry point for the CM_Get_Class_Count routine.
|
|
It returns the number of valid classes currently installed (listed in
|
|
the registry).
|
|
|
|
Arguments:
|
|
|
|
hBinding RPC binding handle, not used.
|
|
|
|
pulClassCount Supplies the address of a variable that will
|
|
receive the number of classes installed.
|
|
|
|
ulFlags Not used.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is CR_SUCCESS.
|
|
If the function fails, the return value is one of the following:
|
|
CR_INVALID_FLAG,
|
|
CR_INVALID_POINTER, or
|
|
CR_REGISTRY_ERROR
|
|
|
|
Notes:
|
|
|
|
** PRESENTLY, ALWAYS RETURNS CR_CALL_NOT_IMPLEMENTED **
|
|
|
|
No corresponding CM_Get_Class_Count routine is implemented.
|
|
This routine currently returns CR_CALL_NOT_IMPLEMENTED.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER(hBinding);
|
|
UNREFERENCED_PARAMETER(pulClassCount);
|
|
UNREFERENCED_PARAMETER(ulFlags);
|
|
|
|
return CR_CALL_NOT_IMPLEMENTED;
|
|
|
|
} // PNP_GetClassCount
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PNP_GetClassName(
|
|
IN handle_t hBinding,
|
|
IN PCWSTR pszClassGuid,
|
|
OUT PWSTR Buffer,
|
|
IN OUT PULONG pulLength,
|
|
IN ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the RPC server entry point for the CM_Get_Class_Name routine.
|
|
It returns the name of the class represented by the GUID.
|
|
|
|
Arguments:
|
|
|
|
hBinding RPC binding handle, not used.
|
|
|
|
pszClassGuid String containing the class guid to retrieve a
|
|
class name for.
|
|
|
|
Buffer Supplies the address of the buffer that receives the
|
|
class name.
|
|
|
|
pulLength On input, this specifies the size of the Buffer in
|
|
characters. On output it contains the number of
|
|
characters actually copied to Buffer.
|
|
|
|
ulFlags Not used, must be zero.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is CR_SUCCESS.
|
|
If the function fails, the return value is one of the following:
|
|
CR_INVALID_FLAG,
|
|
CR_INVALID_POINTER,
|
|
CR_BUFFER_SMALL, or
|
|
CR_REGISTRY_ERROR
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
LONG RegStatus = ERROR_SUCCESS;
|
|
WCHAR RegStr[MAX_CM_PATH];
|
|
HKEY hKey = NULL;
|
|
ULONG ulLength;
|
|
|
|
UNREFERENCED_PARAMETER(hBinding);
|
|
|
|
try {
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if (INVALID_FLAGS(ulFlags, 0)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
if ((!ARGUMENT_PRESENT(pulLength)) ||
|
|
(!ARGUMENT_PRESENT(Buffer) && *pulLength != 0)) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Open the key for the specified class guid
|
|
//
|
|
if ((lstrlen (pszRegPathClass) + lstrlen (pszClassGuid) + sizeof (TEXT("\\"))) > MAX_CM_PATH) {
|
|
Status = CR_BUFFER_SMALL;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (FAILED(StringCchPrintf(
|
|
RegStr,
|
|
SIZECHARS(RegStr),
|
|
TEXT("%s\\%s"),
|
|
pszRegPathClass,
|
|
pszClassGuid))) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
RegStatus = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE, RegStr, 0, KEY_QUERY_VALUE, &hKey);
|
|
|
|
if (RegStatus != ERROR_SUCCESS) {
|
|
Status = CR_REGISTRY_ERROR;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Retrieve the class name string value
|
|
//
|
|
ulLength = *pulLength;
|
|
|
|
*pulLength *= sizeof(WCHAR); // convert to size in bytes
|
|
RegStatus = RegQueryValueEx(
|
|
hKey, pszRegValueClass, NULL, NULL,
|
|
(LPBYTE)Buffer, pulLength);
|
|
*pulLength /= sizeof(WCHAR); // convert back to chars
|
|
|
|
if (RegStatus == ERROR_SUCCESS) {
|
|
Status = CR_SUCCESS;
|
|
}
|
|
else if (RegStatus == ERROR_MORE_DATA) {
|
|
Status = CR_BUFFER_SMALL;
|
|
if ((ARGUMENT_PRESENT(Buffer)) &&
|
|
(ulLength > 0)) {
|
|
*Buffer = L'\0';
|
|
}
|
|
}
|
|
else {
|
|
Status = CR_REGISTRY_ERROR;
|
|
if ((ARGUMENT_PRESENT(Buffer)) &&
|
|
(ulLength > 0)) {
|
|
*Buffer = L'\0';
|
|
*pulLength = 1;
|
|
}
|
|
}
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
//
|
|
// force compiler to respect statement ordering w.r.t. assignments
|
|
// for these variables...
|
|
//
|
|
hKey = hKey;
|
|
}
|
|
|
|
if (hKey != NULL) {
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PNP_GetClassName
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PNP_DeleteClassKey(
|
|
IN handle_t hBinding,
|
|
IN PCWSTR pszClassGuid,
|
|
IN ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the RPC server entry point for the CM_Delete_Class_Key routine.
|
|
It deletes the corresponding registry key.
|
|
|
|
Arguments:
|
|
|
|
hBinding RPC binding handle.
|
|
|
|
pszClassGuid String containing the class guid to delete the device
|
|
setup class registry key for.
|
|
|
|
ulFlags Either CM_DELETE_CLASS_ONLY or CM_DELETE_CLASS_SUBKEYS.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is CR_SUCCESS.
|
|
If the function fails, the return value is one of the following:
|
|
CR_ACCESS_DENIED,
|
|
CR_INVALID_FLAG,
|
|
CR_INVALID_POINTER,
|
|
CR_NO_SUCH_REGISTRY_KEY,
|
|
CR_REGISTRY_ERROR, or
|
|
CR_FAILURE
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
HKEY hKey = NULL;
|
|
|
|
try {
|
|
//
|
|
// Verify client "write" access
|
|
//
|
|
if (!VerifyClientAccess(hBinding,
|
|
PLUGPLAY_WRITE)) {
|
|
Status = CR_ACCESS_DENIED;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if (INVALID_FLAGS(ulFlags, CM_DELETE_CLASS_BITS)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
ASSERT(ARGUMENT_PRESENT(pszClassGuid));
|
|
|
|
if ((!ARGUMENT_PRESENT(pszClassGuid)) ||
|
|
(*pszClassGuid == L'\0')) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Check that the specified class key exists.
|
|
//
|
|
if (RegOpenKeyEx(ghClassKey, pszClassGuid, 0, KEY_READ,
|
|
&hKey) != ERROR_SUCCESS) {
|
|
Status = CR_NO_SUCH_REGISTRY_KEY;
|
|
goto Clean0;
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
if (ulFlags == CM_DELETE_CLASS_SUBKEYS) {
|
|
//
|
|
// Delete the class key and any subkeys under it
|
|
//
|
|
if (!RegDeleteNode(ghClassKey, pszClassGuid)) {
|
|
Status = CR_REGISTRY_ERROR;
|
|
}
|
|
|
|
} else if (ulFlags == CM_DELETE_CLASS_ONLY) {
|
|
//
|
|
// only delete the class key itself (just attempt to delete
|
|
// using the registry routine, it will fail if any subkeys
|
|
// exist)
|
|
//
|
|
if (RegDeleteKey(ghClassKey, pszClassGuid) != ERROR_SUCCESS) {
|
|
Status = CR_REGISTRY_ERROR;
|
|
}
|
|
}
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PNP_DeleteClassKey
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PNP_GetInterfaceDeviceAlias(
|
|
IN handle_t hBinding,
|
|
IN PCWSTR pszInterfaceDevice,
|
|
IN LPGUID AliasInterfaceGuid,
|
|
OUT PWSTR pszAliasInterfaceDevice,
|
|
IN OUT PULONG pulLength,
|
|
IN OUT PULONG pulTransferLen,
|
|
IN ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the RPC server entry point for the CM_Get_Interface_Device_Alias routine.
|
|
It returns an alias string for the specified guid and interface device.
|
|
|
|
Arguments:
|
|
|
|
hBinding RPC binding handle, not used.
|
|
|
|
pszInterfaceDevice Specifies the interface device to find an alias for.
|
|
|
|
AliasInterfaceGuid Supplies the interface class GUID.
|
|
|
|
pszAliasInterfaceDevice Supplies the address of a variable that will
|
|
receive the device interface alias of the specified device
|
|
interface, that is a member of the specified alias
|
|
interface class GUID.
|
|
|
|
pulLength Parameter passed in by caller, on entry it contains
|
|
the size, in bytes, of the buffer, on exit it contains
|
|
either the amount of data copied to the caller's buffer
|
|
(if a transfer occured) or else the size of buffer
|
|
required to hold the property data.
|
|
|
|
pulTransferLen Used by stubs, indicates how much data to copy back
|
|
into user buffer.
|
|
|
|
ulFlags Not used, must be zero.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is CR_SUCCESS.
|
|
If the function fails, the return value is one of the following:
|
|
CR_INVALID_FLAG,
|
|
CR_INVALID_POINTER, or
|
|
CR_REGISTRY_ERROR
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PLUGPLAY_CONTROL_INTERFACE_ALIAS_DATA ControlData;
|
|
|
|
UNREFERENCED_PARAMETER(hBinding);
|
|
|
|
try {
|
|
//
|
|
// Validate parameters
|
|
//
|
|
ASSERT(pulTransferLen != pulLength);
|
|
|
|
if (!ARGUMENT_PRESENT(pszInterfaceDevice) ||
|
|
!ARGUMENT_PRESENT(AliasInterfaceGuid) ||
|
|
!ARGUMENT_PRESENT(pszAliasInterfaceDevice) ||
|
|
!ARGUMENT_PRESENT(pulTransferLen) ||
|
|
!ARGUMENT_PRESENT(pulLength)) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (INVALID_FLAGS(ulFlags, 0)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Make sure we use no more than either what the caller specified or
|
|
// what was allocated by RPC, based on the transfer length.
|
|
//
|
|
*pulLength = min(*pulLength, *pulTransferLen);
|
|
|
|
//
|
|
// Fill in a control structure for the device list info.
|
|
//
|
|
|
|
//
|
|
// Note that AliasInterfaceGuid was already validated above because this
|
|
// buffer is required for the PlugPlayControlGetInterfaceDeviceAlias
|
|
// control, and is probed unconditionally by kernel-mode. Better to
|
|
// fail the call above with a useful status than to return the generic
|
|
// CR_FAILURE after an exception/error from kernel-mode, below.
|
|
//
|
|
|
|
memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_INTERFACE_ALIAS_DATA));
|
|
RtlInitUnicodeString(&ControlData.SymbolicLinkName, pszInterfaceDevice);
|
|
ControlData.AliasClassGuid = AliasInterfaceGuid;
|
|
ControlData.AliasSymbolicLinkName = pszAliasInterfaceDevice;
|
|
ControlData.AliasSymbolicLinkNameLength = *pulLength; // chars
|
|
|
|
//
|
|
// Call kernel-mode to get the device interface alias.
|
|
//
|
|
|
|
ntStatus = NtPlugPlayControl(PlugPlayControlGetInterfaceDeviceAlias,
|
|
&ControlData,
|
|
sizeof(ControlData));
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
*pulLength = ControlData.AliasSymbolicLinkNameLength;
|
|
*pulTransferLen = *pulLength + 1;
|
|
} else if (ntStatus == STATUS_BUFFER_TOO_SMALL) {
|
|
*pulLength = ControlData.AliasSymbolicLinkNameLength;
|
|
Status = CR_BUFFER_SMALL;
|
|
} else {
|
|
*pulLength = 0;
|
|
Status = MapNtStatusToCmError(ntStatus);
|
|
}
|
|
|
|
Clean0:
|
|
|
|
//
|
|
// Initialize output parameters
|
|
//
|
|
if ((Status != CR_SUCCESS) &&
|
|
ARGUMENT_PRESENT(pulTransferLen) &&
|
|
ARGUMENT_PRESENT(pszAliasInterfaceDevice) &&
|
|
(*pulTransferLen > 0)) {
|
|
*pszAliasInterfaceDevice = L'\0';
|
|
*pulTransferLen = 1;
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PNP_GetInterfaceDeviceAlias
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PNP_GetInterfaceDeviceList(
|
|
IN handle_t hBinding,
|
|
IN LPGUID InterfaceGuid,
|
|
IN LPCWSTR pszDeviceID,
|
|
OUT LPWSTR Buffer,
|
|
IN OUT PULONG pulLength,
|
|
IN ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the RPC server entry point for the CM_Get_Device_Interface_List routine.
|
|
It returns a multi_sz interface device list.
|
|
|
|
Arguments:
|
|
|
|
hBinding RPC binding handle, not used.
|
|
|
|
InterfaceGuid Supplies the interface class GUID.
|
|
|
|
pszDeviceID Supplies the device instance string.
|
|
|
|
Buffer Supplies the address of the buffer that receives the
|
|
registry data.
|
|
|
|
pulLength Specifies the size, in bytes, of the buffer.
|
|
|
|
ulFlags Flags specifying which device interfaces to return.
|
|
Currently, may be either:
|
|
CM_GET_DEVICE_INTERFACE_LIST_PRESENT, or
|
|
CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is CR_SUCCESS.
|
|
If the function fails, the return value is one of the following:
|
|
CR_INVALID_FLAG,
|
|
CR_INVALID_DEVNODE,
|
|
CR_INVALID_POINTER, or
|
|
CR_REGISTRY_ERROR
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PLUGPLAY_CONTROL_INTERFACE_LIST_DATA ControlData;
|
|
|
|
UNREFERENCED_PARAMETER(hBinding);
|
|
|
|
try {
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if (INVALID_FLAGS(ulFlags, CM_GET_DEVICE_INTERFACE_LIST_BITS)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (!ARGUMENT_PRESENT(InterfaceGuid) ||
|
|
!ARGUMENT_PRESENT(pulLength) ||
|
|
!ARGUMENT_PRESENT(Buffer) ||
|
|
(*pulLength == 0)) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (!IsLegalDeviceId(pszDeviceID)) {
|
|
Status = CR_INVALID_DEVNODE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Fill in a control structure for the device list info.
|
|
//
|
|
|
|
//
|
|
// Note that InterfaceGuid was already validated above because this
|
|
// buffer is required for the PlugPlayControlGetInterfaceDeviceList
|
|
// control, and is probed unconditionally by kernel-mode. Better to
|
|
// fail the call above with a useful status than to return the generic
|
|
// CR_FAILURE after an exception/error from kernel-mode, below.
|
|
//
|
|
|
|
memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_INTERFACE_LIST_DATA));
|
|
RtlInitUnicodeString(&ControlData.DeviceInstance, pszDeviceID);
|
|
ControlData.InterfaceGuid = InterfaceGuid;
|
|
ControlData.InterfaceList = Buffer;
|
|
ControlData.InterfaceListSize = *pulLength;
|
|
|
|
if (ulFlags == CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES) {
|
|
ControlData.Flags = 0x1; // DEVICE_INTERFACE_INCLUDE_NONACTIVE (ntos\inc\pnp.h)
|
|
} else {
|
|
ControlData.Flags = 0;
|
|
}
|
|
|
|
//
|
|
// Call kernel-mode to get the device interface list.
|
|
//
|
|
|
|
ntStatus = NtPlugPlayControl(PlugPlayControlGetInterfaceDeviceList,
|
|
&ControlData,
|
|
sizeof(ControlData));
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
*pulLength = ControlData.InterfaceListSize;
|
|
} else {
|
|
*pulLength = 0;
|
|
if (ntStatus == STATUS_BUFFER_TOO_SMALL) {
|
|
Status = CR_BUFFER_SMALL;
|
|
} else {
|
|
Status = CR_FAILURE;
|
|
}
|
|
}
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PNP_GetInterfaceDeviceList
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PNP_GetInterfaceDeviceListSize(
|
|
IN handle_t hBinding,
|
|
OUT PULONG pulLen,
|
|
IN LPGUID InterfaceGuid,
|
|
IN LPCWSTR pszDeviceID,
|
|
IN ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the RPC server entry point for the CM_Get_Device_Interface_List_Size
|
|
routine. It returns the size (in chars) of a multi_sz interface device list.
|
|
|
|
Arguments:
|
|
|
|
hBinding RPC binding handle, not used.
|
|
|
|
pulLen Supplies the address of a variable that, upon successful
|
|
return, receives the the size of buffer required to hold
|
|
the multi_sz interface device list.
|
|
|
|
InterfaceGuid Supplies the interface class GUID.
|
|
|
|
pszDeviceID Supplies the device instance string.
|
|
|
|
ulFlags Flags specifying which device interfaces to return.
|
|
Currently, may be either:
|
|
CM_GET_DEVICE_INTERFACE_LIST_PRESENT, or
|
|
CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is CR_SUCCESS.
|
|
If the function fails, the return value is one of the following:
|
|
CR_INVALID_FLAG,
|
|
CR_INVALID_POINTER, or
|
|
CR_REGISTRY_ERROR
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PLUGPLAY_CONTROL_INTERFACE_LIST_DATA ControlData;
|
|
|
|
UNREFERENCED_PARAMETER(hBinding);
|
|
|
|
try {
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if (INVALID_FLAGS(ulFlags, CM_GET_DEVICE_INTERFACE_LIST_BITS)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (!ARGUMENT_PRESENT(InterfaceGuid) ||
|
|
!ARGUMENT_PRESENT(pulLen)) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (!IsLegalDeviceId(pszDeviceID)) {
|
|
Status = CR_INVALID_DEVNODE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Initialize the returned output length
|
|
//
|
|
*pulLen = 0;
|
|
|
|
//
|
|
// Fill in a control structure for the device list info.
|
|
//
|
|
|
|
//
|
|
// Note that InterfaceGuid was already validated above because this
|
|
// buffer is required for the PlugPlayControlGetInterfaceDeviceList
|
|
// control, and is probed unconditionally by kernel-mode. Better to
|
|
// fail the call above with a useful status than to return the generic
|
|
// CR_FAILURE after an exception/error from kernel-mode, below.
|
|
//
|
|
|
|
memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_INTERFACE_LIST_DATA));
|
|
RtlInitUnicodeString(&ControlData.DeviceInstance, pszDeviceID);
|
|
ControlData.InterfaceGuid = InterfaceGuid;
|
|
ControlData.InterfaceList = NULL;
|
|
ControlData.InterfaceListSize = 0;
|
|
|
|
if (ulFlags == CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES) {
|
|
ControlData.Flags = 0x1; // DEVICE_INTERFACE_INCLUDE_NONACTIVE (ntos\inc\pnp.h)
|
|
} else {
|
|
ControlData.Flags = 0;
|
|
}
|
|
|
|
//
|
|
// Call kernel-mode to get the device interface list size.
|
|
//
|
|
|
|
ntStatus = NtPlugPlayControl(PlugPlayControlGetInterfaceDeviceList,
|
|
&ControlData,
|
|
sizeof(ControlData));
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
*pulLen = ControlData.InterfaceListSize;
|
|
} else {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PNP_GetInterfaceDeviceListSize
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PNP_RegisterDeviceClassAssociation(
|
|
IN handle_t hBinding,
|
|
IN LPCWSTR pszDeviceID,
|
|
IN LPGUID InterfaceGuid,
|
|
IN LPCWSTR pszReference OPTIONAL,
|
|
OUT PWSTR pszSymLink,
|
|
IN OUT PULONG pulLength,
|
|
IN OUT PULONG pulTransferLen,
|
|
IN ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the RPC server entry point for the CM_Register_Device_Interface
|
|
routine. It registers a device interface for the specified device and device
|
|
interface class, and returns the symbolic link name for the device interface.
|
|
|
|
Arguments:
|
|
|
|
hBinding RPC binding handle.
|
|
|
|
pszDeviceID Supplies the device instance string.
|
|
|
|
InterfaceGuid Supplies the interface class guid.
|
|
|
|
pszReference Optionally, supplies the reference string name.
|
|
|
|
pszSymLink Receives the symbolic link name.
|
|
|
|
pulLength Parameter passed in by caller, on entry it contains
|
|
the size, in bytes, of the buffer, on exit it contains
|
|
either the amount of data copied to the caller's buffer
|
|
(if a transfer occured) or else the size of buffer
|
|
required to hold the property data.
|
|
|
|
pulTransferLen Used by stubs, indicates how much data to copy back
|
|
into user buffer.
|
|
|
|
ulFlags Not used, must be zero.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is CR_SUCCESS.
|
|
If the function fails, the return value is one of the following:
|
|
CR_ACCESS_DENIED,
|
|
CR_INVALID_FLAG,
|
|
CR_INVALID_POINTER, or
|
|
CR_REGISTRY_ERROR
|
|
|
|
Remarks:
|
|
|
|
The pointer passed in as the pulTransferLen argument must *NOT* be the same
|
|
as the pointer passed in for the pulLength argument.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PLUGPLAY_CONTROL_CLASS_ASSOCIATION_DATA ControlData;
|
|
|
|
try {
|
|
//
|
|
// Verify client "write" access
|
|
//
|
|
if (!VerifyClientAccess(hBinding,
|
|
PLUGPLAY_WRITE)) {
|
|
Status = CR_ACCESS_DENIED;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Validate parameters
|
|
//
|
|
ASSERT(pulTransferLen != pulLength);
|
|
|
|
if (!ARGUMENT_PRESENT(InterfaceGuid) ||
|
|
!ARGUMENT_PRESENT(pszSymLink) ||
|
|
!ARGUMENT_PRESENT(pulTransferLen) ||
|
|
!ARGUMENT_PRESENT(pulLength)) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (INVALID_FLAGS(ulFlags, 0)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (!IsLegalDeviceId(pszDeviceID)) {
|
|
Status = CR_INVALID_DEVNODE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Make sure we use no more than either what the caller specified or
|
|
// what was allocated by RPC, based on the transfer length.
|
|
//
|
|
*pulLength = min(*pulLength, *pulTransferLen);
|
|
|
|
//
|
|
// Fill in a control structure for the device list info.
|
|
//
|
|
|
|
//
|
|
// Note that InterfaceGuid was already validated above because this
|
|
// buffer is required for the PlugPlayControlDeviceClassAssociation
|
|
// control, for Registration only.
|
|
//
|
|
|
|
memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_CLASS_ASSOCIATION_DATA));
|
|
RtlInitUnicodeString(&ControlData.DeviceInstance, pszDeviceID);
|
|
RtlInitUnicodeString(&ControlData.Reference, pszReference);
|
|
ControlData.InterfaceGuid = InterfaceGuid;
|
|
ControlData.Register = TRUE;
|
|
ControlData.SymLink = pszSymLink;
|
|
ControlData.SymLinkLength = *pulLength;
|
|
|
|
//
|
|
// Call kernel-mode to register the device association.
|
|
//
|
|
|
|
ntStatus = NtPlugPlayControl(PlugPlayControlDeviceClassAssociation,
|
|
&ControlData,
|
|
sizeof(ControlData));
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
*pulLength = ControlData.SymLinkLength;
|
|
*pulTransferLen = *pulLength;
|
|
} else if (ntStatus == STATUS_BUFFER_TOO_SMALL) {
|
|
*pulLength = ControlData.SymLinkLength;
|
|
Status = CR_BUFFER_SMALL;
|
|
} else {
|
|
*pulLength = 0;
|
|
Status = MapNtStatusToCmError(ntStatus);
|
|
}
|
|
|
|
Clean0:
|
|
|
|
//
|
|
// Initialize output parameters
|
|
//
|
|
if ((Status != CR_SUCCESS) &&
|
|
ARGUMENT_PRESENT(pszSymLink) &&
|
|
ARGUMENT_PRESENT(pulTransferLen) &&
|
|
(*pulTransferLen > 0)) {
|
|
*pszSymLink = L'\0';
|
|
*pulTransferLen = 1;
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PNP_RegisterDeviceClassAssociation
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PNP_UnregisterDeviceClassAssociation(
|
|
IN handle_t hBinding,
|
|
IN LPCWSTR pszInterfaceDevice,
|
|
IN ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the RPC server entry point for the CM_Unregister_Device_Interface
|
|
routine.
|
|
|
|
Arguments:
|
|
|
|
hBinding RPC binding handle.
|
|
|
|
pszInterfaceDevice Specifies the interface device to unregister
|
|
|
|
ulFlags Not used, must be zero.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is CR_SUCCESS.
|
|
If the function fails, the return value is one of the following:
|
|
CR_ACCESS_DENIED,
|
|
CR_DEVICE_INTERFACE_ACTIVE, or
|
|
CR_FAILURE.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PLUGPLAY_CONTROL_CLASS_ASSOCIATION_DATA ControlData;
|
|
|
|
try {
|
|
//
|
|
// Verify client "write" access
|
|
//
|
|
if (!VerifyClientAccess(hBinding,
|
|
PLUGPLAY_WRITE)) {
|
|
Status = CR_ACCESS_DENIED;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if (!ARGUMENT_PRESENT(pszInterfaceDevice)) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (INVALID_FLAGS(ulFlags, 0)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Fill in a control structure for the device list info.
|
|
//
|
|
|
|
//
|
|
// Note that the DeviceInstance, Reference, and InterfaceGuid members
|
|
// are not required for the PlugPlayControlDeviceClassAssociation
|
|
// control, for unregistration only. Only the symbolic link name is
|
|
// required to unregister the device interface.
|
|
//
|
|
|
|
memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_CLASS_ASSOCIATION_DATA));
|
|
ControlData.Register = FALSE;
|
|
ControlData.SymLink = (LPWSTR)pszInterfaceDevice;
|
|
ControlData.SymLinkLength = lstrlen(pszInterfaceDevice) + 1;
|
|
|
|
//
|
|
// Call kernel-mode to deregister the device association.
|
|
//
|
|
|
|
ntStatus = NtPlugPlayControl(PlugPlayControlDeviceClassAssociation,
|
|
&ControlData,
|
|
sizeof(ControlData));
|
|
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
if (ntStatus == STATUS_ACCESS_DENIED) {
|
|
Status = CR_DEVICE_INTERFACE_ACTIVE;
|
|
} else {
|
|
Status = MapNtStatusToCmError(ntStatus);
|
|
}
|
|
}
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PNP_UnregisterDeviceClassAssociation
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Private export for the Service Controller
|
|
//-------------------------------------------------------------------
|
|
|
|
|
|
|
|
CONFIGRET
|
|
DeleteServicePlugPlayRegKeys(
|
|
IN LPWSTR pszService
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called directly and privately by the Service Controller
|
|
whenever a service has been deleted. It allows the SCM to delete any Plug
|
|
and Play registry keys that may have been created for a service.
|
|
|
|
Arguments:
|
|
|
|
pszService - Specifies the name of the service.
|
|
|
|
Return Value:
|
|
|
|
Return CR_SUCCESS if the function succeeds, otherwise it returns one
|
|
of the CR_* errors.
|
|
|
|
Note:
|
|
|
|
This routine is privately exported, and is to be called only by the
|
|
Service Control Manager, during service deletion.
|
|
|
|
--*/
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
ULONG ulSize, ulFlags, ulHardwareProfile, ulPass;
|
|
LPWSTR pDeviceList = NULL, pDeviceID;
|
|
WCHAR szParentKey[MAX_CM_PATH], szChildKey[MAX_DEVICE_ID_LEN];
|
|
BOOL RootEnumerationRequired = FALSE;
|
|
ULONG ulProblem, ulStatus;
|
|
|
|
try {
|
|
//
|
|
// validate parameters
|
|
//
|
|
if (!ARGUMENT_PRESENT(pszService)) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// retreive the maximum size required for a buffer to receive the list
|
|
// of devices that this service is controlling
|
|
//
|
|
Status = PNP_GetDeviceListSize(NULL,
|
|
pszService,
|
|
&ulSize,
|
|
CM_GETIDLIST_FILTER_SERVICE);
|
|
|
|
if (Status != CR_SUCCESS) {
|
|
goto Clean0;
|
|
}
|
|
|
|
pDeviceList = HeapAlloc(ghPnPHeap, 0, ulSize * sizeof(WCHAR));
|
|
if (pDeviceList == NULL) {
|
|
Status = CR_OUT_OF_MEMORY;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// retrieve the list of devices that this service is controlling, make
|
|
// sure that we don't generate one if none already exist
|
|
//
|
|
Status = PNP_GetDeviceList(NULL,
|
|
pszService,
|
|
pDeviceList,
|
|
&ulSize,
|
|
CM_GETIDLIST_FILTER_SERVICE |
|
|
CM_GETIDLIST_DONOTGENERATE);
|
|
|
|
if (Status != CR_SUCCESS) {
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// delete the registry keys for each device instance for this service
|
|
//
|
|
for (pDeviceID = pDeviceList;
|
|
*pDeviceID;
|
|
pDeviceID += lstrlen(pDeviceID) + 1) {
|
|
|
|
for (ulPass = 0; ulPass < 4; ulPass++) {
|
|
//
|
|
// delete the registry keys for all hardware profiles, followed
|
|
// by the system global registry keys
|
|
//
|
|
ulFlags = 0;
|
|
ulHardwareProfile = 0;
|
|
|
|
if (ulPass == 0) {
|
|
ulFlags = CM_REGISTRY_HARDWARE | CM_REGISTRY_CONFIG;
|
|
ulHardwareProfile = 0xFFFFFFFF;
|
|
} else if (ulPass == 1) {
|
|
ulFlags = CM_REGISTRY_SOFTWARE | CM_REGISTRY_CONFIG;
|
|
ulHardwareProfile = 0xFFFFFFFF;
|
|
} else if (ulPass == 2) {
|
|
ulFlags = CM_REGISTRY_HARDWARE;
|
|
ulHardwareProfile = 0;
|
|
} else if (ulPass == 3) {
|
|
ulFlags = CM_REGISTRY_SOFTWARE;
|
|
ulHardwareProfile = 0;
|
|
}
|
|
|
|
//
|
|
// form the registry path based on the device id and the flags
|
|
//
|
|
if (GetDevNodeKeyPath(NULL,
|
|
pDeviceID,
|
|
ulFlags,
|
|
ulHardwareProfile,
|
|
szParentKey,
|
|
SIZECHARS(szParentKey),
|
|
szChildKey,
|
|
SIZECHARS(szChildKey),
|
|
FALSE) == CR_SUCCESS) {
|
|
|
|
//
|
|
// remove the specified registry key
|
|
//
|
|
PNP_DeleteRegistryKey(
|
|
NULL, // rpc binding handle (NULL)
|
|
pDeviceID, // device id
|
|
szParentKey, // parent of key to delete
|
|
szChildKey, // key to delete
|
|
ulHardwareProfile); // flags, not used
|
|
}
|
|
}
|
|
|
|
//
|
|
// Uninstall the device instance (see also PNP_UninstallDevInst).
|
|
//
|
|
|
|
//------------------------------------------------------------------
|
|
// Uninstall deletes instance key (and all subkeys) for all
|
|
// the hardware keys (this means the main Enum branch, the
|
|
// config specific keys under HKLM, and the Enum branch under
|
|
// HKCU). In the case of the user hardware keys (under HKCU),
|
|
// I delete those whether it's a phantom or not, but since
|
|
// I can't access the user key from the service side, I have
|
|
// to do that part on the client side. For the main hw Enum key
|
|
// and the config specific hw keys, I only delete them outright
|
|
// if they are phantoms. If not a phantom, then I just make the
|
|
// device instance volatile (by saving the original key, deleting
|
|
// old key, creating new volatile key and restoring the old
|
|
// contents) so at least it will go away during the next boot
|
|
//------------------------------------------------------------------
|
|
|
|
if ((GetDeviceStatus(pDeviceID, &ulStatus, &ulProblem) == CR_SUCCESS) &&
|
|
(ulStatus & DN_DRIVER_LOADED)) {
|
|
|
|
//-------------------------------------------------------------
|
|
// device is not a phantom
|
|
//-------------------------------------------------------------
|
|
|
|
if ((ulStatus & DN_ROOT_ENUMERATED) &&
|
|
!(ulStatus & DN_DISABLEABLE)) {
|
|
//
|
|
// if a device is root enumerated, but not disableable, it is not uninstallable
|
|
//
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_REGISTRY,
|
|
"UMPNPMGR: DeleteServicePlugPlayRegKeys: "
|
|
"failed uninstall of %ws (this root device is not disableable)\n",
|
|
pDeviceID));
|
|
} else {
|
|
//
|
|
// do the volatile-copy-thing
|
|
//
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_REGISTRY,
|
|
"UMPNPMGR: DeleteServicePlugPlayRegKeys: "
|
|
"doing volatile key thing on %ws\n",
|
|
pDeviceID));
|
|
|
|
UninstallRealDevice(pDeviceID);
|
|
}
|
|
|
|
} else {
|
|
|
|
//-------------------------------------------------------------
|
|
// device is a phantom so actually delete it
|
|
//-------------------------------------------------------------
|
|
|
|
if (UninstallPhantomDevice(pDeviceID) != CR_SUCCESS) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// if it is a root enumerated device, we need to reenumerate the
|
|
// root (if not planning to do so already) so that the PDO will
|
|
// go away, otherwise a new device could be created and the root
|
|
// enumerator would get very confused.
|
|
//
|
|
if ((!RootEnumerationRequired) &&
|
|
(IsDeviceRootEnumerated(pDeviceID))) {
|
|
RootEnumerationRequired = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now that we're done processing all devices, see if we need to
|
|
// reenumerate the root.
|
|
//
|
|
if (RootEnumerationRequired) {
|
|
|
|
//
|
|
// Reenumerate the root devnode asynchronously so that the service
|
|
// controller does not block waiting for this routine to complete!!
|
|
// (If we were processing device events at this time, the SCM would
|
|
// be blocked here and not be able to deliver any events for us.
|
|
// That would stall the event queue, preventing a synchronous device
|
|
// enumeration from completing).
|
|
//
|
|
|
|
ReenumerateDevInst(pszRegRootEnumerator,
|
|
FALSE,
|
|
CM_REENUMERATE_ASYNCHRONOUS);
|
|
}
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
//
|
|
// force compiler to respect statement ordering w.r.t. assignments
|
|
// for these variables...
|
|
//
|
|
pDeviceList = pDeviceList;
|
|
}
|
|
|
|
if (pDeviceList) {
|
|
HeapFree(ghPnPHeap, 0, pDeviceList);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // DeleteServicePlugPlayRegKeys
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Private utility routines
|
|
//-------------------------------------------------------------------
|
|
|
|
|
|
|
|
LPWSTR
|
|
MapPropertyToString(
|
|
ULONG ulProperty
|
|
)
|
|
{
|
|
switch (ulProperty) {
|
|
|
|
case CM_DRP_DEVICEDESC:
|
|
return pszRegValueDeviceDesc;
|
|
|
|
case CM_DRP_HARDWAREID:
|
|
return pszRegValueHardwareIDs;
|
|
|
|
case CM_DRP_COMPATIBLEIDS:
|
|
return pszRegValueCompatibleIDs;
|
|
|
|
case CM_DRP_SERVICE:
|
|
return pszRegValueService;
|
|
|
|
case CM_DRP_CLASS:
|
|
return pszRegValueClass;
|
|
|
|
case CM_DRP_CLASSGUID:
|
|
return pszRegValueClassGuid;
|
|
|
|
case CM_DRP_DRIVER:
|
|
return pszRegValueDriver;
|
|
|
|
case CM_DRP_CONFIGFLAGS:
|
|
return pszRegValueConfigFlags;
|
|
|
|
case CM_DRP_MFG:
|
|
return pszRegValueMfg;
|
|
|
|
case CM_DRP_FRIENDLYNAME:
|
|
return pszRegValueFriendlyName;
|
|
|
|
case CM_DRP_LOCATION_INFORMATION:
|
|
return pszRegValueLocationInformation;
|
|
|
|
case CM_DRP_CAPABILITIES:
|
|
return pszRegValueCapabilities;
|
|
|
|
case CM_DRP_UI_NUMBER:
|
|
return pszRegValueUiNumber;
|
|
|
|
case CM_DRP_UPPERFILTERS:
|
|
return pszRegValueUpperFilters;
|
|
|
|
case CM_DRP_LOWERFILTERS:
|
|
return pszRegValueLowerFilters;
|
|
|
|
case CM_DRP_SECURITY: // and CM_CRP_SECURITY
|
|
return pszRegValueSecurity;
|
|
|
|
case CM_DRP_DEVTYPE: // and CM_CRP_DEVTYPE
|
|
return pszRegValueDevType;
|
|
|
|
case CM_DRP_EXCLUSIVE: // and CM_CRP_EXCLUSIVE
|
|
return pszRegValueExclusive;
|
|
|
|
case CM_DRP_CHARACTERISTICS: // and CM_CRP_CHARACTERISTICS
|
|
return pszRegValueCharacteristics;
|
|
|
|
case CM_DRP_UI_NUMBER_DESC_FORMAT:
|
|
return pszRegValueUiNumberDescFormat;
|
|
|
|
case CM_DRP_REMOVAL_POLICY_OVERRIDE:
|
|
return pszRegValueRemovalPolicyOverride;
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
} // MapPropertyToString
|
|
|
|
|
|
|
|
ULONG
|
|
MapPropertyToNtProperty(
|
|
ULONG ulProperty
|
|
)
|
|
{
|
|
switch (ulProperty) {
|
|
|
|
case CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME:
|
|
return PNP_PROPERTY_PDONAME;
|
|
|
|
case CM_DRP_BUSTYPEGUID:
|
|
return PNP_PROPERTY_BUSTYPEGUID;
|
|
|
|
case CM_DRP_LEGACYBUSTYPE:
|
|
return PNP_PROPERTY_LEGACYBUSTYPE;
|
|
|
|
case CM_DRP_BUSNUMBER:
|
|
return PNP_PROPERTY_BUSNUMBER;
|
|
|
|
case CM_DRP_ADDRESS:
|
|
return PNP_PROPERTY_ADDRESS;
|
|
|
|
case CM_DRP_DEVICE_POWER_DATA:
|
|
return PNP_PROPERTY_POWER_DATA;
|
|
|
|
case CM_DRP_REMOVAL_POLICY:
|
|
return PNP_PROPERTY_REMOVAL_POLICY;
|
|
|
|
case CM_DRP_REMOVAL_POLICY_HW_DEFAULT:
|
|
return PNP_PROPERTY_REMOVAL_POLICY_HARDWARE_DEFAULT;
|
|
|
|
case CM_DRP_REMOVAL_POLICY_OVERRIDE:
|
|
return PNP_PROPERTY_REMOVAL_POLICY_OVERRIDE;
|
|
|
|
case CM_DRP_INSTALL_STATE:
|
|
return PNP_PROPERTY_INSTALL_STATE;
|
|
|
|
case CM_DRP_LOCATION_PATHS:
|
|
return PNP_PROPERTY_LOCATION_PATHS;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
} // MapPropertyToNtProperty
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PNP_GetCustomDevProp(
|
|
IN handle_t hBinding,
|
|
IN LPCWSTR pDeviceID,
|
|
IN LPCWSTR CustomPropName,
|
|
OUT PULONG pulRegDataType,
|
|
OUT LPBYTE Buffer,
|
|
OUT PULONG pulTransferLen,
|
|
IN OUT PULONG pulLength,
|
|
IN ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the RPC server entry point for the CM_Get_DevNode_Custom_Property
|
|
routine.
|
|
|
|
Arguments:
|
|
|
|
hBinding RPC binding handle, not used.
|
|
|
|
pDeviceID Supplies a string containing the device instance
|
|
whose property will be read from.
|
|
|
|
CustomPropName Supplies a string identifying the name of the property
|
|
(registry value entry name) to be retrieved.
|
|
|
|
pulRegDataType Supplies the address of a variable that will receive
|
|
the registry data type for this property (i.e., the REG_*
|
|
constants).
|
|
|
|
Buffer Supplies the address of the buffer that receives the
|
|
registry data. If the caller is simply retrieving the
|
|
required size, pulLength will be zero.
|
|
|
|
pulTransferLen Used by stubs, indicates how much data to copy back
|
|
into user buffer.
|
|
|
|
pulLength Parameter passed in by caller, on entry it contains
|
|
the size, in bytes, of the buffer, on exit it contains
|
|
either the amount of data copied to the caller's buffer
|
|
(if a transfer occurred) or else the size of buffer
|
|
required to hold the property data.
|
|
|
|
ulFlags May be a combination of the following values:
|
|
|
|
CM_CUSTOMDEVPROP_MERGE_MULTISZ : merge the
|
|
devnode-specific REG_SZ or REG_MULTI_SZ property (if
|
|
present) with the per-hardware-id REG_SZ or REG_MULTI_SZ
|
|
property (if present). The result will always be a
|
|
REG_MULTI_SZ.
|
|
|
|
Note: REG_EXPAND_SZ data is not merged in this manner, as
|
|
there is no way to indicate that the resultant list needs
|
|
environment variable expansion (i.e., there's no such
|
|
registry datatype as REG_EXPAND_MULTI_SZ).
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is CR_SUCCESS.
|
|
If the function fails, the return value is one of the following:
|
|
CR_INVALID_DEVNODE,
|
|
CR_REGISTRY_ERROR,
|
|
CR_BUFFER_SMALL,
|
|
CR_NO_SUCH_VALUE, or
|
|
CR_FAILURE.
|
|
|
|
Remarks:
|
|
|
|
The pointer passed in as the pulTransferLen argument must *NOT* be the same
|
|
as the pointer passed in for the pulLength argument.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
LONG RegStatus;
|
|
HKEY hDevKey = NULL;
|
|
HKEY hDevParamsKey = NULL;
|
|
HKEY hPerHwIdSubKey = NULL;
|
|
WCHAR PerHwIdSubkeyName[MAX_DEVNODE_ID_LEN];
|
|
ULONG RequiredSize = 0;
|
|
FILETIME CacheDate, LastUpdateTime;
|
|
DWORD RegDataType, RegDataSize;
|
|
LPBYTE PerHwIdBuffer = NULL;
|
|
DWORD PerHwIdBufferLen = 0;
|
|
LPWSTR pCurId;
|
|
BOOL MergeMultiSzResult = FALSE;
|
|
|
|
UNREFERENCED_PARAMETER(hBinding);
|
|
|
|
try {
|
|
//
|
|
// Validate parameters
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(pulTransferLen) ||
|
|
!ARGUMENT_PRESENT(pulLength)) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// We should never have both arguments pointing to the same memory...
|
|
//
|
|
ASSERT(pulTransferLen != pulLength);
|
|
|
|
//
|
|
// ...but if we do, fail the call.
|
|
//
|
|
if (pulTransferLen == pulLength) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
*pulTransferLen = 0;
|
|
|
|
if (INVALID_FLAGS(ulFlags, CM_CUSTOMDEVPROP_BITS)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
if(!IsLegalDeviceId(pDeviceID)) {
|
|
Status = CR_INVALID_DEVNODE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// First, open the device instance key. We'll then open the "Device
|
|
// Parameters" subkey off of that. We do this in two steps, because
|
|
// we're likely to need a handle to the device instance key in order
|
|
// to track down the per-hw-id property.
|
|
//
|
|
if(ERROR_SUCCESS != RegOpenKeyEx(ghEnumKey,
|
|
pDeviceID,
|
|
0,
|
|
KEY_READ | KEY_WRITE,
|
|
&hDevKey)) {
|
|
|
|
hDevKey = NULL; // ensure hDevKey is still NULL so we
|
|
// won't erroneously try to close it.
|
|
|
|
RequiredSize = 0; // no size info for caller
|
|
|
|
Status = CR_REGISTRY_ERROR;
|
|
goto Clean0;
|
|
}
|
|
|
|
if(ERROR_SUCCESS == RegOpenKeyEx(hDevKey,
|
|
pszRegKeyDeviceParam,
|
|
0,
|
|
KEY_READ,
|
|
&hDevParamsKey)) {
|
|
|
|
RequiredSize = *pulLength;
|
|
|
|
RegStatus = RegQueryValueEx(hDevParamsKey,
|
|
CustomPropName,
|
|
NULL,
|
|
pulRegDataType,
|
|
Buffer,
|
|
&RequiredSize
|
|
);
|
|
|
|
if(RegStatus == ERROR_SUCCESS) {
|
|
//
|
|
// We need to distinguish between the case where we succeeded
|
|
// because the caller supplied a zero-length buffer (we call it
|
|
// CR_BUFFER_SMALL) and the "real" success case.
|
|
//
|
|
if((*pulLength == 0) && (RequiredSize != 0)) {
|
|
Status = CR_BUFFER_SMALL;
|
|
}
|
|
|
|
} else {
|
|
|
|
if(RegStatus == ERROR_MORE_DATA) {
|
|
Status = CR_BUFFER_SMALL;
|
|
} else {
|
|
RequiredSize = 0;
|
|
Status = CR_NO_SUCH_VALUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// At this point, Status is one of the following:
|
|
//
|
|
// CR_SUCCESS : we found the value and our buffer was
|
|
// sufficiently-sized to hold it,
|
|
// CR_BUFFER_SMALL : we found the value and our buffer wasn't
|
|
// large enough to hold it, or
|
|
// CR_NO_SUCH_VALUE : we didn't find the value.
|
|
//
|
|
// If we found a value (whether or not our buffer was large enough
|
|
// to hold it), we're done, except for cases where the caller
|
|
// has asked us to append the per-hw-id string(s) with the
|
|
// per-devnode string(s).
|
|
//
|
|
if(Status == CR_NO_SUCH_VALUE) {
|
|
//
|
|
// No devnode-specific property, so we use the same buffer and
|
|
// length for retrieval of per-hw-id property...
|
|
//
|
|
PerHwIdBuffer = Buffer;
|
|
PerHwIdBufferLen = *pulLength;
|
|
|
|
} else {
|
|
//
|
|
// Figure out if we need to worry about appending results
|
|
// together into a multi-sz list...
|
|
//
|
|
if((ulFlags & CM_CUSTOMDEVPROP_MERGE_MULTISZ) &&
|
|
((*pulRegDataType == REG_MULTI_SZ) || (*pulRegDataType == REG_SZ))) {
|
|
|
|
MergeMultiSzResult = TRUE;
|
|
|
|
//
|
|
// Ensure that the size of our string(s) buffer is at least
|
|
// one Unicode character. If we have a buffer of one
|
|
// character, ensure that character is a null.
|
|
//
|
|
if(RequiredSize < sizeof(WCHAR)) {
|
|
RequiredSize = sizeof(WCHAR);
|
|
if(RequiredSize > *pulLength) {
|
|
Status = CR_BUFFER_SMALL;
|
|
} else {
|
|
ASSERT(Status == CR_SUCCESS);
|
|
*(PWSTR)Buffer = L'\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!MergeMultiSzResult) {
|
|
//
|
|
// We're outta here!
|
|
//
|
|
if(Status == CR_SUCCESS) {
|
|
//
|
|
// We have data to transfer.
|
|
//
|
|
*pulTransferLen = RequiredSize;
|
|
}
|
|
|
|
goto Clean0;
|
|
|
|
} else {
|
|
//
|
|
// We're supposed to merge our per-devnode string(s) with
|
|
// any per-hw-id string(s) we find. Make sure our buffer
|
|
// and length reflect a properly-formatted multi-sz list,
|
|
// then setup our per-hw-id buffer info so that we'll
|
|
// append to this list later on...
|
|
//
|
|
if(Status == CR_BUFFER_SMALL) {
|
|
//
|
|
// We won't even try to retrieve any actual data from
|
|
// a per-hw-id key (all we'll get is the additional
|
|
// size requirement).
|
|
//
|
|
PerHwIdBuffer = NULL;
|
|
PerHwIdBufferLen = 0;
|
|
|
|
if(*pulRegDataType == REG_SZ) {
|
|
//
|
|
// The data we retrieved from the devnode's "Device
|
|
// Parameters" subkey was a REG_SZ. Add one
|
|
// character width to the required length to
|
|
// reflect the size of the string after conversion
|
|
// to multi-sz (unless the size is 1 character,
|
|
// indicating an empty string, which is also the
|
|
// size of an empty multi-sz list).
|
|
//
|
|
if(RequiredSize > sizeof(WCHAR)) {
|
|
RequiredSize += sizeof(WCHAR);
|
|
}
|
|
|
|
*pulRegDataType = REG_MULTI_SZ;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// We actually retrieved a REG_SZ or REG_MULTI_SZ value
|
|
// into our caller-supplied buffer. Ensure that the
|
|
// string(s) contained therein is(are) in proper
|
|
// multi-sz format, and that the size is correct.
|
|
//
|
|
if(*pulRegDataType == REG_SZ) {
|
|
|
|
RegDataSize = lstrlen((LPWSTR)Buffer) + 1;
|
|
|
|
if((RegDataSize * sizeof(WCHAR)) > RequiredSize) {
|
|
//
|
|
// The string we retrieved is longer than the
|
|
// buffer--this indicates the string wasn't
|
|
// properly null-terminated. Discard this
|
|
// string.
|
|
//
|
|
Status = CR_NO_SUCH_VALUE;
|
|
RequiredSize = 0;
|
|
PerHwIdBuffer = Buffer;
|
|
PerHwIdBufferLen = *pulLength;
|
|
|
|
} else {
|
|
//
|
|
// The string was large enough to fit in the
|
|
// buffer. Add another null character to
|
|
// turn this into a multi-sz (if there's room).
|
|
// (Again, we don't need to do increase the
|
|
// length if this is an empty string.)
|
|
//
|
|
if(RegDataSize == 1) {
|
|
RequiredSize = sizeof(WCHAR);
|
|
PerHwIdBuffer = Buffer;
|
|
PerHwIdBufferLen = *pulLength;
|
|
//
|
|
// Assuming no per-hw-id data is found
|
|
// later, this is the size of the data
|
|
// we'll be handing back to the caller.
|
|
//
|
|
*pulTransferLen = RequiredSize;
|
|
|
|
} else {
|
|
RequiredSize = (RegDataSize + 1) * sizeof(WCHAR);
|
|
|
|
if(RequiredSize > *pulLength) {
|
|
//
|
|
// Oops--while the string fits nicely into
|
|
// the caller-supplied buffer, adding an
|
|
// extra null char pushes it over the limit.
|
|
// Turn this into a CR_BUFFER_SMALL case.
|
|
//
|
|
Status = CR_BUFFER_SMALL;
|
|
PerHwIdBuffer = NULL;
|
|
PerHwIdBufferLen = 0;
|
|
|
|
} else {
|
|
//
|
|
// We've got room to add the extra null
|
|
// character. Do so, and setup our
|
|
// per-hw-id buffer to start at the end of
|
|
// our existing (single string) list...
|
|
//
|
|
PerHwIdBuffer =
|
|
(LPBYTE)((LPWSTR)Buffer + RegDataSize);
|
|
|
|
PerHwIdBufferLen =
|
|
*pulLength - (RegDataSize * sizeof(WCHAR));
|
|
|
|
*((LPWSTR)PerHwIdBuffer) = L'\0';
|
|
|
|
//
|
|
// Assuming no per-hw-id data is found
|
|
// later, this is the size of the data
|
|
// we'll be handing back to the caller.
|
|
//
|
|
*pulTransferLen = RequiredSize;
|
|
}
|
|
}
|
|
|
|
*pulRegDataType = REG_MULTI_SZ;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// We retrieved a multi-sz list. Step through it
|
|
// to find the end of the list.
|
|
//
|
|
RegDataSize = 0;
|
|
|
|
for(pCurId = (LPWSTR)Buffer;
|
|
*pCurId;
|
|
pCurId = (LPWSTR)(Buffer + RegDataSize)) {
|
|
|
|
RegDataSize +=
|
|
(lstrlen(pCurId) + 1) * sizeof(WCHAR);
|
|
|
|
if(RegDataSize < RequiredSize) {
|
|
//
|
|
// This string fits in the buffer, and
|
|
// there's still space left over (i.e., for
|
|
// at least a terminating null). Move on
|
|
// to the next string in the list.
|
|
//
|
|
continue;
|
|
|
|
} else if(RegDataSize > RequiredSize) {
|
|
//
|
|
// This string extends past the end of the
|
|
// buffer, indicating that it wasn't
|
|
// properly null-terminated. This could've
|
|
// caused an exception, in which case we'd
|
|
// have discarded any contents of this
|
|
// value. For consistency, we'll discard
|
|
// the contents anyway. (Note: a multi-sz
|
|
// list that simply ommitted the final
|
|
// terminating NULL will not fall into this
|
|
// category--we deal with that correctly
|
|
// and "fix it up".)
|
|
//
|
|
Status = CR_NO_SUCH_VALUE;
|
|
RequiredSize = 0;
|
|
PerHwIdBuffer = Buffer;
|
|
PerHwIdBufferLen = *pulLength;
|
|
break;
|
|
|
|
} else {
|
|
//
|
|
// This string exactly fits into the
|
|
// remaining buffer space, indicating that
|
|
// the multi-sz list wasn't properly
|
|
// double-null terminated. We'll go ahead
|
|
// and do that now...
|
|
//
|
|
RequiredSize = RegDataSize + sizeof(WCHAR);
|
|
|
|
if(RequiredSize > *pulLength) {
|
|
//
|
|
// Oops--while the string fits nicely
|
|
// into the caller-supplied buffer,
|
|
// adding an extra null char pushes it
|
|
// over the limit. Turn this into a
|
|
// CR_BUFFER_SMALL case.
|
|
//
|
|
Status = CR_BUFFER_SMALL;
|
|
PerHwIdBuffer = NULL;
|
|
PerHwIdBufferLen = 0;
|
|
|
|
} else {
|
|
//
|
|
// We've got room to add the extra null
|
|
// character. Do so, and setup our
|
|
// per-hw-id buffer to start at the end
|
|
// of our existing list...
|
|
//
|
|
PerHwIdBuffer = Buffer + RegDataSize;
|
|
|
|
PerHwIdBufferLen =
|
|
*pulLength - RegDataSize;
|
|
|
|
*((LPWSTR)PerHwIdBuffer) = L'\0';
|
|
|
|
//
|
|
// Assuming no per-hw-id data is found
|
|
// later, this is the size of the data
|
|
// we'll be handing back to the caller.
|
|
//
|
|
*pulTransferLen = RequiredSize;
|
|
}
|
|
|
|
//
|
|
// We've reached the end of the list, so we
|
|
// can break out of the loop.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We've now processed all (valid) strings in the
|
|
// multi-sz list we retrieved. If there was a
|
|
// problem (either unterminated string or
|
|
// unterminated list), we fixed that up (and
|
|
// adjusted RequiredSize accordingly). However,
|
|
// if the list was valid, we need to compute
|
|
// RequiredSize (e.g., the buffer might've been
|
|
// larger than the multi-sz list).
|
|
//
|
|
// We can recognize a properly-formatted multi-sz
|
|
// list, because that's the only time we'd have
|
|
// exited the loop with pCurId pointing to a null
|
|
// character...
|
|
//
|
|
if(!*pCurId) {
|
|
ASSERT(RequiredSize >= (RegDataSize + sizeof(WCHAR)));
|
|
RequiredSize = RegDataSize + sizeof(WCHAR);
|
|
|
|
PerHwIdBuffer = Buffer + RegDataSize;
|
|
PerHwIdBufferLen = *pulLength - RegDataSize;
|
|
|
|
//
|
|
// Assuming no per-hw-id data is found later,
|
|
// this is the size of the data we'll be
|
|
// handing back to the caller.
|
|
//
|
|
*pulTransferLen = RequiredSize;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// We couldn't open the devnode's "Device Parameters" subkey.
|
|
// Ensure hDevParamsKey is still NULL so we won't erroneously try
|
|
// to close it.
|
|
//
|
|
hDevParamsKey = NULL;
|
|
|
|
//
|
|
// Setup our pointer for retrieval of per-hw-id value...
|
|
//
|
|
PerHwIdBuffer = Buffer;
|
|
PerHwIdBufferLen = *pulLength;
|
|
|
|
//
|
|
// Setup our default return values in case no per-hw-id data is
|
|
// found...
|
|
//
|
|
Status = CR_NO_SUCH_VALUE;
|
|
RequiredSize = 0;
|
|
}
|
|
|
|
//
|
|
// From this point on use PerHwIdBuffer/PerHwIdBufferLen instead of
|
|
// caller-supplied Buffer/pulLength, since we may be appending results
|
|
// to those retrieved from the devnode's "Device Parameters" subkey...
|
|
//
|
|
|
|
//
|
|
// If we get to here, then we need to go look for the value under
|
|
// the appropriate per-hw-id registry key. First, figure out whether
|
|
// the per-hw-id information has changed since we last cached the
|
|
// most appropriate key.
|
|
//
|
|
RegDataSize = sizeof(LastUpdateTime);
|
|
|
|
if((ERROR_SUCCESS != RegQueryValueEx(ghPerHwIdKey,
|
|
pszRegValueLastUpdateTime,
|
|
NULL,
|
|
&RegDataType,
|
|
(PBYTE)&LastUpdateTime,
|
|
&RegDataSize))
|
|
|| (RegDataType != REG_BINARY)
|
|
|| (RegDataSize != sizeof(FILETIME))) {
|
|
|
|
//
|
|
// We can't ascertain when (or even if) the per-hw-id database was
|
|
// last populated. At this point, we bail with whatever status we
|
|
// had after our attempt at retrieving the per-devnode property.
|
|
//
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// (RegDataSize is already set appropriately, no need to initialize it
|
|
// again)
|
|
//
|
|
if(ERROR_SUCCESS == RegQueryValueEx(hDevKey,
|
|
pszRegValueCustomPropertyCacheDate,
|
|
NULL,
|
|
&RegDataType,
|
|
(PBYTE)&CacheDate,
|
|
&RegDataSize)) {
|
|
//
|
|
// Just to be extra paranoid...
|
|
//
|
|
if((RegDataType != REG_BINARY) || (RegDataSize != sizeof(FILETIME))) {
|
|
ZeroMemory(&CacheDate, sizeof(CacheDate));
|
|
}
|
|
|
|
} else {
|
|
ZeroMemory(&CacheDate, sizeof(CacheDate));
|
|
}
|
|
|
|
if(CompareFileTime(&CacheDate, &LastUpdateTime) == 0) {
|
|
//
|
|
// The Per-Hw-Id database hasn't been updated since we cached away
|
|
// the most-appropriate hardware id subkey. We can now use this
|
|
// subkey to see if there's a per-hw-id value entry contained
|
|
// therein for the requested property.
|
|
//
|
|
RegDataSize = sizeof(PerHwIdSubkeyName);
|
|
|
|
if(ERROR_SUCCESS != RegQueryValueEx(hDevKey,
|
|
pszRegValueCustomPropertyHwIdKey,
|
|
NULL,
|
|
&RegDataType,
|
|
(PBYTE)PerHwIdSubkeyName,
|
|
&RegDataSize)) {
|
|
//
|
|
// The value entry wasn't present, indicating there is no
|
|
// applicable per-hw-id key.
|
|
//
|
|
goto Clean0;
|
|
|
|
} else if(RegDataType != REG_SZ) {
|
|
//
|
|
// The data isn't a REG_SZ, like we expected. This should never
|
|
// happen, but if it does, go ahead and re-assess the key we
|
|
// should be using.
|
|
//
|
|
*PerHwIdSubkeyName = L'\0';
|
|
|
|
} else {
|
|
//
|
|
// We have a per-hw-id subkey to use. Go ahead and attempt to
|
|
// open it up here. If we find someone has tampered with the
|
|
// database and deleted this subkey, then we can at least go
|
|
// re-evaluate below to see if we can find a new key that's
|
|
// applicable for this devnode.
|
|
//
|
|
if(ERROR_SUCCESS != RegOpenKeyEx(ghPerHwIdKey,
|
|
PerHwIdSubkeyName,
|
|
0,
|
|
KEY_READ,
|
|
&hPerHwIdSubKey)) {
|
|
|
|
hPerHwIdSubKey = NULL;
|
|
|
|
|
|
|
|
*PerHwIdSubkeyName = L'\0';
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Per-Hw-Id database has been updated since we last cached away
|
|
// our custom property default key. (Note: The only time CacheDate
|
|
// could be _newer than_ LastUpdateTime would be when a previous
|
|
// update was (re-)applied to the per-hw-id database. In this case,
|
|
// we'd want to re-assess the key we're using, since we always want
|
|
// to be exactly in-sync with the current state of the database.
|
|
//
|
|
*PerHwIdSubkeyName = L'\0';
|
|
}
|
|
|
|
if(!(*PerHwIdSubkeyName)) {
|
|
//
|
|
// We need to look for a (new) per-hw-id key from which to retrieve
|
|
// properties applicable for this device.
|
|
//
|
|
hPerHwIdSubKey = FindMostAppropriatePerHwIdSubkey(hDevKey,
|
|
KEY_READ,
|
|
PerHwIdSubkeyName,
|
|
&RegDataSize
|
|
);
|
|
|
|
if(hPerHwIdSubKey) {
|
|
|
|
RegStatus = RegSetValueEx(hDevKey,
|
|
pszRegValueCustomPropertyHwIdKey,
|
|
0,
|
|
REG_SZ,
|
|
(PBYTE)PerHwIdSubkeyName,
|
|
RegDataSize * sizeof(WCHAR) // need size in bytes
|
|
);
|
|
} else {
|
|
|
|
RegStatus = RegDeleteKey(hDevKey,
|
|
pszRegValueCustomPropertyHwIdKey
|
|
);
|
|
}
|
|
|
|
if(RegStatus == ERROR_SUCCESS) {
|
|
//
|
|
// We successfully updated the cached per-hw-id key name. Now
|
|
// update the CustomPropertyCacheDate to reflect the date
|
|
// associated with the per-hw-id database.
|
|
//
|
|
RegSetValueEx(hDevKey,
|
|
pszRegValueCustomPropertyCacheDate,
|
|
0,
|
|
REG_BINARY,
|
|
(PBYTE)&LastUpdateTime,
|
|
sizeof(LastUpdateTime)
|
|
);
|
|
}
|
|
|
|
if(!hPerHwIdSubKey) {
|
|
//
|
|
// We couldn't find an applicable per-hw-id key for this
|
|
// devnode.
|
|
//
|
|
goto Clean0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we get to here, we have a handle to the per-hw-id subkey from
|
|
// which we can query the requested property.
|
|
//
|
|
RegDataSize = PerHwIdBufferLen; // remember buffer size prior to call
|
|
|
|
RegStatus = RegQueryValueEx(hPerHwIdSubKey,
|
|
CustomPropName,
|
|
NULL,
|
|
&RegDataType,
|
|
PerHwIdBuffer,
|
|
&PerHwIdBufferLen
|
|
);
|
|
|
|
if(RegStatus == ERROR_SUCCESS) {
|
|
//
|
|
// Again, we need to distinguish between the case where we
|
|
// succeeded because we supplied a zero-length buffer (we call it
|
|
// CR_BUFFER_SMALL) and the "real" success case.
|
|
//
|
|
if(RegDataSize == 0) {
|
|
|
|
if(PerHwIdBufferLen != 0) {
|
|
Status = CR_BUFFER_SMALL;
|
|
} else if(MergeMultiSzResult) {
|
|
//
|
|
// We already have the multi-sz results we retrieved from
|
|
// the devnode's "Device Parameters" subkey ready to return
|
|
// to the caller...
|
|
//
|
|
ASSERT(*pulRegDataType == REG_MULTI_SZ);
|
|
ASSERT((Status == CR_SUCCESS) || (Status == CR_BUFFER_SMALL));
|
|
ASSERT(RequiredSize >= sizeof(WCHAR));
|
|
ASSERT((Status != CR_SUCCESS) || (*pulTransferLen >= sizeof(WCHAR)));
|
|
|
|
goto Clean0;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Our success was genuine.
|
|
//
|
|
Status = CR_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// It's possible that we're supposed to be merging results into a
|
|
// multi-sz list, but didn't find a value under the devnode's
|
|
// "Device Parameters" subkey. Now that we have found a value
|
|
// under the per-hw-id subkey, we need to ensure the data returned
|
|
// is in multi-sz format.
|
|
//
|
|
if(!MergeMultiSzResult && (RequiredSize == 0)) {
|
|
|
|
if((ulFlags & CM_CUSTOMDEVPROP_MERGE_MULTISZ) &&
|
|
((RegDataType == REG_MULTI_SZ) || (RegDataType == REG_SZ))) {
|
|
|
|
MergeMultiSzResult = TRUE;
|
|
*pulRegDataType = REG_MULTI_SZ;
|
|
RequiredSize = sizeof(WCHAR);
|
|
|
|
if(RequiredSize > *pulLength) {
|
|
Status = CR_BUFFER_SMALL;
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
if(RegStatus == ERROR_MORE_DATA) {
|
|
Status = CR_BUFFER_SMALL;
|
|
} else {
|
|
//
|
|
// If we were merging results into our multi-sz list, ensure
|
|
// that our list-terminating null didn't get blown away.
|
|
//
|
|
if(MergeMultiSzResult) {
|
|
|
|
if(RegDataSize != 0) {
|
|
*((LPWSTR)PerHwIdBuffer) = L'\0';
|
|
}
|
|
|
|
//
|
|
// We already have the multi-sz results we retrieved from
|
|
// the devnode's "Device Parameters" subkey ready to return
|
|
// to the caller...
|
|
//
|
|
ASSERT(*pulRegDataType == REG_MULTI_SZ);
|
|
ASSERT((Status == CR_SUCCESS) || (Status == CR_BUFFER_SMALL));
|
|
ASSERT(RequiredSize >= sizeof(WCHAR));
|
|
ASSERT((Status != CR_SUCCESS) || (*pulTransferLen >= sizeof(WCHAR)));
|
|
|
|
} else {
|
|
ASSERT(Status == CR_NO_SUCH_VALUE);
|
|
ASSERT(*pulTransferLen == 0);
|
|
}
|
|
|
|
goto Clean0;
|
|
}
|
|
}
|
|
|
|
if(!MergeMultiSzResult) {
|
|
|
|
*pulRegDataType = RegDataType;
|
|
RequiredSize = PerHwIdBufferLen;
|
|
|
|
if(Status == CR_SUCCESS) {
|
|
//
|
|
// We have data to transfer.
|
|
//
|
|
*pulTransferLen = RequiredSize;
|
|
}
|
|
|
|
} else {
|
|
|
|
ASSERT(*pulRegDataType == REG_MULTI_SZ);
|
|
ASSERT((Status == CR_SUCCESS) || (Status == CR_BUFFER_SMALL));
|
|
ASSERT(RequiredSize >= sizeof(WCHAR));
|
|
|
|
//
|
|
// Unless the buffer size we retrieved is greater than one Unicode
|
|
// character, it isn't going to affect the resultant size of our
|
|
// multi-sz list.
|
|
//
|
|
if(PerHwIdBufferLen <= sizeof(WCHAR)) {
|
|
ASSERT((Status != CR_BUFFER_SMALL) || (*pulTransferLen == 0));
|
|
goto Clean0;
|
|
}
|
|
|
|
if(Status == CR_BUFFER_SMALL) {
|
|
//
|
|
// We might've previously believed that we could return data to
|
|
// the caller (e.g., because the data retrieved from the
|
|
// devnode's "Device Parameters" subkey fit into our buffer.
|
|
// Now that we see the data isn't going to fit, we need to
|
|
// ensure that *pulTransferLen is zero to indicate no data is
|
|
// being returned.
|
|
//
|
|
*pulTransferLen = 0;
|
|
|
|
if(RegDataType == REG_MULTI_SZ) {
|
|
//
|
|
// Just want the lengths of the string(s) plus
|
|
// their terminating nulls, excluding list-
|
|
// terminating null char.
|
|
//
|
|
RequiredSize += (PerHwIdBufferLen - sizeof(WCHAR));
|
|
|
|
} else if(RegDataType == REG_SZ) {
|
|
//
|
|
// We can just add the size of this string into our
|
|
// total requirement (unless it's an empty string,
|
|
// in which case we don't need to do anything at
|
|
// all).
|
|
//
|
|
RequiredSize += PerHwIdBufferLen;
|
|
|
|
} else {
|
|
//
|
|
// per-hw-id data wasn't a REG_SZ or REG_MULTI_SZ, so
|
|
// ignore it.
|
|
//
|
|
goto Clean0;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// We succeeded in retrieving more data into our multi-sz list.
|
|
// If the data we retrieved is multi-sz, then we don't have any
|
|
// additional work to do. However, if we retrieved a simple
|
|
// REG_SZ, then we need to find the end of the string, and add
|
|
// a second list-terminating null.
|
|
//
|
|
if(RegDataType == REG_MULTI_SZ) {
|
|
|
|
RequiredSize += (PerHwIdBufferLen - sizeof(WCHAR));
|
|
|
|
} else if(RegDataType == REG_SZ) {
|
|
|
|
RegDataSize = lstrlen((LPWSTR)PerHwIdBuffer) + 1;
|
|
|
|
if((RegDataSize == 1) ||
|
|
((RegDataSize * sizeof(WCHAR)) > PerHwIdBufferLen)) {
|
|
//
|
|
// The string we retrieved is either (a) empty or
|
|
// (b) longer than the buffer (the latter indicating
|
|
// that the string wasn't properly null-terminated).
|
|
// In either case, we don't want to append anything to
|
|
// our existing result, but we do need to ensure our
|
|
// list-terminating null character is still there...
|
|
//
|
|
*((LPWSTR)PerHwIdBuffer) = L'\0';
|
|
|
|
} else {
|
|
//
|
|
// Compute total size requirement..
|
|
//
|
|
RequiredSize += (RegDataSize * sizeof(WCHAR));
|
|
|
|
if(RequiredSize > *pulLength) {
|
|
//
|
|
// Adding the list-terminating null character
|
|
// pushed us over the size of the caller-
|
|
// supplied buffer. :-(
|
|
//
|
|
Status = CR_BUFFER_SMALL;
|
|
*pulTransferLen = 0;
|
|
goto Clean0;
|
|
|
|
} else {
|
|
//
|
|
// Add list-terminating null character...
|
|
//
|
|
*((LPWSTR)PerHwIdBuffer + RegDataSize) = L'\0';
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// per-hw-id data wasn't a REG_SZ or a REG_MULTI_SZ, so
|
|
// ignore it. (Make sure, though, that we still have our
|
|
// final terminating null character.)
|
|
//
|
|
*((LPWSTR)PerHwIdBuffer) = L'\0';
|
|
}
|
|
|
|
*pulTransferLen = RequiredSize;
|
|
}
|
|
}
|
|
|
|
Clean0:
|
|
|
|
if (ARGUMENT_PRESENT(pulLength)) {
|
|
*pulLength = RequiredSize;
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
//
|
|
// force compiler to respect statement ordering w.r.t. assignments
|
|
// for these variables...
|
|
//
|
|
hDevKey = hDevKey;
|
|
hDevParamsKey = hDevParamsKey;
|
|
hPerHwIdSubKey = hPerHwIdSubKey;
|
|
}
|
|
|
|
if(hDevKey != NULL) {
|
|
RegCloseKey(hDevKey);
|
|
}
|
|
if(hDevParamsKey != NULL) {
|
|
RegCloseKey(hDevParamsKey);
|
|
}
|
|
if(hPerHwIdSubKey != NULL) {
|
|
RegCloseKey(hPerHwIdSubKey);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PNP_GetCustomDevProp
|
|
|
|
|
|
|
|
HKEY
|
|
FindMostAppropriatePerHwIdSubkey(
|
|
IN HKEY hDevKey,
|
|
IN REGSAM samDesired,
|
|
OUT LPWSTR PerHwIdSubkeyName,
|
|
OUT LPDWORD PerHwIdSubkeyLen
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds the subkey in the per-hw-id database that is most
|
|
appropriate for the device whose instance key was passed as input. This
|
|
determination is made by taking each of the device's hardware and
|
|
compatible ids, in turn, and forming a subkey name by replacing backslashes
|
|
(\) with hashes (#). An attempt is made to open that subkey under the
|
|
per-hw-id key, and the first such id to succeed, if any, is the most
|
|
appropriate (i.e., most-specific) database entry.
|
|
|
|
Note: we must consider both hardware and compatible ids, because some buses
|
|
(such as PCI) may shift hardware ids down into the compatible id list under
|
|
certain circumstances (e.g., PCI\VENxxxxDEVyyyy gets moved into the
|
|
compatible list in the presence of subsys info).
|
|
|
|
Arguments:
|
|
|
|
hDevKey Supplies a handle to the device instance key for whom the
|
|
most-appropriate per-hw-id subkey is to be ascertained.
|
|
|
|
samDesired Supplies an access mask indicating the desired access
|
|
rights to the per-hw-id key being returned.
|
|
|
|
PerHwIdSubkeyName Supplies a buffer (that must be at least
|
|
MAX_DEVNODE_ID_LEN characters in length) that, upon
|
|
success, receives the most-appropriate per-hw-id subkey
|
|
name.
|
|
|
|
PerHwIdSubkeyLen Supplies the address of a variable that, upon successful
|
|
return, receives the length of the subkey name (in
|
|
characters), including terminating NULL, stored into the
|
|
PerHwIdSubkeyName buffer.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is a handle to the most-
|
|
appropriate per-hw-id subkey.
|
|
|
|
If the function fails, the return value is NULL.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i;
|
|
DWORD RegDataType;
|
|
PWCHAR IdList;
|
|
HKEY hPerHwIdSubkey;
|
|
PWSTR pCurId, pSrcChar, pDestChar;
|
|
DWORD CurIdLen;
|
|
DWORD idSize;
|
|
WCHAR ids[REGSTR_VAL_MAX_HCID_LEN];
|
|
|
|
//
|
|
// Note: we don't need to use structured exception handling in this
|
|
// routine, since if we crash here (e.g., due to retrieval of a bogus
|
|
// hardware or compatible id list), we won't leak any resource. Thus, the
|
|
// caller's try/except is sufficient.
|
|
//
|
|
|
|
//
|
|
// First process the hardware id list, and if no appropriate match
|
|
// found there, then examine the compatible id list.
|
|
//
|
|
for(i = 0; i < 2; i++) {
|
|
|
|
idSize = sizeof(ids);
|
|
if((ERROR_SUCCESS != RegQueryValueEx(hDevKey,
|
|
(i ? pszRegValueCompatibleIDs
|
|
: pszRegValueHardwareIDs),
|
|
NULL,
|
|
&RegDataType,
|
|
(PBYTE)ids,
|
|
&idSize))
|
|
|| (RegDataType != REG_MULTI_SZ)) {
|
|
|
|
//
|
|
// Missing or invalid id list--bail now.
|
|
//
|
|
return NULL;
|
|
}
|
|
IdList = ids;
|
|
//
|
|
// Now iterate through each id in our list, trying to open each one
|
|
// in turn under the per-hw-id database key.
|
|
//
|
|
for(pCurId = IdList; *pCurId; pCurId += CurIdLen) {
|
|
|
|
CurIdLen = lstrlen(pCurId) + 1;
|
|
|
|
if(CurIdLen > MAX_DEVNODE_ID_LEN) {
|
|
//
|
|
// Bogus id in the list--skip it.
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Transfer id into our subkey name buffer, converting path
|
|
// separator characters ('\') to hashes ('#').
|
|
//
|
|
pSrcChar = pCurId;
|
|
pDestChar = PerHwIdSubkeyName;
|
|
|
|
do {
|
|
*pDestChar = (*pSrcChar != L'\\') ? *pSrcChar : L'#';
|
|
pDestChar++;
|
|
} while(*(pSrcChar++));
|
|
|
|
if(ERROR_SUCCESS == RegOpenKeyEx(ghPerHwIdKey,
|
|
PerHwIdSubkeyName,
|
|
0,
|
|
samDesired,
|
|
&hPerHwIdSubkey)) {
|
|
//
|
|
// We've found our key!
|
|
//
|
|
*PerHwIdSubkeyLen = CurIdLen;
|
|
return hPerHwIdSubkey;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we get to here, we didn't find an appropriate per-hw-id subkey to
|
|
// return to the caller.
|
|
//
|
|
return NULL;
|
|
}
|