mirror of https://github.com/lianthony/NT4.0
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.
2309 lines
66 KiB
2309 lines
66 KiB
|
|
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
regprop.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the API routines that reg and set registry
|
|
properties and operates on classes.
|
|
|
|
CM_Get_DevNode_Registry_Property
|
|
CM_Set_DevNode_Registry_Property
|
|
CM_Open_DevNode_Key
|
|
CM_Delete_DevNode_Key
|
|
CM_Open_Class_Key
|
|
CM_Enumerate_Classes
|
|
CM_Get_Class_Name
|
|
|
|
Author:
|
|
|
|
Paula Tomlinson (paulat) 6-22-1995
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
|
|
Revision History:
|
|
|
|
22-Jun-1995 paulat
|
|
|
|
Creation and initial implementation.
|
|
|
|
--*/
|
|
|
|
|
|
//
|
|
// includes
|
|
//
|
|
#include "precomp.h"
|
|
#include "setupapi.h"
|
|
#include "spapip.h"
|
|
#include "cmdat.h"
|
|
|
|
//
|
|
// Private prototypes
|
|
//
|
|
|
|
ULONG
|
|
GetPropertyDataType(
|
|
IN ULONG ulProperty
|
|
);
|
|
|
|
|
|
//
|
|
// global data
|
|
//
|
|
extern PVOID hLocalBindingHandle; // NOT MODIFIED BY THESE PROCEDURES
|
|
|
|
|
|
|
|
|
|
CONFIGRET
|
|
CM_Get_DevNode_Registry_Property_ExW(
|
|
IN DEVINST dnDevInst,
|
|
IN ULONG ulProperty,
|
|
OUT PULONG pulRegDataType, OPTIONAL
|
|
OUT PVOID Buffer, OPTIONAL
|
|
IN OUT PULONG pulLength,
|
|
IN ULONG ulFlags,
|
|
IN HMACHINE hMachine
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves the specified value from the device instance's
|
|
registry storage key.
|
|
|
|
Parameters:
|
|
|
|
dnDevInst Supplies the handle of the device instance for which a
|
|
property is to be retrieved.
|
|
|
|
ulProperty Supplies an ordinal specifying the property to be retrieved.
|
|
(CM_DRP_*)
|
|
|
|
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.
|
|
|
|
pulLength Supplies the address of the variable that contains the size,
|
|
in bytes, of the buffer. The API replaces the initial size
|
|
with the number of bytes of registry data copied to the buffer.
|
|
If the variable is initially zero, the API replaces it with
|
|
the buffer size needed to receive all the registry data. In
|
|
this case, the Buffer parameter is ignored.
|
|
|
|
ulFlags Must be zero.
|
|
|
|
hMachine Machine handle returned from CM_Connect_Machine or NULL.
|
|
|
|
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_DEVINST,
|
|
CR_NO_SUCH_REGISTRY_KEY,
|
|
CR_INVALID_FLAG,
|
|
CR_INVALID_POINTER,
|
|
CR_NO_SUCH_VALUE,
|
|
CR_REGISTRY_ERROR, or
|
|
CR_BUFFER_SMALL.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
LPWSTR pDeviceID = NULL;
|
|
ULONG ulTempDataType=0, ulTransferLen=0;
|
|
BYTE NullBuffer=0;
|
|
handle_t hBinding = NULL;
|
|
PVOID hStringTable = NULL;
|
|
|
|
|
|
try {
|
|
//
|
|
// validate parameters
|
|
//
|
|
if (dnDevInst == 0) {
|
|
Status = CR_INVALID_DEVINST;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (pulLength == NULL) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (Buffer == NULL && *pulLength != 0) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (INVALID_FLAGS(ulFlags, 0)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (ulProperty < CM_DRP_MIN || ulProperty > CM_DRP_MAX) {
|
|
Status = CR_INVALID_PROPERTY;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// setup rpc binding handle and string table handle
|
|
//
|
|
if (!PnPGetGlobalHandles(hMachine, &hStringTable, &hBinding)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// retrieve the string form of the device id string
|
|
//
|
|
pDeviceID = StringTableStringFromId(hStringTable, dnDevInst);
|
|
if (pDeviceID == NULL || INVALID_DEVINST(pDeviceID)) {
|
|
Status = CR_INVALID_DEVINST;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// NOTE: The ulTransferLen variable is just used to control
|
|
// how much data is marshalled via rpc between address spaces.
|
|
// ulTransferLen should be set on entry to the size of the Buffer.
|
|
// The last parameter should also be the size of the Buffer on entry
|
|
// and on exit contains either the amount transferred (if a transfer
|
|
// occured) or the amount required, this value should be passed back
|
|
// in the callers pulLength parameter.
|
|
//
|
|
ulTransferLen = *pulLength;
|
|
if (Buffer == NULL) {
|
|
Buffer = &NullBuffer;
|
|
}
|
|
|
|
|
|
RpcTryExcept {
|
|
//
|
|
// call rpc service entry point
|
|
//
|
|
Status = PNP_GetDeviceRegProp(
|
|
hBinding, // rpc binding handle
|
|
pDeviceID, // string representation of device instance
|
|
ulProperty, // id for the property
|
|
&ulTempDataType, // receives registry data type
|
|
Buffer, // receives registry data
|
|
&ulTransferLen, // input/output buffer size
|
|
pulLength, // bytes copied (or bytes required)
|
|
ulFlags); // not used
|
|
}
|
|
RpcExcept (1) {
|
|
PnPTrace(
|
|
TEXT("PNP_DevNodeRegistryProperty caused an exception (%d)\n"),
|
|
RpcExceptionCode());
|
|
Status = MapRpcExceptionToCR(RpcExceptionCode());
|
|
}
|
|
RpcEndExcept
|
|
|
|
|
|
if (pulRegDataType != NULL) {
|
|
//
|
|
// I pass a temp variable to the rpc stubs since they require the
|
|
// output param to always be valid, then if user did pass in a valid
|
|
// pointer to receive the info, do the assignment now
|
|
//
|
|
*pulRegDataType = ulTempDataType;
|
|
}
|
|
|
|
|
|
Clean0:
|
|
;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // CM_Get_DevNode_Registry_Property_ExW
|
|
|
|
|
|
|
|
|
|
CONFIGRET
|
|
CM_Set_DevNode_Registry_Property_ExW(
|
|
IN DEVINST dnDevInst,
|
|
IN ULONG ulProperty,
|
|
IN PCVOID Buffer, OPTIONAL
|
|
IN OUT ULONG ulLength,
|
|
IN ULONG ulFlags,
|
|
IN HMACHINE hMachine
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets the specified value in the device instance's registry
|
|
storage key.
|
|
|
|
Parameters:
|
|
|
|
dnDevInst Supplies the handle of the device instance for which a
|
|
property is to be retrieved.
|
|
|
|
ulProperty Supplies an ordinal specifying the property to be set.
|
|
(CM_DRP_*)
|
|
|
|
Buffer Supplies the address of the buffer that contains the
|
|
registry data. This data must be of the proper type
|
|
for that property.
|
|
|
|
ulLength Supplies the number of bytes of registry data to write.
|
|
|
|
ulFlags Must be zero.
|
|
|
|
hMachine Machine handle returned from CM_Connect_Machine or NULL.
|
|
|
|
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_NO_SUCH_REGISTRY_KEY,
|
|
CR_INVALID_FLAG,
|
|
CR_INVALID_POINTER,
|
|
CR_NO_SUCH_VALUE,
|
|
CR_REGISTRY_ERROR,
|
|
CR_INVALID_DATA, or
|
|
CR_BUFFER_SMALL.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
WCHAR PropertyName[MAX_PATH];
|
|
LPWSTR pDeviceID = NULL;
|
|
ULONG ulRegDataType = 0, ulTransferLen = 0;
|
|
BYTE NullBuffer = 0x0;
|
|
handle_t hBinding = NULL;
|
|
PVOID hStringTable = NULL;
|
|
|
|
|
|
try {
|
|
//
|
|
// validate permission
|
|
//
|
|
if (!IsUserAdmin()) {
|
|
Status = CR_ACCESS_DENIED;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// validate parameters
|
|
//
|
|
if (dnDevInst == 0) {
|
|
Status = CR_INVALID_DEVINST;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (Buffer == NULL && ulLength != 0) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (INVALID_FLAGS(ulFlags, 0)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (ulProperty < CM_DRP_MIN || ulProperty > CM_DRP_MAX) {
|
|
Status = CR_INVALID_PROPERTY;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// setup rpc binding handle and string table handle
|
|
//
|
|
if (!PnPGetGlobalHandles(hMachine, &hStringTable, &hBinding)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// retrieve the string form of the device id string
|
|
//
|
|
pDeviceID = StringTableStringFromId(hStringTable, dnDevInst);
|
|
if (pDeviceID == NULL || INVALID_DEVINST(pDeviceID)) {
|
|
Status = CR_INVALID_DEVNODE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// we need to specify what registry data to use for storing this data
|
|
//
|
|
ulRegDataType = GetPropertyDataType(ulProperty);
|
|
|
|
//
|
|
// NOTE: The ulTransferLen variable is just used to control
|
|
// how much data is marshalled via rpc between address spaces.
|
|
// ulTransferLen should be set on entry to the size of the Buffer.
|
|
// The last parameter should also be the size of the Buffer on entry,
|
|
// for setting registry properties, the length is not an output
|
|
// parameter.
|
|
//
|
|
if (Buffer == NULL) {
|
|
Buffer = &NullBuffer;
|
|
}
|
|
|
|
|
|
RpcTryExcept {
|
|
//
|
|
// call rpc service entry point
|
|
//
|
|
Status = PNP_SetDeviceRegProp(
|
|
hBinding, // rpc binding handle
|
|
pDeviceID, // string representation of devinst
|
|
ulProperty, // string name for property
|
|
ulRegDataType, // specify registry data type
|
|
(LPBYTE)Buffer, // receives registry data
|
|
ulLength, // amount to return in Buffer
|
|
ulFlags); // not used
|
|
}
|
|
RpcExcept (1) {
|
|
PnPTrace(
|
|
TEXT("PNP_DevNodeRegistryProperty caused an exception (%d)\n"),
|
|
RpcExceptionCode());
|
|
Status = MapRpcExceptionToCR(RpcExceptionCode());
|
|
}
|
|
RpcEndExcept
|
|
|
|
|
|
Clean0:
|
|
;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // CM_Set_DevNode_Registry_Property_ExW
|
|
|
|
|
|
|
|
|
|
CONFIGRET
|
|
CM_Open_DevNode_Key_Ex(
|
|
IN DEVINST dnDevNode,
|
|
IN REGSAM samDesired,
|
|
IN ULONG ulHardwareProfile,
|
|
IN REGDISPOSITION Disposition,
|
|
OUT PHKEY phkDevice,
|
|
IN ULONG ulFlags,
|
|
IN HMACHINE hMachine
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine opens the software storage registry key associated with a
|
|
device instance.
|
|
|
|
Parameters:
|
|
|
|
dnDevNode Handle of a device instance. This handle is typically
|
|
retrieved by a call to CM_Locate_DevNode or
|
|
CM_Create_DevNode.
|
|
|
|
samDesired Specifies an access mask that describes the desired
|
|
security access for the key. This parameter can be
|
|
a combination of the values used in calls to RegOpenKeyEx.
|
|
|
|
ulHardwareProfile Supplies the handle of the hardware profile to open the
|
|
storage key under. This parameter is only used if the
|
|
CM_REGISTRY_CONFIG flag is specified in ulFlags. If
|
|
this parameter is 0, the API uses the current hardware
|
|
profile.
|
|
|
|
Disposition Specifies how the registry key is to be opened. May be
|
|
one of the following values:
|
|
|
|
RegDisposition_OpenAlways - Open the key if it exists,
|
|
otherwise, create the key.
|
|
RegDisposition_OpenExisting - Open the key only if it
|
|
exists, otherwise fail with CR_NO_SUCH_REGISTRY_VALUE.
|
|
|
|
phkDevice Supplies the address of the variable that receives an
|
|
opened handle to the specified key. When access to this
|
|
key is completed, it must be closed via RegCloseKey.
|
|
|
|
ulFlags Specifies what type of storage key should be opened.
|
|
Can be a combination of these values:
|
|
|
|
CM_REGISTRY_HARDWARE (0x00000000)
|
|
Open a key for storing driver-independent information
|
|
relating to the device instance. On Windows NT, the
|
|
full path to such a storage key is of the form:
|
|
|
|
HKLM\System\CurrentControlSet\Enum\<enumerator>\
|
|
<DeviceID>\<InstanceID>\Device Parameters
|
|
|
|
CM_REGISTRY_SOFTWARE (0x00000001)
|
|
Open a key for storing driver-specific information
|
|
relating to the device instance. On Windows NT, the
|
|
full path to such a storage key is of the form:
|
|
|
|
HKLM\System\CurrentControlSet\Control\Class\
|
|
<DevNodeClass>\<ClassInstanceOrdinal>
|
|
|
|
CM_REGISTRY_USER (0x00000100)
|
|
Open a key under HKEY_CURRENT_USER instead of
|
|
HKEY_LOCAL_MACHINE. This flag may not be used with
|
|
CM_REGISTRY_CONFIG. There is no analagous kernel-mode
|
|
API on NT to get a per-user device configuration
|
|
storage, since this concept does not apply to device
|
|
drivers (no user may be logged on, etc). However,
|
|
this flag is provided for consistency with Win95, and
|
|
because it is foreseeable that it could be useful to
|
|
Win32 services that interact with Plug-and-Play model.
|
|
|
|
CM_REGSITRY_CONFIG (0x00000200)
|
|
Open the key under a hardware profile branch instead
|
|
of HKEY_LOCAL_MACHINE. If this flag is specified,
|
|
then ulHardwareProfile supplies the handle of the
|
|
hardware profile to be used. This flag may not be
|
|
used with CM_REGISTRY_USER.
|
|
|
|
hMachine Machine handle returned from CM_Connect_Machine or NULL.
|
|
|
|
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_DEVICE_ID,
|
|
CR_INVALID_FLAG,
|
|
CR_INVALID_POINTER, or
|
|
CR_REGISTRY_ERROR
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
LONG RegStatus = ERROR_SUCCESS;
|
|
LPWSTR pDeviceID = NULL;
|
|
WCHAR szMachineName[MAX_COMPUTERNAME_LENGTH + 1];
|
|
HKEY hKey=NULL, hRemoteKey=NULL, hBranchKey=NULL;
|
|
WCHAR szKey[MAX_CM_PATH], szPrivateKey[MAX_CM_PATH];
|
|
WCHAR szClassInstance[MAX_PATH];
|
|
PVOID hStringTable = NULL;
|
|
handle_t hBinding = NULL;
|
|
|
|
|
|
try {
|
|
//
|
|
// validate parameters
|
|
//
|
|
if (phkDevice == NULL) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
*phkDevice = NULL;
|
|
|
|
if (dnDevNode == 0) {
|
|
Status = CR_INVALID_DEVINST;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (INVALID_FLAGS(ulFlags, CM_REGISTRY_BITS)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (INVALID_FLAGS(Disposition, RegDisposition_Bits)) {
|
|
Status = CR_INVALID_DATA;
|
|
goto Clean0;
|
|
}
|
|
|
|
if ((ulFlags & CM_REGISTRY_CONFIG) &&
|
|
(ulHardwareProfile > MAX_CONFIG_VALUE)) {
|
|
|
|
Status = CR_INVALID_DATA;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// setup rpc binding handle and string table handle
|
|
//
|
|
if (!PnPGetGlobalHandles(hMachine, &hStringTable, &hBinding)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
if ((hBinding != hLocalBindingHandle) && (ulFlags & CM_REGISTRY_USER)) {
|
|
Status = CR_ACCESS_DENIED; // current user key can't be remoted
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// retrieve the device id string and validate it
|
|
//
|
|
pDeviceID = StringTableStringFromId(hStringTable, dnDevNode);
|
|
if (pDeviceID == NULL || INVALID_DEVINST(pDeviceID)) {
|
|
Status = CR_INVALID_DEVNODE;
|
|
goto Clean0;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
// determine the branch key to use; either HKLM or HKCU
|
|
//-------------------------------------------------------------
|
|
|
|
if (hBinding == hLocalBindingHandle) {
|
|
|
|
if (ulFlags & CM_REGISTRY_USER) {
|
|
hBranchKey = HKEY_CURRENT_USER;
|
|
}
|
|
else {
|
|
//
|
|
// all other cases go to HKLM (validate permission first?)
|
|
//
|
|
hBranchKey = HKEY_LOCAL_MACHINE;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// retrieve machine name
|
|
//
|
|
PnPRetrieveMachineName(hMachine, szMachineName);
|
|
|
|
//
|
|
// use remote HKLM branch (we only support connect to
|
|
// HKEY_LOCAL_MACHINE on the remote machine, not HKEY_CURRENT_USER)
|
|
//
|
|
RegStatus = RegConnectRegistry(szMachineName, HKEY_LOCAL_MACHINE,
|
|
&hRemoteKey);
|
|
|
|
if (RegStatus != ERROR_SUCCESS) {
|
|
Status = CR_REGISTRY_ERROR;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// hBranchKey is either a predefined key or assigned to by
|
|
// another key, I never attempt to close it. If hRemoteKey is
|
|
// non-NULL I will attempt to close it during cleanup since
|
|
// it is explicitly opened.
|
|
//
|
|
hBranchKey = hRemoteKey;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
// form the registry path based on the device id and the flags.
|
|
//-------------------------------------------------------------
|
|
|
|
Status = GetDevNodeKeyPath(hBinding, pDeviceID, ulFlags,
|
|
ulHardwareProfile, szKey, szPrivateKey);
|
|
|
|
if (Status != CR_SUCCESS) {
|
|
goto Clean0;
|
|
}
|
|
|
|
lstrcat(szKey, TEXT("\\"));
|
|
lstrcat(szKey, szPrivateKey);
|
|
|
|
//
|
|
// open the registry key (method of open is based on flags)
|
|
//
|
|
if (Disposition == RegDisposition_OpenAlways) {
|
|
|
|
//-----------------------------------------------------
|
|
// open the registry key always
|
|
//-----------------------------------------------------
|
|
|
|
//
|
|
// Only the main Enum subtree under HKLM has strict security
|
|
// that requires me to first create the key on the server
|
|
// side and then open it here on the client side. This
|
|
// condition currently only occurs if the flags have
|
|
// CM_REGISTRY_HARDWARE set but no other flags set.
|
|
//
|
|
if (ulFlags == CM_REGISTRY_HARDWARE) {
|
|
//
|
|
// first try to open it (in case it already exists). If it
|
|
// doesn't exist, then I'll have to have the protected server
|
|
// side create the key. I still need to open it from here, the
|
|
// client-side, so that the registry handle will be in the
|
|
// caller's address space.
|
|
//
|
|
RegStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKey, 0,
|
|
samDesired, phkDevice);
|
|
|
|
if (RegStatus != ERROR_SUCCESS) {
|
|
//
|
|
// call server side to create the key
|
|
//
|
|
RpcTryExcept {
|
|
|
|
Status = PNP_CreateKey(hBinding, szKey, samDesired, 0);
|
|
}
|
|
RpcExcept (1) {
|
|
PnPTrace(
|
|
TEXT("PNP_CreateKey caused an exception (%d)\n"),
|
|
RpcExceptionCode());
|
|
Status = MapRpcExceptionToCR(RpcExceptionCode());
|
|
}
|
|
RpcEndExcept
|
|
|
|
if (Status != CR_SUCCESS) {
|
|
*phkDevice = NULL;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// the key was created successfully, so open it now
|
|
//
|
|
RegStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKey, 0,
|
|
samDesired, phkDevice);
|
|
|
|
if (RegStatus == ERROR_ACCESS_DENIED) {
|
|
*phkDevice = NULL;
|
|
Status = CR_ACCESS_DENIED;
|
|
goto Clean0;
|
|
}
|
|
else if (RegStatus != ERROR_SUCCESS) {
|
|
//
|
|
// if we still can't open the key, I give up
|
|
//
|
|
*phkDevice = NULL;
|
|
Status = CR_REGISTRY_ERROR;
|
|
goto Clean0;
|
|
}
|
|
}
|
|
}
|
|
|
|
else {
|
|
//
|
|
// these keys have admin-full privilege so try to open
|
|
// from the client-side and just let the security of the
|
|
// key judge whether the caller can access it.
|
|
//
|
|
RegStatus = RegCreateKeyEx(hBranchKey, szKey, 0, NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
samDesired, NULL, phkDevice, NULL);
|
|
|
|
if (RegStatus == ERROR_ACCESS_DENIED) {
|
|
*phkDevice = NULL;
|
|
Status = CR_ACCESS_DENIED;
|
|
goto Clean0;
|
|
}
|
|
else if (RegStatus != ERROR_SUCCESS) {
|
|
*phkDevice = NULL;
|
|
Status = CR_REGISTRY_ERROR;
|
|
goto Clean0;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
//-----------------------------------------------------
|
|
// open only if it already exists
|
|
//-----------------------------------------------------
|
|
|
|
//
|
|
// the actual open always occurs on the client side so I can
|
|
// pass back a handle that's valid for the calling process.
|
|
// Only creates need to happen on the server side
|
|
//
|
|
RegStatus = RegOpenKeyEx(hBranchKey, szKey, 0, samDesired,
|
|
phkDevice);
|
|
|
|
if (RegStatus == ERROR_ACCESS_DENIED) {
|
|
*phkDevice = NULL;
|
|
Status = CR_ACCESS_DENIED;
|
|
goto Clean0;
|
|
}
|
|
else if (RegStatus != ERROR_SUCCESS) {
|
|
*phkDevice = NULL;
|
|
Status = CR_NO_SUCH_REGISTRY_KEY;
|
|
}
|
|
}
|
|
|
|
|
|
Clean0:
|
|
;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
if (hKey != NULL) {
|
|
RegCloseKey(hKey);
|
|
}
|
|
if (hRemoteKey != NULL) {
|
|
RegCloseKey(hRemoteKey);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // CM_Open_DevNode_Key_ExW
|
|
|
|
|
|
|
|
|
|
CONFIGRET
|
|
CM_Delete_DevNode_Key_Ex(
|
|
IN DEVNODE dnDevNode,
|
|
IN ULONG ulHardwareProfile,
|
|
IN ULONG ulFlags,
|
|
IN HANDLE hMachine
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine deletes a registry storage key associated with a device
|
|
instance.
|
|
|
|
dnDevNode Handle of a device instance. This handle is typically
|
|
retrieved by a call to CM_Locate_DevNode or CM_Create_DevNode.
|
|
|
|
ulHardwareProfile Supplies the handle of the hardware profile to delete
|
|
the storage key under. This parameter is only used if the
|
|
CM_REGISTRY_CONFIG flag is specified in ulFlags. If this
|
|
parameter is 0, the API uses the current hardware profile.
|
|
If this parameter is 0xFFFFFFFF, then the specified storage
|
|
key(s) for all hardware profiles is (are) deleted.
|
|
|
|
ulFlags Specifies what type(s) of storage key(s) should be deleted.
|
|
Can be a combination of these values:
|
|
|
|
CM_REGISTRY_HARDWARE - Delete the key for storing driver-
|
|
independent information relating to the device instance.
|
|
This may be combined with CM_REGISTRY_SOFTWARE to delete
|
|
both device and driver keys simultaneously.
|
|
CM_REGISTRY_SOFTWARE - Delete the key for storing driver-
|
|
specific information relating to the device instance.
|
|
This may be combined with CM_REGISTRY_HARDWARE to
|
|
delete both driver and device keys simultaneously.
|
|
CM_REGISTRY_USER - Delete the specified key(s) under
|
|
HKEY_CURRENT_USER instead of HKEY_LOCAL_MACHINE.
|
|
This flag may not be used with CM_REGISTRY_CONFIG.
|
|
CM_REGISTRY_CONFIG - Delete the specified keys(s) under a
|
|
hardware profile branch instead of HKEY_LOCAL_MACHINE.
|
|
If this flag is specified, then ulHardwareProfile
|
|
supplies the handle to the hardware profile to be used.
|
|
This flag may not be used with CM_REGISTRY_USER.
|
|
|
|
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_REGISTRY_ERROR
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
LONG RegStatus = ERROR_SUCCESS;
|
|
PVOID hStringTable = NULL;
|
|
handle_t hBinding = NULL;
|
|
HKEY hKey = NULL;
|
|
LPWSTR pDeviceID = NULL;
|
|
WCHAR szParentKey[MAX_CM_PATH], szChildKey[MAX_DEVICE_ID_LEN];
|
|
WCHAR RegStr[MAX_CM_PATH], szProfile[MAX_PROFILE_ID_LEN];
|
|
ULONG ulIndex = 0, ulSize = 0;
|
|
|
|
|
|
try {
|
|
//
|
|
// validate parameters
|
|
//
|
|
if (dnDevNode == 0) {
|
|
Status = CR_INVALID_DEVINST;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (INVALID_FLAGS(ulFlags, CM_REGISTRY_BITS)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
if ((ulFlags & CM_REGISTRY_USER) && (ulFlags & CM_REGISTRY_CONFIG)) {
|
|
Status = CR_INVALID_FLAG; // can't specify both
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// setup string table handle
|
|
//
|
|
if (!PnPGetGlobalHandles(hMachine, &hStringTable, &hBinding)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// retrieve the device id string and validate it
|
|
//
|
|
pDeviceID = StringTableStringFromId(hStringTable, dnDevNode);
|
|
if (pDeviceID == NULL || INVALID_DEVINST(pDeviceID)) {
|
|
Status = CR_INVALID_DEVNODE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// form the registry path based on the device id and the flags.
|
|
//
|
|
Status = GetDevNodeKeyPath(hBinding, pDeviceID, ulFlags,
|
|
ulHardwareProfile, szParentKey, szChildKey);
|
|
|
|
if (Status != CR_SUCCESS) {
|
|
goto Clean0;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
// For either hw and sw user keys, the client side is privileged
|
|
// enough to do the delete (if the caller doesn't have admin, it
|
|
// will be denied but that is the desired behaviour). Also, the
|
|
// service-side cannot access the HKEY_CURRENT_USER key unless it
|
|
// does some sort of impersonation.
|
|
//------------------------------------------------------------------
|
|
|
|
if (ulFlags & CM_REGISTRY_USER) {
|
|
//
|
|
// handle the special config-specific case when the profile
|
|
// specified is -1, then need to delete the private key
|
|
// for all profiles
|
|
//
|
|
if ((ulFlags & CM_REGISTRY_CONFIG) &&
|
|
(ulHardwareProfile == 0xFFFFFFFF)) {
|
|
|
|
wsprintf(RegStr, TEXT("%s\\%s"),
|
|
pszRegPathIDConfigDB,
|
|
pszRegKeyKnownDockingStates);
|
|
|
|
RegStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegStr, 0,
|
|
KEY_ALL_ACCESS, &hKey);
|
|
|
|
//
|
|
// enumerate the hardware profile keys
|
|
//
|
|
for (ulIndex = 0; RegStatus == ERROR_SUCCESS; ulIndex++) {
|
|
|
|
ulSize = MAX_PROFILE_ID_LEN * sizeof(WCHAR);
|
|
RegStatus = RegEnumKeyEx(hKey, ulIndex, szProfile, &ulSize,
|
|
NULL, NULL, NULL, NULL);
|
|
|
|
if (RegStatus == ERROR_SUCCESS) {
|
|
//
|
|
// szParentKey contains replacement symbol for the
|
|
// profile id
|
|
//
|
|
wsprintf(RegStr, szParentKey, szProfile);
|
|
|
|
Status = DeletePrivateKey(HKEY_CURRENT_USER, RegStr,
|
|
szChildKey);
|
|
|
|
if (Status != CR_SUCCESS) {
|
|
goto Clean0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else {
|
|
//
|
|
// not for all profiles, so just delete the specified key
|
|
//
|
|
Status = DeletePrivateKey(HKEY_CURRENT_USER, szParentKey,
|
|
szChildKey);
|
|
|
|
if (Status != CR_SUCCESS) {
|
|
goto Clean0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
// For the remaining cases (no user keys), do the work on the
|
|
// server side, sense that side has the code to make the key
|
|
// volatile if necessary instead of deleting. Also, access to
|
|
// some of these registry keys requires system privilege.
|
|
//------------------------------------------------------------------
|
|
|
|
else {
|
|
//
|
|
// validate permission
|
|
//
|
|
if (!IsUserAdmin()) {
|
|
Status = CR_ACCESS_DENIED;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (!(ulFlags & CM_REGISTRY_CONFIG)) {
|
|
ulHardwareProfile = 0;
|
|
}
|
|
|
|
|
|
RpcTryExcept {
|
|
//
|
|
// call rpc service entry point
|
|
//
|
|
Status = PNP_DeleteRegistryKey(
|
|
hBinding, // rpc binding handle
|
|
pDeviceID, // device id
|
|
szParentKey, // parent of key to delete
|
|
szChildKey, // key to delete
|
|
ulHardwareProfile); // flags, not used
|
|
}
|
|
RpcExcept (1) {
|
|
PnPTrace(
|
|
TEXT("PNP_DeleteRegistryKey caused an exception (%d)\n"),
|
|
RpcExceptionCode());
|
|
Status = MapRpcExceptionToCR(RpcExceptionCode());
|
|
}
|
|
RpcEndExcept
|
|
}
|
|
|
|
|
|
Clean0:
|
|
;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
if (hKey != NULL) {
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // CM_Delete_DevNode_Key_Ex
|
|
|
|
|
|
|
|
|
|
|
|
CONFIGRET
|
|
CM_Open_Class_Key_ExW(
|
|
IN LPGUID ClassGuid, OPTIONAL
|
|
IN LPCWSTR pszClassName, OPTIONAL
|
|
IN REGSAM samDesired,
|
|
IN REGDISPOSITION Disposition,
|
|
OUT PHKEY phkClass,
|
|
IN ULONG ulFlags,
|
|
IN HMACHINE hMachine
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine opens the class registry key, and optionally, a specific
|
|
class's subkey.
|
|
|
|
Parameters:
|
|
|
|
ClassGuid Optionally, supplies the address of a class GUID representing
|
|
the class subkey to be opened.
|
|
|
|
pszClassName Specifies the string form of the class name for the class
|
|
represented by ClasGuid.
|
|
|
|
samDesired Specifies an access mask that describes the desired security
|
|
access for the new key. This parameter can be a combination
|
|
of the values used in calls to RegOpenKeyEx.
|
|
|
|
Disposition Specifies how the registry key is to be opened. May be one
|
|
of the following values:
|
|
RegDisposition_OpenAlways - Open the key if it exists,
|
|
otherwise, create the key.
|
|
RegDisposition_OpenExisting - Open the key f it exists,
|
|
otherwise, fail with CR_NO_SUCH_REGISTRY_KEY.
|
|
|
|
phkClass Supplies the address of the variable that receives an opened
|
|
handle to the specified key. When access to this key is
|
|
completed, it must be closed via RegCloseKey.
|
|
|
|
ulFlags Must be zero.
|
|
|
|
hMachine Machine handle returned from CM_Connect_Machine or NULL.
|
|
|
|
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;
|
|
LONG RegStatus = ERROR_SUCCESS;
|
|
HKEY hRootKey = NULL, hClassKey = NULL, hRemoteKey = NULL;
|
|
WCHAR szStringGuid[MAX_GUID_STRING_LEN],
|
|
szMachineName[MAX_COMPUTERNAME_LENGTH + 1];
|
|
PVOID hStringTable = NULL;
|
|
|
|
|
|
try {
|
|
//
|
|
// validate input parameters
|
|
//
|
|
if (phkClass == NULL) {
|
|
Status = CR_INVALID_POINTER;
|
|
}
|
|
|
|
*phkClass = NULL;
|
|
|
|
if (INVALID_FLAGS(Disposition, RegDisposition_Bits)) {
|
|
Status = CR_INVALID_DATA;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (INVALID_FLAGS(ulFlags, 0)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// get reg key for HKEY_LOCAL_MACHINE
|
|
//
|
|
if (hMachine == NULL) {
|
|
//
|
|
// local call
|
|
//
|
|
hRootKey = HKEY_LOCAL_MACHINE;
|
|
}
|
|
else {
|
|
//
|
|
// setup string table handle and retreive machine name
|
|
//
|
|
if (!PnPGetGlobalHandles(hMachine, &hStringTable, NULL)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
PnPRetrieveMachineName(hMachine, szMachineName);
|
|
|
|
//
|
|
// connect to HKEY_LOCAL_MACHINE on remote machine
|
|
//
|
|
RegStatus = RegConnectRegistry(szMachineName, HKEY_LOCAL_MACHINE,
|
|
&hRemoteKey);
|
|
|
|
if (RegStatus != ERROR_SUCCESS) {
|
|
Status = CR_REGISTRY_ERROR;
|
|
goto Clean0;
|
|
}
|
|
|
|
hRootKey = hRemoteKey;
|
|
}
|
|
|
|
|
|
//
|
|
// attempt to open the class reg key
|
|
//
|
|
RegStatus = RegOpenKeyEx(hRootKey, pszRegPathClass, 0, samDesired,
|
|
&hClassKey);
|
|
|
|
if (RegStatus != ERROR_SUCCESS) {
|
|
Status = CR_REGISTRY_ERROR;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// if no guid specified, return the class key now
|
|
//
|
|
if (ClassGuid == NULL) {
|
|
*phkClass = hClassKey;
|
|
hClassKey = NULL;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// GUID was specified, form a string from the guid
|
|
//
|
|
if (!GuidToString(ClassGuid, szStringGuid)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// attempt to open/create that key
|
|
//
|
|
if (Disposition == RegDisposition_OpenAlways) {
|
|
|
|
ULONG ulDisposition;
|
|
|
|
RegStatus = RegCreateKeyEx(hClassKey, szStringGuid, 0, NULL,
|
|
REG_OPTION_NON_VOLATILE, samDesired,
|
|
NULL, phkClass, &ulDisposition);
|
|
|
|
if (pszClassName != NULL &&
|
|
RegStatus == ERROR_SUCCESS &&
|
|
ulDisposition == REG_CREATED_NEW_KEY) {
|
|
RegSetValueEx(*phkClass, pszRegValueClass, 0, REG_SZ,
|
|
(LPBYTE)pszClassName,
|
|
lstrlen(pszClassName) * sizeof(WCHAR));
|
|
}
|
|
|
|
} else {
|
|
RegStatus = RegOpenKeyEx(hClassKey, szStringGuid, 0, samDesired,
|
|
phkClass);
|
|
}
|
|
|
|
if (RegStatus != ERROR_SUCCESS) {
|
|
*phkClass = NULL;
|
|
Status = CR_NO_SUCH_REGISTRY_KEY;
|
|
goto Clean0;
|
|
}
|
|
|
|
|
|
Clean0:
|
|
;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
if (hClassKey != NULL) {
|
|
RegCloseKey(hClassKey);
|
|
}
|
|
if (hRemoteKey != NULL) {
|
|
RegCloseKey(hRemoteKey);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // CM_Open_Class_Key_ExW
|
|
|
|
|
|
|
|
|
|
CONFIGRET
|
|
CM_Enumerate_Classes_Ex(
|
|
IN ULONG ulClassIndex,
|
|
OUT LPGUID ClassGuid,
|
|
IN ULONG ulFlags,
|
|
IN HMACHINE hMachine
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine enumerates the installed classes in the system. It
|
|
retrieves the GUID string for a single class each time it is called.
|
|
To enumerate installed classes, an application should initially call the
|
|
CM_Enumerate_Classes function with the ulClassIndex parameter set to
|
|
zero. The application should then increment the ulClassIndex parameter
|
|
and call CM_Enumerate_Classes until there are no more classes (until the
|
|
function returns CR_NO_SUCH_VALUE).
|
|
|
|
It is possible to receive a CR_INVALID_DATA error while enumerating
|
|
installed classes. This may happen if the registry key represented by
|
|
the specified index is determined to be an invalid class key. Such keys
|
|
should be ignored during enumeration.
|
|
|
|
Parameters:
|
|
|
|
ulClassIndex Supplies the index of the class to retrieve the class
|
|
GUID string for.
|
|
|
|
ClassGuid Supplies the address of a variable that receives the GUID
|
|
for the class whose index is specified by ulClassIndex.
|
|
|
|
ulFlags Must be zero.
|
|
|
|
hMachine Machine handle returned from CM_Connect_Machine or NULL.
|
|
|
|
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_NO_SUCH_VALUE,
|
|
CR_REGISTRY_ERROR,
|
|
CR_REMOTE_COMM_FAILURE,
|
|
CR_MACHINE_UNAVAILABLE,
|
|
CR_FAILURE.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
WCHAR szClassGuid[MAX_GUID_STRING_LEN];
|
|
ULONG ulLength = MAX_GUID_STRING_LEN;
|
|
handle_t hBinding = NULL;
|
|
|
|
|
|
try {
|
|
//
|
|
// validate input parameters
|
|
//
|
|
if (ClassGuid == NULL) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (INVALID_FLAGS(ulFlags, 0)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// initialize guid struct
|
|
//
|
|
UuidCreateNil(ClassGuid);
|
|
|
|
//
|
|
// setup rpc binding handle
|
|
//
|
|
if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
|
|
RpcTryExcept {
|
|
//
|
|
// call rpc service entry point
|
|
//
|
|
Status = PNP_EnumerateSubKeys(
|
|
hBinding, // rpc binding handle
|
|
PNP_CLASS_SUBKEYS, // subkeys of class branch
|
|
ulClassIndex, // index of class key to enumerate
|
|
szClassGuid, // will contain class name
|
|
ulLength, // length of Buffer in chars,
|
|
&ulLength, // size copied (or size required)
|
|
ulFlags); // currently unused
|
|
}
|
|
RpcExcept (1) {
|
|
PnPTrace(
|
|
TEXT("PNP_EnumerateSubKeys caused an exception (%d)\n"),
|
|
RpcExceptionCode());
|
|
Status = MapRpcExceptionToCR(RpcExceptionCode());
|
|
}
|
|
RpcEndExcept
|
|
|
|
|
|
if (Status == CR_SUCCESS) {
|
|
if (!GuidFromString(szClassGuid, ClassGuid)) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
}
|
|
|
|
|
|
Clean0:
|
|
;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // CM_Enumerate_Classes_Ex
|
|
|
|
|
|
|
|
|
|
CONFIGRET
|
|
CM_Get_Class_Name_ExW(
|
|
IN LPGUID ClassGuid,
|
|
OUT PTCHAR Buffer,
|
|
IN OUT PULONG pulLength,
|
|
IN ULONG ulFlags,
|
|
IN HMACHINE hMachine
|
|
)
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves the class name associated with the specified
|
|
class GUID string.
|
|
|
|
Parameters:
|
|
|
|
ClassGuid Supplies a pointer to the class GUID whose name
|
|
is to be retrieved.
|
|
|
|
Buffer Supplies the address of the character buffer that receives
|
|
the class name corresponding to the specified GUID.
|
|
|
|
pulLength Supplies the address of the variable that contains the
|
|
length, in characters, of the Buffer. Upon return, this
|
|
variable will contain the number of characters (including
|
|
terminating NULL) written to Buffer (if the supplied buffer
|
|
isn't large enough, then the routine will fail with
|
|
CR_BUFFER_SMALL, and this value will indicate how large the
|
|
buffer needs to be in order to succeed).
|
|
|
|
ulFlags Must be zero.
|
|
|
|
hMachine Machine handle returned from CM_Connect_Machine or NULL.
|
|
|
|
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;
|
|
WCHAR szStringGuid[MAX_GUID_STRING_LEN];
|
|
handle_t hBinding = NULL;
|
|
|
|
|
|
try {
|
|
//
|
|
// validate input parameters
|
|
//
|
|
if (ClassGuid == NULL ||
|
|
Buffer == NULL ||
|
|
pulLength == NULL) {
|
|
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (INVALID_FLAGS(ulFlags, 0)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// convert from guid to string
|
|
//
|
|
if (!GuidToString(ClassGuid, szStringGuid)) {
|
|
Status = CR_INVALID_DATA;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// setup rpc binding handle
|
|
//
|
|
if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
|
|
RpcTryExcept {
|
|
//
|
|
// call rpc service entry point
|
|
//
|
|
Status = PNP_GetClassName(
|
|
hBinding, // rpc binding handle
|
|
szStringGuid,
|
|
Buffer,
|
|
pulLength, // returns count of keys under Class
|
|
ulFlags); // not used
|
|
}
|
|
RpcExcept (1) {
|
|
PnPTrace(
|
|
TEXT("PNP_GetClassName caused an exception (%d)\n"),
|
|
RpcExceptionCode());
|
|
Status = MapRpcExceptionToCR(RpcExceptionCode());
|
|
}
|
|
RpcEndExcept
|
|
|
|
|
|
Clean0:
|
|
;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // CM_Get_Class_Name_ExW
|
|
|
|
|
|
|
|
|
|
CONFIGRET
|
|
CM_Get_Class_Key_Name_ExW(
|
|
IN LPGUID ClassGuid,
|
|
OUT LPWSTR pszKeyName,
|
|
IN OUT PULONG pulLength,
|
|
IN ULONG ulFlags,
|
|
IN HMACHINE hMachine
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves the class name associated with the specified
|
|
class GUID string.
|
|
|
|
Parameters:
|
|
|
|
ClassGuid Supplies a pointer to the class GUID whose name
|
|
is to be retrieved.
|
|
|
|
pszKeyName Returns the name of the class key in the registry that
|
|
corresponds to the specified ClassGuid. The returned key
|
|
name is relative to
|
|
HKLM\System\CurrentControlSet\Control\Class.
|
|
|
|
pulLength Supplies the address of the variable that contains the
|
|
length, in characters, of the Buffer. Upon return, this
|
|
variable will contain the number of characters (including
|
|
terminating NULL) written to Buffer (if the supplied buffer
|
|
isn't large enough, then the routine will fail with
|
|
CR_BUFFER_SMALL, and this value will indicate how large the
|
|
buffer needs to be in order to succeed).
|
|
|
|
ulFlags Must be zero.
|
|
|
|
hMachine Machine handle returned from CM_Connect_Machine or NULL.
|
|
|
|
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;
|
|
WCHAR szStringGuid[MAX_GUID_STRING_LEN];
|
|
handle_t hBinding = NULL;
|
|
|
|
|
|
try {
|
|
//
|
|
// validate input parameters
|
|
//
|
|
if (pulLength == NULL) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (ClassGuid == NULL || pszKeyName == NULL) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (INVALID_FLAGS(ulFlags, 0)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (*pulLength < MAX_GUID_STRING_LEN) {
|
|
*pulLength = MAX_GUID_STRING_LEN;
|
|
Status = CR_BUFFER_SMALL;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// convert from guid to string
|
|
//
|
|
if (GuidToString(ClassGuid, pszKeyName)) {
|
|
*pulLength = MAX_GUID_STRING_LEN;
|
|
} else {
|
|
Status = CR_INVALID_DATA;
|
|
}
|
|
|
|
Clean0:
|
|
;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // CM_Get_Class_Key_Name_ExW
|
|
|
|
|
|
|
|
|
|
CONFIGRET
|
|
CM_Delete_Class_Key_Ex(
|
|
IN LPGUID ClassGuid,
|
|
IN ULONG ulFlags,
|
|
IN HANDLE hMachine
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine deletes the specified class key from the registry.
|
|
|
|
Parameters:
|
|
|
|
ClassGuid Supplies a pointer to the class GUID to delete.
|
|
|
|
ulFlags Must be one of the following values:
|
|
CM_DELETE_CLASS_ONLY - only deletes the class key if it
|
|
doesn't have any subkeys.
|
|
CM_DELETE_CLASS_SUBKEYS - deletes the class key and any
|
|
subkeys of the class key.
|
|
|
|
hMachine Machine handle returned from CM_Connect_Machine or NULL.
|
|
|
|
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;
|
|
WCHAR szStringGuid[MAX_GUID_STRING_LEN];
|
|
handle_t hBinding = NULL;
|
|
|
|
|
|
try {
|
|
//
|
|
// validate input parameters
|
|
//
|
|
if (ClassGuid == NULL) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (INVALID_FLAGS(ulFlags, CM_DELETE_CLASS_BITS)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// convert from guid to string
|
|
//
|
|
if (!GuidToString(ClassGuid, szStringGuid)) {
|
|
Status = CR_INVALID_DATA;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// setup rpc binding handle
|
|
//
|
|
if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
|
|
RpcTryExcept {
|
|
//
|
|
// call rpc service entry point
|
|
//
|
|
Status = PNP_DeleteClassKey(
|
|
hBinding, // rpc binding handle
|
|
szStringGuid,
|
|
ulFlags);
|
|
}
|
|
RpcExcept (1) {
|
|
PnPTrace(
|
|
TEXT("PNP_DeleteClassKey caused an exception (%d)\n"),
|
|
RpcExceptionCode());
|
|
Status = MapRpcExceptionToCR(RpcExceptionCode());
|
|
}
|
|
RpcEndExcept
|
|
|
|
|
|
Clean0:
|
|
;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // CM_Delete_Class_Key_Ex
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Local Stubs
|
|
//-------------------------------------------------------------------
|
|
|
|
|
|
CONFIGRET
|
|
CM_Get_DevNode_Registry_PropertyW(
|
|
IN DEVINST dnDevInst,
|
|
IN ULONG ulProperty,
|
|
OUT PULONG pulRegDataType, OPTIONAL
|
|
OUT PVOID Buffer, OPTIONAL
|
|
IN OUT PULONG pulLength,
|
|
IN ULONG ulFlags
|
|
)
|
|
{
|
|
return CM_Get_DevNode_Registry_Property_ExW(dnDevInst, ulProperty,
|
|
pulRegDataType, Buffer,
|
|
pulLength, ulFlags, NULL);
|
|
}
|
|
|
|
|
|
CONFIGRET
|
|
CM_Get_DevNode_Registry_PropertyA(
|
|
IN DEVINST dnDevInst,
|
|
IN ULONG ulProperty,
|
|
OUT PULONG pulRegDataType, OPTIONAL
|
|
OUT PVOID Buffer, OPTIONAL
|
|
IN OUT PULONG pulLength,
|
|
IN ULONG ulFlags
|
|
)
|
|
{
|
|
return CM_Get_DevNode_Registry_Property_ExA(dnDevInst, ulProperty,
|
|
pulRegDataType, Buffer,
|
|
pulLength, ulFlags, NULL);
|
|
}
|
|
|
|
|
|
CONFIGRET
|
|
CM_Set_DevNode_Registry_PropertyW(
|
|
IN DEVINST dnDevInst,
|
|
IN ULONG ulProperty,
|
|
IN PCVOID Buffer, OPTIONAL
|
|
IN OUT ULONG ulLength,
|
|
IN ULONG ulFlags
|
|
)
|
|
{
|
|
return CM_Set_DevNode_Registry_Property_ExW(dnDevInst, ulProperty, Buffer,
|
|
ulLength, ulFlags, NULL);
|
|
}
|
|
|
|
|
|
CONFIGRET
|
|
CM_Set_DevNode_Registry_PropertyA(
|
|
IN DEVINST dnDevInst,
|
|
IN ULONG ulProperty,
|
|
IN PCVOID Buffer, OPTIONAL
|
|
IN OUT ULONG ulLength,
|
|
IN ULONG ulFlags
|
|
)
|
|
{
|
|
return CM_Set_DevNode_Registry_Property_ExA(dnDevInst, ulProperty, Buffer,
|
|
ulLength, ulFlags, NULL);
|
|
}
|
|
|
|
|
|
CONFIGRET
|
|
CM_Open_DevNode_Key(
|
|
IN DEVINST dnDevNode,
|
|
IN REGSAM samDesired,
|
|
IN ULONG ulHardwareProfile,
|
|
IN REGDISPOSITION Disposition,
|
|
OUT PHKEY phkDevice,
|
|
IN ULONG ulFlags
|
|
)
|
|
{
|
|
return CM_Open_DevNode_Key_Ex(dnDevNode, samDesired, ulHardwareProfile,
|
|
Disposition, phkDevice, ulFlags, NULL);
|
|
}
|
|
|
|
|
|
CONFIGRET
|
|
CM_Delete_DevNode_Key(
|
|
IN DEVNODE dnDevNode,
|
|
IN ULONG ulHardwareProfile,
|
|
IN ULONG ulFlags
|
|
)
|
|
|
|
{
|
|
return CM_Delete_DevNode_Key_Ex(dnDevNode, ulHardwareProfile,
|
|
ulFlags, NULL);
|
|
}
|
|
|
|
|
|
CONFIGRET
|
|
CM_Open_Class_KeyW(
|
|
IN LPGUID ClassGuid, OPTIONAL
|
|
IN LPCWSTR pszClassName, OPTIONAL
|
|
IN REGSAM samDesired,
|
|
IN REGDISPOSITION Disposition,
|
|
OUT PHKEY phkClass,
|
|
IN ULONG ulFlags
|
|
)
|
|
{
|
|
return CM_Open_Class_Key_ExW(ClassGuid, pszClassName, samDesired,
|
|
Disposition, phkClass, ulFlags, NULL);
|
|
}
|
|
|
|
|
|
CONFIGRET
|
|
CM_Open_Class_KeyA(
|
|
IN LPGUID ClassGuid, OPTIONAL
|
|
IN LPCSTR pszClassName, OPTIONAL
|
|
IN REGSAM samDesired,
|
|
IN REGDISPOSITION Disposition,
|
|
OUT PHKEY phkClass,
|
|
IN ULONG ulFlags
|
|
)
|
|
{
|
|
return CM_Open_Class_Key_ExA(ClassGuid, pszClassName, samDesired,
|
|
Disposition, phkClass, ulFlags, NULL);
|
|
}
|
|
|
|
|
|
CONFIGRET
|
|
CM_Enumerate_Classes(
|
|
IN ULONG ulClassIndex,
|
|
OUT LPGUID ClassGuid,
|
|
IN ULONG ulFlags
|
|
)
|
|
{
|
|
return CM_Enumerate_Classes_Ex(ulClassIndex, ClassGuid, ulFlags, NULL);
|
|
}
|
|
|
|
|
|
CONFIGRET
|
|
CM_Get_Class_NameW(
|
|
IN LPGUID ClassGuid,
|
|
OUT PWCHAR Buffer,
|
|
IN OUT PULONG pulLength,
|
|
IN ULONG ulFlags
|
|
)
|
|
{
|
|
return CM_Get_Class_Name_ExW(ClassGuid, Buffer, pulLength, ulFlags, NULL);
|
|
}
|
|
|
|
|
|
CONFIGRET
|
|
CM_Get_Class_NameA(
|
|
IN LPGUID ClassGuid,
|
|
OUT PCHAR Buffer,
|
|
IN OUT PULONG pulLength,
|
|
IN ULONG ulFlags
|
|
)
|
|
{
|
|
return CM_Get_Class_Name_ExA(ClassGuid, Buffer, pulLength, ulFlags, NULL);
|
|
}
|
|
|
|
|
|
CONFIGRET
|
|
CM_Get_Class_Key_NameA(
|
|
IN LPGUID ClassGuid,
|
|
OUT LPSTR pszKeyName,
|
|
IN OUT PULONG pulLength,
|
|
IN ULONG ulFlags
|
|
)
|
|
{
|
|
return CM_Get_Class_Key_Name_ExA(ClassGuid, pszKeyName, pulLength,
|
|
ulFlags, NULL);
|
|
}
|
|
|
|
|
|
CONFIGRET
|
|
CM_Get_Class_Key_NameW(
|
|
IN LPGUID ClassGuid,
|
|
OUT LPWSTR pszKeyName,
|
|
IN OUT PULONG pulLength,
|
|
IN ULONG ulFlags
|
|
)
|
|
{
|
|
return CM_Get_Class_Key_Name_ExW(ClassGuid, pszKeyName, pulLength,
|
|
ulFlags, NULL);
|
|
}
|
|
|
|
|
|
CONFIGRET
|
|
CM_Delete_Class_Key(
|
|
IN LPGUID ClassGuid,
|
|
IN ULONG ulFlags
|
|
)
|
|
{
|
|
return CM_Delete_Class_Key_Ex(ClassGuid, ulFlags, NULL);
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// ANSI STUBS
|
|
//-------------------------------------------------------------------
|
|
|
|
|
|
|
|
CONFIGRET
|
|
CM_Get_DevNode_Registry_Property_ExA(
|
|
IN DEVINST dnDevInst,
|
|
IN ULONG ulProperty,
|
|
OUT PULONG pulRegDataType, OPTIONAL
|
|
OUT PVOID Buffer, OPTIONAL
|
|
IN OUT PULONG pulLength,
|
|
IN ULONG ulFlags,
|
|
IN HMACHINE hMachine
|
|
)
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
ULONG ulDataType = 0, UniLen = 0, AnsiBufferLen = 0;
|
|
PWSTR pUniBuffer = NULL, pUniString = NULL;
|
|
PSTR pAnsiString = NULL;
|
|
|
|
//
|
|
// validate essential parameters only
|
|
//
|
|
if (pulLength == NULL) {
|
|
return CR_INVALID_POINTER;
|
|
}
|
|
|
|
//
|
|
// examine datatype to see if need to convert return data
|
|
//
|
|
ulDataType = GetPropertyDataType(ulProperty);
|
|
|
|
if (ulDataType == REG_SZ ||
|
|
ulDataType == REG_MULTI_SZ ||
|
|
ulDataType == REG_EXPAND_SZ) {
|
|
|
|
if (Buffer != NULL) {
|
|
//
|
|
// pass a Unicode buffer instead and convert back to caller's
|
|
// ANSI buffer on return
|
|
//
|
|
UniLen = *pulLength * sizeof(WCHAR);
|
|
pUniBuffer = malloc(UniLen);
|
|
if (pUniBuffer == NULL) {
|
|
return CR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
Status = CM_Get_DevNode_Registry_Property_ExW(dnDevInst,
|
|
ulProperty,
|
|
pulRegDataType,
|
|
pUniBuffer,
|
|
&UniLen,
|
|
ulFlags,
|
|
hMachine);
|
|
if (Status == CR_SUCCESS) {
|
|
|
|
if (ulDataType == REG_MULTI_SZ) {
|
|
//
|
|
// must convert the multi_sz list to ansi
|
|
//
|
|
AnsiBufferLen = *pulLength;
|
|
|
|
for (pUniString = pUniBuffer, pAnsiString = Buffer;
|
|
*pUniString;
|
|
pUniString += lstrlen(pUniString) + 1) {
|
|
|
|
Status = PnPUnicodeToMultiByte(pUniString,
|
|
pAnsiString,
|
|
AnsiBufferLen);
|
|
if (Status != CR_SUCCESS) {
|
|
free(pUniBuffer);
|
|
return Status;
|
|
}
|
|
|
|
AnsiBufferLen -= lstrlenA(pAnsiString);
|
|
pAnsiString += lstrlenA(pAnsiString) + 1;
|
|
}
|
|
*(pAnsiString++) = 0x0; // add second null term
|
|
|
|
} else {
|
|
//
|
|
// just convert the single unicode string to ansi
|
|
//
|
|
Status = PnPUnicodeToMultiByte(pUniBuffer,
|
|
Buffer,
|
|
*pulLength);
|
|
}
|
|
}
|
|
|
|
free(pUniBuffer);
|
|
|
|
} else {
|
|
//
|
|
// no return buffer to convert, pass through to Wide version
|
|
//
|
|
Status = CM_Get_DevNode_Registry_Property_ExW(dnDevInst,
|
|
ulProperty,
|
|
pulRegDataType,
|
|
NULL,
|
|
&UniLen,
|
|
ulFlags,
|
|
hMachine);
|
|
}
|
|
|
|
if (Status == CR_SUCCESS || Status == CR_BUFFER_SMALL) {
|
|
//
|
|
// sizes are always in bytes, must convert to size for ANSI
|
|
//
|
|
*pulLength = UniLen / sizeof(WCHAR);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// for the non-string registry data types, just pass call
|
|
// on through to the Wide version
|
|
//
|
|
Status = CM_Get_DevNode_Registry_Property_ExW(dnDevInst,
|
|
ulProperty,
|
|
pulRegDataType,
|
|
Buffer,
|
|
pulLength,
|
|
ulFlags,
|
|
hMachine);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // CM_Get_DevNode_Registry_Property_ExA
|
|
|
|
|
|
|
|
|
|
CONFIGRET
|
|
CM_Set_DevNode_Registry_Property_ExA(
|
|
IN DEVINST dnDevInst,
|
|
IN ULONG ulProperty,
|
|
IN PCVOID Buffer, OPTIONAL
|
|
IN ULONG ulLength,
|
|
IN ULONG ulFlags,
|
|
IN HMACHINE hMachine
|
|
)
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
ULONG ulDataType = 0, UniSize = 0, UniBufferSize = 0;
|
|
PWSTR pUniBuffer = NULL, pUniString = NULL, pUniNext = NULL;
|
|
PSTR pAnsiString = NULL;
|
|
|
|
//
|
|
// validate essential parameters only
|
|
//
|
|
if (Buffer == NULL && ulLength != 0) {
|
|
return CR_INVALID_POINTER;
|
|
}
|
|
|
|
if (Buffer == NULL) {
|
|
//
|
|
// No need to convert the parameter
|
|
//
|
|
return CM_Set_DevNode_Registry_Property_ExW(dnDevInst,
|
|
ulProperty,
|
|
Buffer,
|
|
ulLength,
|
|
ulFlags,
|
|
hMachine);
|
|
}
|
|
|
|
//
|
|
// examine datatype to see if need to convert input buffer
|
|
//
|
|
ulDataType = GetPropertyDataType(ulProperty);
|
|
|
|
if (ulDataType == REG_SZ || ulDataType == REG_EXPAND_SZ) {
|
|
//
|
|
// convert buffer string data to unicode and pass to wide version
|
|
//
|
|
if (CaptureAndConvertAnsiArg(Buffer, &pUniBuffer) == NO_ERROR) {
|
|
|
|
UniSize = (lstrlen(pUniBuffer)+1) * sizeof(WCHAR);
|
|
|
|
Status = CM_Set_DevNode_Registry_Property_ExW(dnDevInst,
|
|
ulProperty,
|
|
pUniBuffer,
|
|
UniSize,
|
|
ulFlags,
|
|
hMachine);
|
|
MyFree(pUniBuffer);
|
|
}
|
|
|
|
} else if (ulDataType == REG_MULTI_SZ) {
|
|
//
|
|
// must convert the multi_sz list to unicode first
|
|
//
|
|
UniBufferSize = ulLength * sizeof(WCHAR);
|
|
pUniBuffer = malloc(UniBufferSize);
|
|
if (pUniBuffer == NULL) {
|
|
return CR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
for (pAnsiString = (PSTR)Buffer, pUniNext = pUniBuffer;
|
|
*pAnsiString;
|
|
pAnsiString += lstrlenA(pAnsiString) + 1) {
|
|
|
|
if (CaptureAndConvertAnsiArg(pAnsiString, &pUniString) == NO_ERROR) {
|
|
|
|
UniSize += (lstrlen(pUniString)+1) * sizeof(WCHAR);
|
|
|
|
if (UniSize >= UniBufferSize) {
|
|
MyFree(pUniString);
|
|
return CR_INVALID_DATA;
|
|
}
|
|
|
|
lstrcpy(pUniNext, pUniString);
|
|
pUniNext += lstrlen(pUniNext) + 1;
|
|
|
|
MyFree(pUniString);
|
|
}
|
|
}
|
|
*(pUniNext++) = 0x0; // add second null term
|
|
|
|
Status = CM_Set_DevNode_Registry_Property_ExW(dnDevInst,
|
|
ulProperty,
|
|
pUniBuffer,
|
|
UniBufferSize,
|
|
ulFlags,
|
|
hMachine);
|
|
free(pUniBuffer);
|
|
|
|
} else {
|
|
|
|
Status = CM_Set_DevNode_Registry_Property_ExW(dnDevInst,
|
|
ulProperty,
|
|
Buffer,
|
|
ulLength,
|
|
ulFlags,
|
|
hMachine);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // CM_Set_DevNode_Registry_Property_ExA
|
|
|
|
|
|
|
|
|
|
CONFIGRET
|
|
CM_Open_Class_Key_ExA(
|
|
IN LPGUID ClassGuid, OPTIONAL
|
|
IN LPCSTR pszClassName, OPTIONAL
|
|
IN REGSAM samDesired,
|
|
IN REGDISPOSITION Disposition,
|
|
OUT PHKEY phkClass,
|
|
IN ULONG ulFlags,
|
|
IN HMACHINE hMachine
|
|
)
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
|
|
if (pszClassName != NULL) {
|
|
|
|
PWSTR pUniClassName = NULL;
|
|
|
|
if (CaptureAndConvertAnsiArg(pszClassName, &pUniClassName) == NO_ERROR) {
|
|
|
|
Status = CM_Open_Class_Key_ExW(ClassGuid,
|
|
pUniClassName,
|
|
samDesired,
|
|
Disposition,
|
|
phkClass,
|
|
ulFlags,
|
|
hMachine);
|
|
MyFree(pUniClassName);
|
|
|
|
} else {
|
|
Status = CR_INVALID_DATA;
|
|
}
|
|
} else {
|
|
|
|
Status = CM_Open_Class_Key_ExW(ClassGuid,
|
|
NULL,
|
|
samDesired,
|
|
Disposition,
|
|
phkClass,
|
|
ulFlags,
|
|
hMachine);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // CM_Open_Class_Key_ExA
|
|
|
|
|
|
|
|
|
|
CONFIGRET
|
|
CM_Get_Class_Name_ExA(
|
|
IN LPGUID ClassGuid,
|
|
OUT PCHAR Buffer,
|
|
IN OUT PULONG pulLength,
|
|
IN ULONG ulFlags,
|
|
IN HMACHINE hMachine
|
|
)
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
WCHAR UniBuffer[MAX_CLASS_NAME_LEN];
|
|
ULONG AnsiLen, UniLen = MAX_CLASS_NAME_LEN;
|
|
|
|
//
|
|
// validate parameters
|
|
//
|
|
if (Buffer == NULL || pulLength == NULL) {
|
|
return CR_INVALID_POINTER;
|
|
}
|
|
|
|
AnsiLen = *pulLength;
|
|
*pulLength = 0;
|
|
|
|
//
|
|
// call the wide version, passing a unicode buffer as a parameter
|
|
//
|
|
Status = CM_Get_Class_Name_ExW(ClassGuid,
|
|
UniBuffer,
|
|
&UniLen,
|
|
ulFlags,
|
|
hMachine);
|
|
|
|
//
|
|
// convert the unicode buffer to caller's ansi buffer
|
|
//
|
|
if (Status == CR_SUCCESS) {
|
|
|
|
Status = PnPUnicodeToMultiByte(UniBuffer, Buffer, AnsiLen);
|
|
|
|
if (Status == CR_SUCCESS) {
|
|
*pulLength = lstrlenA(Buffer)+1;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // CM_Get_Class_Name_ExA
|
|
|
|
|
|
|
|
CONFIGRET
|
|
CM_Get_Class_Key_Name_ExA(
|
|
IN LPGUID ClassGuid,
|
|
OUT LPSTR pszKeyName,
|
|
IN OUT PULONG pulLength,
|
|
IN ULONG ulFlags,
|
|
IN HMACHINE hMachine
|
|
)
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
WCHAR UniBuffer[MAX_GUID_STRING_LEN];
|
|
ULONG AnsiLen, UniLen = MAX_GUID_STRING_LEN;
|
|
|
|
//
|
|
// validate parameters
|
|
//
|
|
if (ClassGuid == NULL || pszKeyName == NULL || pulLength == NULL) {
|
|
return CR_INVALID_POINTER;
|
|
}
|
|
|
|
AnsiLen = *pulLength;
|
|
*pulLength = 0;
|
|
|
|
//
|
|
// call the wide version, passing a unicode buffer as a parameter
|
|
//
|
|
Status = CM_Get_Class_Key_Name_ExW(ClassGuid,
|
|
UniBuffer,
|
|
&UniLen,
|
|
ulFlags,
|
|
hMachine);
|
|
|
|
//
|
|
// convert the unicode buffer to an ansi string and copy to the
|
|
// caller's buffer
|
|
//
|
|
if (Status == CR_SUCCESS) {
|
|
|
|
Status = PnPUnicodeToMultiByte(UniBuffer, pszKeyName, AnsiLen);
|
|
|
|
if (Status == CR_SUCCESS) {
|
|
*pulLength = lstrlenA(pszKeyName)+1;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // CM_Get_Class_Key_Name_ExA
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Private utility routines
|
|
//-------------------------------------------------------------------
|
|
|
|
|
|
|
|
ULONG
|
|
GetPropertyDataType(
|
|
IN ULONG ulProperty)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes a property ID and returns the registry data type that
|
|
is used to store this property data (i.e., REG_SZ, etc).
|
|
Parameters:
|
|
|
|
ulProperty Property ID (one of the CM_DRP_* defines)
|
|
|
|
Return Value:
|
|
|
|
Returns one of the predefined registry data types, REG_SZ is the default.
|
|
|
|
--*/
|
|
|
|
{
|
|
switch(ulProperty) {
|
|
|
|
case CM_DRP_CONFIGURATION:
|
|
case CM_DRP_CONFIGURATIONVECTOR:
|
|
return REG_BINARY; // BUGBUG
|
|
|
|
case CM_DRP_DEVICEDESC:
|
|
case CM_DRP_SERVICE:
|
|
case CM_DRP_CLASS:
|
|
case CM_DRP_CLASSGUID:
|
|
case CM_DRP_DRIVER:
|
|
case CM_DRP_MFG:
|
|
case CM_DRP_FRIENDLYNAME:
|
|
return REG_SZ;
|
|
|
|
case CM_DRP_HARDWAREID:
|
|
case CM_DRP_COMPATIBLEIDS:
|
|
case CM_DRP_NTDEVICEPATHS:
|
|
return REG_MULTI_SZ;
|
|
|
|
case CM_DRP_CONFIGFLAGS:
|
|
return REG_DWORD;
|
|
|
|
default:
|
|
return REG_SZ;
|
|
}
|
|
|
|
} // GetPropertyDataType
|
|
|
|
|
|
|
|
|
|
|