/*++ Copyright (c) Microsoft Corporation. All rights reserved. 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_Get_Class_Registry_Property CM_Set_Class_Registry_Property CM_Open_DevNode_Key CM_Delete_DevNode_Key CM_Open_Class_Key CM_Enumerate_Classes CM_Get_Class_Name CM_Get_Class_Key_Name CM_Delete_Class_Key CM_Get_Device_Interface_Alias CM_Get_Device_Interface_List CM_Get_Device_Interface_List_Size CM_Register_Device_Interface CM_Unregister_Device_Interface 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 "cfgi.h" #include "setupapi.h" #include "spapip.h" #include "cmdat.h" // // Private prototypes // ULONG GetPropertyDataType( IN ULONG ulProperty ); // // use these from SetupAPI // PSECURITY_DESCRIPTOR pSetupConvertTextToSD( IN PCWSTR SDS, OUT PULONG SecDescSize ); PWSTR pSetupConvertSDToText( IN PSECURITY_DESCRIPTOR SD, OUT PULONG pSDSSize ); // // 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; WCHAR pDeviceID[MAX_DEVICE_ID_LEN]; ULONG ulSizeID = MAX_DEVICE_ID_LEN; ULONG ulTempDataType=0, ulTransferLen=0; ULONG ulGetProperty = ulProperty; BYTE NullBuffer=0; handle_t hBinding = NULL; PVOID hStringTable = NULL; BOOL Success; try { // // validate parameters // if (dnDevInst == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } if (!ARGUMENT_PRESENT(pulLength)) { Status = CR_INVALID_POINTER; goto Clean0; } if ((!ARGUMENT_PRESENT(Buffer)) && (*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; } if (ulProperty == CM_DRP_SECURITY_SDS) { // // translates operation // LPVOID tmpBuffer = NULL; ULONG tmpBufferSize = 0; ULONG datatype; LPWSTR sds = NULL; ulTempDataType = REG_SZ; try { Status = CM_Get_DevNode_Registry_Property_ExW(dnDevInst, CM_DRP_SECURITY, &datatype, NULL, &tmpBufferSize, ulFlags, hMachine); if (Status != CR_SUCCESS && Status != CR_BUFFER_SMALL) { leave; } tmpBuffer = pSetupMalloc(tmpBufferSize); if (tmpBuffer == NULL) { Status = CR_OUT_OF_MEMORY; leave; } Status = CM_Get_DevNode_Registry_Property_ExW(dnDevInst, CM_DRP_SECURITY, &datatype, tmpBuffer, &tmpBufferSize, ulFlags, hMachine); if (Status != CR_SUCCESS) { leave; } // // now translate // sds = pSetupConvertSDToText((PSECURITY_DESCRIPTOR)tmpBuffer,NULL); if (sds == NULL) { Status = CR_FAILURE; leave; } ulTransferLen = (lstrlen(sds)+1) * sizeof(WCHAR); // required size if (*pulLength == 0 || Buffer == NULL || *pulLength < ulTransferLen) { // // buffer too small, or buffer size wanted // required buffer size // Status = CR_BUFFER_SMALL; *pulLength = ulTransferLen; ulTransferLen = 0; } else { // // copy data // memcpy(Buffer,sds,ulTransferLen); *pulLength = ulTransferLen; } } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } if (tmpBuffer != NULL) { pSetupFree(tmpBuffer); } if (sds != NULL) { // // must use LocalFree // LocalFree(sds); } if (Status != CR_SUCCESS) { goto Clean0; } } else { // // 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 // Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,pDeviceID,&ulSizeID); if (Success == FALSE || 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 ulGetProperty, // 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 (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_GetDeviceRegProp 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: NOTHING; } 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_OUT_OF_MEMORY, CR_INVALID_DATA, or CR_BUFFER_SMALL. --*/ { CONFIGRET Status = CR_SUCCESS; WCHAR pDeviceID [MAX_DEVICE_ID_LEN]; ULONG ulRegDataType = 0, ulLen = MAX_DEVICE_ID_LEN; BYTE NullBuffer = 0x0; handle_t hBinding = NULL; PVOID hStringTable = NULL; BOOL Success; PVOID Buffer2 = NULL; PVOID Buffer3 = NULL; try { // // validate parameters // if (dnDevInst == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } if ((!ARGUMENT_PRESENT(Buffer)) && (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 // Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,pDeviceID,&ulLen); if (Success == FALSE || 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); // // if data type is REG_DWORD, make sure size is right // if((ulRegDataType == REG_DWORD) && ulLength && (ulLength != sizeof(DWORD))) { Status = CR_INVALID_DATA; goto Clean0; } // // if the register is CM_DRP_SECURITY_SDS, convert it // if (ulProperty == CM_DRP_SECURITY_SDS) { if (ulLength) { // // this form of CM_DRP_SECURITY provides a string that needs to be converted to binary // PCWSTR UnicodeSecDesc = (PCWSTR)Buffer; Buffer2 = pSetupConvertTextToSD(UnicodeSecDesc,&ulLength); if (Buffer2 == NULL) { // // If last error is ERROR_SCE_DISABLED, then the failure is // due to SCE APIs being "turned off" on Embedded. Treat // this as a (successful) no-op... // if(GetLastError() == ERROR_SCE_DISABLED) { Status = CR_SUCCESS; } else { Status = CR_INVALID_DATA; } goto Clean0; } Buffer = Buffer2; } ulProperty = CM_DRP_SECURITY; ulRegDataType = REG_BINARY; } // // if data type is REG_MULTI_SZ, make sure it is double-NULL terminated // if ((ulRegDataType == REG_MULTI_SZ) && (ulLength != 0)) { ULONG ulNewLength; PWSTR tmpBuffer, bufferEnd; ulLength &= ~(ULONG)1; tmpBuffer = (PWSTR)Buffer; bufferEnd = (PWSTR)((PUCHAR)tmpBuffer + ulLength); ulNewLength = ulLength; while (tmpBuffer < bufferEnd && *tmpBuffer != TEXT('\0')) { while (tmpBuffer < bufferEnd && *tmpBuffer != TEXT('\0')) { tmpBuffer++; } if (tmpBuffer >= bufferEnd) { ulNewLength += sizeof(TEXT('\0'));; } else { tmpBuffer++; } } if (tmpBuffer >= bufferEnd) { ulNewLength += sizeof(TEXT('\0'));; } else { ulNewLength = ((ULONG)(tmpBuffer - (PWSTR)Buffer) + 1) * sizeof(WCHAR); } if (ulNewLength > ulLength) { Buffer3 = pSetupMalloc(ulNewLength); if (Buffer3 == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } memcpy(Buffer3, Buffer, ulLength); memset((PUCHAR)Buffer3 + ulLength, 0, ulNewLength - ulLength); Buffer = Buffer3; } ulLength = ulNewLength; } 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 (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_SetDeviceRegProp caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } if (Buffer3) { pSetupFree(Buffer3); } if (Buffer2) { // // SceSvc requires LocalFree // LocalFree(Buffer2); } return Status; } // CM_Set_DevNode_Registry_Property_ExW CONFIGRET CM_Get_Class_Registry_PropertyW( IN LPGUID ClassGUID, 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 classes's registry storage key. Parameters: ClassGUID Supplies the Class GUID. 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; ULONG ulTempDataType=0, ulTransferLen=0; BYTE NullBuffer=0; handle_t hBinding = NULL; BOOL Success; WCHAR szStringGuid[MAX_GUID_STRING_LEN]; ULONG ulGetProperty = ulProperty; try { // // validate parameters // if (!ARGUMENT_PRESENT(ClassGUID)) { Status = CR_INVALID_POINTER; goto Clean0; } if (!ARGUMENT_PRESENT(pulLength)) { Status = CR_INVALID_POINTER; goto Clean0; } if ((!ARGUMENT_PRESENT(Buffer)) && (*pulLength != 0)) { Status = CR_INVALID_POINTER; goto Clean0; } if (INVALID_FLAGS(ulFlags, 0)) { Status = CR_INVALID_FLAG; goto Clean0; } // // convert from guid to string // if (pSetupStringFromGuid(ClassGUID, szStringGuid,MAX_GUID_STRING_LEN) != NO_ERROR) { Status = CR_INVALID_DATA; goto Clean0; } if (ulProperty < CM_CRP_MIN || ulProperty > CM_CRP_MAX) { Status = CR_INVALID_PROPERTY; goto Clean0; } if (ulProperty == CM_CRP_SECURITY_SDS) { // // translates operation // LPVOID tmpBuffer = NULL; ULONG tmpBufferSize = 0; ULONG datatype; LPWSTR sds = NULL; ulTempDataType = REG_SZ; try { Status = CM_Get_Class_Registry_PropertyW(ClassGUID,CM_CRP_SECURITY,&datatype,NULL,&tmpBufferSize,ulFlags,hMachine); if (Status != CR_SUCCESS && Status != CR_BUFFER_SMALL) { leave; } tmpBuffer = pSetupMalloc(tmpBufferSize); if (tmpBuffer == NULL) { Status = CR_OUT_OF_MEMORY; leave; } Status = CM_Get_Class_Registry_PropertyW(ClassGUID,CM_CRP_SECURITY,&datatype,tmpBuffer,&tmpBufferSize,ulFlags,hMachine); if (Status != CR_SUCCESS) { leave; } // // now translate // sds = pSetupConvertSDToText((PSECURITY_DESCRIPTOR)tmpBuffer,NULL); if (sds == NULL) { Status = CR_FAILURE; leave; } ulTransferLen = (lstrlen(sds)+1) * sizeof(WCHAR); // required size if (*pulLength == 0 || Buffer == NULL || *pulLength < ulTransferLen) { // // buffer too small, or buffer size wanted // required buffer size // Status = CR_BUFFER_SMALL; *pulLength = ulTransferLen; ulTransferLen = 0; } else { // // copy data // memcpy(Buffer,sds,ulTransferLen); *pulLength = ulTransferLen; } } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } if (tmpBuffer != NULL) { pSetupFree(tmpBuffer); } if (sds != NULL) { // // must use LocalFree // LocalFree(sds); } if (Status != CR_SUCCESS) { goto Clean0; } } else { // // setup rpc binding handle and string table handle // if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) { Status = CR_FAILURE; 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_GetClassRegProp( hBinding, // rpc binding handle szStringGuid, // string representation of class ulGetProperty, // 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 (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_GetClassRegProp 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: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Get_Class_Registry_PropertyW CONFIGRET CM_Set_Class_Registry_PropertyW( IN LPGUID ClassGUID, IN ULONG ulProperty, IN PCVOID Buffer OPTIONAL, IN 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: ClassGUID Supplies the Class GUID. 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_OUT_OF_MEMORY, CR_INVALID_DATA, or CR_BUFFER_SMALL. --*/ { CONFIGRET Status = CR_SUCCESS; ULONG ulRegDataType = 0, ulLen = MAX_DEVICE_ID_LEN; BYTE NullBuffer = 0x0; handle_t hBinding = NULL; BOOL Success; WCHAR szStringGuid[MAX_GUID_STRING_LEN]; PVOID Buffer2 = NULL; PVOID Buffer3 = NULL; try { // // validate parameters // if (!ARGUMENT_PRESENT(ClassGUID)) { Status = CR_INVALID_POINTER; goto Clean0; } if ((!ARGUMENT_PRESENT(Buffer)) && (ulLength != 0)) { Status = CR_INVALID_POINTER; goto Clean0; } if (INVALID_FLAGS(ulFlags, 0)) { Status = CR_INVALID_FLAG; goto Clean0; } // // convert from guid to string // if (pSetupStringFromGuid(ClassGUID, szStringGuid,MAX_GUID_STRING_LEN) != NO_ERROR) { Status = CR_INVALID_DATA; goto Clean0; } if (ulProperty < CM_CRP_MIN || ulProperty > CM_CRP_MAX) { Status = CR_INVALID_PROPERTY; goto Clean0; } // // setup rpc binding handle and string table handle // if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // we need to specify what registry data to use for storing this data // ulRegDataType = GetPropertyDataType(ulProperty); // // if data type is REG_DWORD, make sure size is right // if((ulRegDataType == REG_DWORD) && ulLength && (ulLength != sizeof(DWORD))) { Status = CR_INVALID_DATA; goto Clean0; } // // if the register is CM_CRP_SECURITY_SDS, convert it // if (ulProperty == CM_CRP_SECURITY_SDS) { if (ulLength) { // // this form of CM_CRP_SECURITY provides a string that needs to be converted to binary // PCWSTR UnicodeSecDesc = (PCWSTR)Buffer; Buffer2 = pSetupConvertTextToSD(UnicodeSecDesc,&ulLength); if (Buffer2 == NULL) { // // If last error is ERROR_SCE_DISABLED, then the failure is // due to SCE APIs being "turned off" on Embedded. Treat // this as a (successful) no-op... // if(GetLastError() == ERROR_SCE_DISABLED) { Status = CR_SUCCESS; } else { Status = CR_INVALID_DATA; } goto Clean0; } Buffer = Buffer2; } ulProperty = CM_CRP_SECURITY; ulRegDataType = REG_BINARY; } // // if data type is REG_MULTI_SZ, make sure it is double-NULL terminated // if ((ulRegDataType == REG_MULTI_SZ) && (ulLength != 0)) { ULONG ulNewLength; PWSTR tmpBuffer, bufferEnd; ulLength &= ~(ULONG)1; tmpBuffer = (PWSTR)Buffer; bufferEnd = (PWSTR)((PUCHAR)tmpBuffer + ulLength); ulNewLength = ulLength; while (tmpBuffer < bufferEnd && *tmpBuffer != TEXT('\0')) { while (tmpBuffer < bufferEnd && *tmpBuffer != TEXT('\0')) { tmpBuffer++; } if (tmpBuffer >= bufferEnd) { ulNewLength += sizeof(TEXT('\0'));; } else { tmpBuffer++; } } if (tmpBuffer >= bufferEnd) { ulNewLength += sizeof(TEXT('\0'));; } else { ulNewLength = ((ULONG)(tmpBuffer - (PWSTR)Buffer) + 1) * sizeof(WCHAR); } if (ulNewLength > ulLength) { Buffer3 = pSetupMalloc(ulNewLength); if (Buffer3 == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } memcpy(Buffer3, Buffer, ulLength); memset((PUCHAR)Buffer3 + ulLength, 0, ulNewLength - ulLength); Buffer = Buffer3; } ulLength = ulNewLength; } if (Buffer == NULL) { Buffer = &NullBuffer; } RpcTryExcept { // // call rpc service entry point // Status = PNP_SetClassRegProp( hBinding, // rpc binding handle szStringGuid, // string representation of class 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 (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_SetClassRegProp caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } if (Buffer2) { // // SceSvc requires LocalFree // LocalFree(Buffer2); } if (Buffer3) { pSetupFree(Buffer3); } return Status; } // CM_Set_Class_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\\ \\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\ \ 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_REGISTRY_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; PWSTR pszMachineName = NULL; WCHAR pDeviceID[MAX_DEVICE_ID_LEN]; HKEY hKey=NULL, hRemoteKey=NULL, hBranchKey=NULL; PWSTR pszKey = NULL, pszPrivateKey = NULL; PVOID hStringTable = NULL; handle_t hBinding = NULL; ULONG ulLen = MAX_DEVICE_ID_LEN; BOOL Success; try { // // validate parameters // if (!ARGUMENT_PRESENT(phkDevice)) { 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 // Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevNode,pDeviceID,&ulLen); if (Success == FALSE || 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 // pszMachineName = pSetupMalloc((MAX_PATH + 3)*sizeof(WCHAR)); if (pszMachineName == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } PnPRetrieveMachineName(hMachine, pszMachineName); // // use remote HKLM branch (we only support connect to // HKEY_LOCAL_MACHINE on the remote machine, not HKEY_CURRENT_USER) // RegStatus = RegConnectRegistry(pszMachineName, HKEY_LOCAL_MACHINE, &hRemoteKey); pSetupFree(pszMachineName); pszMachineName = NULL; 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; } // // allocate some buffer space to work with // pszKey = pSetupMalloc(MAX_CM_PATH*sizeof(WCHAR)); if (pszKey == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } pszPrivateKey = pSetupMalloc(MAX_CM_PATH*sizeof(WCHAR)); if (pszPrivateKey == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } // // form the registry path based on the device id and the flags. // Status = GetDevNodeKeyPath(hBinding, pDeviceID, ulFlags, ulHardwareProfile, pszKey, pszPrivateKey); if (Status != CR_SUCCESS) { goto Clean0; } lstrcat(pszKey, TEXT("\\")); lstrcat(pszKey, pszPrivateKey); pSetupFree(pszPrivateKey); pszPrivateKey = NULL; // // 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, pszKey, 0, samDesired, phkDevice); if (RegStatus != ERROR_SUCCESS) { // // call server side to create the key // RpcTryExcept { // // call rpc service entry point // Status = PNP_CreateKey( hBinding, pDeviceID, samDesired, 0); } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "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, pszKey, 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, pszKey, 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, pszKey, 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: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; // // Reference the following variables so the compiler will respect // statement ordering w.r.t. their assignment. // pszMachineName = pszMachineName; pszPrivateKey = pszPrivateKey; pszKey = pszKey; } if (pszMachineName) { pSetupFree(pszMachineName); } if (pszPrivateKey) { pSetupFree(pszPrivateKey); } if (pszKey) { pSetupFree(pszKey); } 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; PWSTR pszParentKey = NULL, pszChildKey = NULL, pszRegStr = NULL; WCHAR pDeviceID[MAX_DEVICE_ID_LEN], szProfile[MAX_PROFILE_ID_LEN]; ULONG ulIndex = 0, ulSize = 0,ulLen = MAX_DEVICE_ID_LEN; BOOL Success; 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 // Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevNode,pDeviceID,&ulLen); if (Success == FALSE || INVALID_DEVINST(pDeviceID)) { Status = CR_INVALID_DEVNODE; goto Clean0; } // // allocate some buffer space to work with // pszParentKey = pSetupMalloc(MAX_CM_PATH*sizeof(WCHAR)); if (pszParentKey == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } pszChildKey = pSetupMalloc(MAX_CM_PATH*sizeof(WCHAR)); if (pszChildKey == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } pszRegStr = pSetupMalloc(MAX_CM_PATH*sizeof(WCHAR)); if (pszRegStr == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } // // form the registry path based on the device id and the flags. // Status = GetDevNodeKeyPath(hBinding, pDeviceID, ulFlags, ulHardwareProfile, pszParentKey, pszChildKey); 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(pszRegStr, TEXT("%s\\%s"), pszRegPathIDConfigDB, pszRegKeyKnownDockingStates); 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 * sizeof(WCHAR); RegStatus = RegEnumKeyEx(hKey, ulIndex, szProfile, &ulSize, NULL, NULL, NULL, NULL); if (RegStatus == ERROR_SUCCESS) { // // pszParentKey contains replacement symbol for the // profile id // wsprintf(pszRegStr, pszParentKey, szProfile); Status = DeletePrivateKey(HKEY_CURRENT_USER, pszRegStr, pszChildKey); if (Status != CR_SUCCESS) { goto Clean0; } } } } else { // // not for all profiles, so just delete the specified key // Status = DeletePrivateKey(HKEY_CURRENT_USER, pszParentKey, pszChildKey); 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 { if (!(ulFlags & CM_REGISTRY_CONFIG)) { ulHardwareProfile = 0; } RpcTryExcept { // // call rpc service entry point // Status = PNP_DeleteRegistryKey( hBinding, // rpc binding handle pDeviceID, // device id pszParentKey, // parent of key to delete pszChildKey, // key to delete ulHardwareProfile); // flags, not used } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_DeleteRegistryKey caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept } Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; // // Reference the following variables so the compiler will respect // statement ordering w.r.t. their assignment. // pszRegStr = pszRegStr; pszChildKey = pszChildKey; pszParentKey = pszParentKey; } if (hKey != NULL) { RegCloseKey(hKey); } if (pszRegStr) { pSetupFree(pszRegStr); } if (pszChildKey) { pSetupFree(pszChildKey); } if (pszParentKey) { pSetupFree(pszParentKey); } 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 ClassGuid. This parameter is only valid if the CM_OPEN_CLASS_KEY_INSTALLER flag is set in the ulFlags parameter. If specified, this string will replace any existing class name associated with this setup class GUID. This parameter must be set to NULL if the CM_OPEN_CLASS_KEY_INTERFACE bit is set in the ulFlags parameter. 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 May be one of the following values: CM_OPEN_CLASS_KEY_INSTALLER - open/create a setup class key for the specified GUID under HKLM\System\CurrentControlSet\Control\Class. CM_OPEN_CLASS_KEY_INTERFACE - open/create a device interface class key for the specified GUID under HKLM\System\CurrentControlSet\Control\DeviceClasses. 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, hRemoteKey = NULL; PWSTR pszMachineName = NULL, pszRegStr = NULL; PVOID hStringTable = NULL; try { // // validate input parameters // if (!ARGUMENT_PRESENT(phkClass)) { Status = CR_INVALID_POINTER; goto Clean0; } *phkClass = NULL; if (INVALID_FLAGS(Disposition, RegDisposition_Bits)) { Status = CR_INVALID_DATA; goto Clean0; } if (INVALID_FLAGS(ulFlags, CM_OPEN_CLASS_KEY_BITS)) { Status = CR_INVALID_FLAG; goto Clean0; } // // If ulFlags == CM_OPEN_CLASS_KEY_INTERFACE then pszClassName had // better be NULL. // if((ulFlags == CM_OPEN_CLASS_KEY_INTERFACE) && pszClassName) { Status = CR_INVALID_DATA; 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; } pszMachineName = pSetupMalloc((MAX_PATH + 3)*sizeof(WCHAR)); if (pszMachineName == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } PnPRetrieveMachineName(hMachine, pszMachineName); // // connect to HKEY_LOCAL_MACHINE on remote machine // RegStatus = RegConnectRegistry(pszMachineName, HKEY_LOCAL_MACHINE, &hRemoteKey); pSetupFree(pszMachineName); pszMachineName = NULL; if (RegStatus != ERROR_SUCCESS) { Status = CR_REGISTRY_ERROR; goto Clean0; } hRootKey = hRemoteKey; } // // allocate some buffer space to work with // pszRegStr = pSetupMalloc(MAX_PATH*sizeof(WCHAR)); if (pszRegStr == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } // // Form the registry path // if (ulFlags == CM_OPEN_CLASS_KEY_INTERFACE) { lstrcpy(pszRegStr, pszRegPathDeviceClass); } else { lstrcpy(pszRegStr, pszRegPathClass); } if (ClassGuid != NULL) { // optional class guid was specified WCHAR szStringGuid[MAX_GUID_STRING_LEN]; if (pSetupStringFromGuid(ClassGuid, szStringGuid,MAX_GUID_STRING_LEN) == NO_ERROR) { lstrcat(pszRegStr, TEXT("\\")); lstrcat(pszRegStr, szStringGuid); } } // // attempt to open/create that key // if (Disposition == RegDisposition_OpenAlways) { ULONG ulDisposition; RegStatus = RegCreateKeyEx(hRootKey, pszRegStr, 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, phkClass, &ulDisposition); } else { RegStatus = RegOpenKeyEx(hRootKey, pszRegStr, 0, samDesired, phkClass); } if((pszClassName != NULL) && (RegStatus == ERROR_SUCCESS)) { RegSetValueEx(*phkClass, pszRegValueClass, 0, REG_SZ, (LPBYTE)pszClassName, (lstrlen(pszClassName) + 1) * sizeof(WCHAR)); } if (RegStatus != ERROR_SUCCESS) { *phkClass = NULL; Status = CR_NO_SUCH_REGISTRY_KEY; goto Clean0; } Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; // // Reference the following variables so the compiler will respect // statement ordering w.r.t. their assignment. // pszMachineName = pszMachineName; pszRegStr = pszRegStr; } if (pszMachineName != NULL) { pSetupFree(pszMachineName); } if (pszRegStr != NULL) { pSetupFree(pszRegStr); } 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 (!ARGUMENT_PRESENT(ClassGuid)) { Status = CR_INVALID_POINTER; goto Clean0; } if (INVALID_FLAGS(ulFlags, 0)) { Status = CR_INVALID_FLAG; goto Clean0; } // // initialize guid struct // ZeroMemory(ClassGuid, sizeof(GUID)); // // 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 (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_EnumerateSubKeys caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept if (Status == CR_SUCCESS) { if (pSetupGuidFromString(szClassGuid, ClassGuid) != NO_ERROR) { Status = CR_FAILURE; } } Clean0: NOTHING; } 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 ((!ARGUMENT_PRESENT(ClassGuid)) || (!ARGUMENT_PRESENT(Buffer)) || (!ARGUMENT_PRESENT(pulLength))) { Status = CR_INVALID_POINTER; goto Clean0; } if (INVALID_FLAGS(ulFlags, 0)) { Status = CR_INVALID_FLAG; goto Clean0; } // // convert from guid to string // if (pSetupStringFromGuid(ClassGuid, szStringGuid,MAX_GUID_STRING_LEN) != NO_ERROR) { 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 (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_GetClassName caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: NOTHING; } 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; handle_t hBinding = NULL; // // NOTE: the supplied machine handle is never referenced by this routine, // since it is known/assumed that the key name of the class key requested is // always just the string representation of the supplied class GUID. // there is no corresponding UMPNPMGR server-side routine. // UNREFERENCED_PARAMETER(hMachine); try { // // validate input parameters // if (!ARGUMENT_PRESENT(pulLength)) { Status = CR_INVALID_POINTER; goto Clean0; } if ((!ARGUMENT_PRESENT(ClassGuid)) || (!ARGUMENT_PRESENT(pszKeyName))) { 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 (pSetupStringFromGuid(ClassGuid, pszKeyName,MAX_GUID_STRING_LEN) == NO_ERROR) { *pulLength = MAX_GUID_STRING_LEN; } else { Status = CR_INVALID_DATA; } Clean0: NOTHING; } 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 (!ARGUMENT_PRESENT(ClassGuid)) { 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 (pSetupStringFromGuid(ClassGuid, szStringGuid,MAX_GUID_STRING_LEN) != NO_ERROR) { 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 (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_DeleteClassKey caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Delete_Class_Key_Ex CMAPI CONFIGRET WINAPI CM_Get_Device_Interface_Alias_ExW( IN LPCWSTR pszDeviceInterface, IN LPGUID AliasInterfaceGuid, OUT LPWSTR pszAliasDeviceInterface, IN OUT PULONG pulLength, IN ULONG ulFlags, IN HMACHINE hMachine ) { CONFIGRET Status = CR_SUCCESS; handle_t hBinding = NULL; ULONG ulTransferLen = 0; try { // // validate input parameters // if ((!ARGUMENT_PRESENT(pszDeviceInterface)) || (!ARGUMENT_PRESENT(AliasInterfaceGuid)) || (!ARGUMENT_PRESENT(pszAliasDeviceInterface)) || (!ARGUMENT_PRESENT(pulLength))) { Status = CR_INVALID_POINTER; goto Clean0; } if (INVALID_FLAGS(ulFlags, 0)) { Status = CR_INVALID_FLAG; goto Clean0; } // // initialize output parameters // *pszAliasDeviceInterface = '\0'; ulTransferLen = *pulLength; // // setup rpc binding handle // if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) { Status = CR_FAILURE; goto Clean0; } RpcTryExcept { // // call rpc service entry point // Status = PNP_GetInterfaceDeviceAlias( hBinding, pszDeviceInterface, AliasInterfaceGuid, pszAliasDeviceInterface, pulLength, &ulTransferLen, ulFlags); } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_GetInterfaceDeviceAlias caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Get_Device_Interface_Alias_ExW CMAPI CONFIGRET WINAPI CM_Get_Device_Interface_List_ExW( IN LPGUID InterfaceClassGuid, IN DEVINSTID_W pDeviceID OPTIONAL, OUT PWCHAR Buffer, IN ULONG BufferLen, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine returns a list of interface devices of the specified interface class. You can optionally filter the list of returned interface devices based on only those created by a particular devinst. Typically the CM_Get_Interface_Device_List routine is called first to determine how big a buffer must be allocated to hold the interface device list. Parameters: InterfaceClassGuid This GUID specifies which interface devices to return (only those interface devices that belong to this interface class). pDeviceID Optional devinst to filter the list of returned interface devices (if non-zero, only the interfaces devices associated with this devinst will be returned). Buffer Supplies the buffer that will contain the returned multi_sz list of interface devices. BufferLen Specifies how big the Buffer parameter is in characters. ulFlags Must be one of the following values: CM_GET_DEVICE_INTERFACE_LIST_PRESENT - only currently 'live' devices CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES 0x00000001 - all registered devices, live or not 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; handle_t hBinding = NULL; try { // // validate input parameters // if ((!ARGUMENT_PRESENT(Buffer)) || (BufferLen == 0)) { Status = CR_INVALID_POINTER; goto Clean0; } if (INVALID_FLAGS(ulFlags, CM_GET_DEVICE_INTERFACE_LIST_BITS)) { Status = CR_INVALID_FLAG; goto Clean0; } // // initialize output parameters // *Buffer = '\0'; // // setup rpc binding handle // if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) { Status = CR_FAILURE; goto Clean0; } RpcTryExcept { // // call rpc service entry point // Status = PNP_GetInterfaceDeviceList( hBinding, // RPC Binding Handle InterfaceClassGuid, // Device interface GUID pDeviceID, // filter string, optional Buffer, // will contain device list &BufferLen, // in/out size of Buffer ulFlags); // filter flag } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_GetInterfaceDeviceList caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Get_Device_Interface_List_ExW CMAPI CONFIGRET WINAPI CM_Get_Device_Interface_List_Size_ExW( IN PULONG pulLen, IN LPGUID InterfaceClassGuid, IN DEVINSTID_W pDeviceID OPTIONAL, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine returns the size (in characters) of buffer required to hold a multi_sz list of interface devices of the specified interface class. You can optionally filter the list of returned interface devices based on only those created by a particular devinst. This routine is typically called before a call to the CM_Get_Interface_Device_List routine. Parameters: pulLen On a successful return from this routine, this parameter will contain the size (in characters) required to hold the multi_sz list of returned interface devices. InterfaceClassGuid This GUID specifies which interface devices to include (only those interface devices that belong to this interface class). pDeviceID Optional devinst to filter the list of returned interface devices (if non-zero, only the interfaces devices associated with this devinst will be returned). ulFlags Must be one of the following values: CM_GET_DEVICE_INTERFACE_LIST_PRESENT - only currently 'live' devices CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES - all registered devices, live or not 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; handle_t hBinding = NULL; try { // // validate input parameters // if (!ARGUMENT_PRESENT(pulLen)) { Status = CR_INVALID_POINTER; goto Clean0; } if (INVALID_FLAGS(ulFlags, CM_GET_DEVICE_INTERFACE_LIST_BITS)) { Status = CR_INVALID_FLAG; goto Clean0; } // // initialize output parameters // *pulLen = 0; // // setup rpc binding handle // if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) { Status = CR_FAILURE; goto Clean0; } RpcTryExcept { // // call rpc service entry point // Status = PNP_GetInterfaceDeviceListSize( hBinding, // RPC Binding Handle pulLen, // Size of buffer required (in chars) InterfaceClassGuid, // Device interface GUID pDeviceID, // filter string, optional ulFlags); // filter flag } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_GetInterfaceDeviceListSize caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Get_Device_Interface_List_Size_Ex CMAPI CONFIGRET WINAPI CM_Register_Device_Interface_ExW( IN DEVINST dnDevInst, IN LPGUID InterfaceClassGuid, IN LPCWSTR pszReference OPTIONAL, OUT LPWSTR pszDeviceInterface, IN OUT PULONG pulLength, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: Parameters: 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 pszDeviceID [MAX_DEVICE_ID_LEN]; ULONG ulTransferLen,ulLen = MAX_DEVICE_ID_LEN; PVOID hStringTable = NULL; handle_t hBinding = NULL; BOOL Success; try { // // validate input parameters // if ((!ARGUMENT_PRESENT(pulLength)) || (!ARGUMENT_PRESENT(pszDeviceInterface)) || (!ARGUMENT_PRESENT(InterfaceClassGuid))) { Status = CR_INVALID_POINTER; goto Clean0; } if (dnDevInst == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } if (INVALID_FLAGS(ulFlags, 0)) { Status = CR_INVALID_FLAG; goto Clean0; } // // setup rpc binding handle and string table handle // if (!PnPGetGlobalHandles(hMachine, &hStringTable, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // retreive device instance string that corresponds to dnParent // (note that this is not optional, even a first level device instance // has a parent (the root device instance) // Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,pszDeviceID,&ulLen); if (Success == FALSE || INVALID_DEVINST(pszDeviceID)) { Status = CR_INVALID_DEVNODE; goto Clean0; } // // ulTransferLen is just used to control how many bytes in the // pszInterfaceDevice buffer must be marshalled. We need two // length params, since pulLength may contained the required bytes // (if the passed in buffer was too small) which may differ from // how many types are actually available to marshall (in the buffer // too small case, we'll marshall zero so ulTransferLen will be zero // but pulLength will describe how many bytes are required to hold // the Interface Device string. // ulTransferLen = *pulLength; RpcTryExcept { // // call rpc service entry point // Status = PNP_RegisterDeviceClassAssociation( hBinding, // RPC Binding Handle pszDeviceID, // device instance InterfaceClassGuid, // Device interface GUID pszReference, // reference string, optional pszDeviceInterface, // returns interface device name pulLength, // pszInterfaceDevice buffer required in chars &ulTransferLen, // how many chars to marshall back ulFlags); // filter flag } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_RegisterDeviceClassAssociation caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Register_Device_Interface CMAPI CONFIGRET WINAPI CM_Unregister_Device_Interface_ExW( IN LPCWSTR pszDeviceInterface, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: Parameters: 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; handle_t hBinding = NULL; try { // // validate input parameters // if (!ARGUMENT_PRESENT(pszDeviceInterface)) { Status = CR_INVALID_POINTER; goto Clean0; } if (INVALID_FLAGS(ulFlags, 0)) { Status = CR_INVALID_FLAG; goto Clean0; } // // setup rpc binding handle // if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) { Status = CR_FAILURE; goto Clean0; } RpcTryExcept { // // call rpc service entry point // Status = PNP_UnregisterDeviceClassAssociation( hBinding, // RPC Binding Handle pszDeviceInterface, // interface device ulFlags); // unused } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_UnregisterDeviceClassAssociation caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Unregister_Device_Interface_ExW CMAPI CONFIGRET WINAPI CM_Get_DevNode_Custom_Property_ExW( IN DEVINST dnDevInst, IN PCWSTR pszCustomPropertyName, 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 property, either from the devnode's device (aka, hardware) key, or from the most-specific per-hardware-id storage key for that devnode. Parameters: dnDevInst Supplies the handle of the device instance for which a custom property is to be retrieved. pszCustomPropertyName Supplies a string identifying the name of the property (registry value entry name) to be retrieved. 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 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. 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 indicates the cause of failure, and is typically one of the following: CR_INVALID_DEVNODE, CR_INVALID_FLAG, CR_INVALID_POINTER, CR_REGISTRY_ERROR, CR_BUFFER_SMALL, CR_NO_SUCH_VALUE, or CR_FAILURE. --*/ { CONFIGRET Status = CR_SUCCESS; WCHAR pDeviceID[MAX_DEVICE_ID_LEN]; ULONG ulSizeID, ulTempDataType, ulTransferLen; BYTE NullBuffer = 0; handle_t hBinding; PVOID hStringTable; BOOL Success; try { // // validate parameters // if(!dnDevInst) { Status = CR_INVALID_DEVINST; goto Clean0; } if(!pszCustomPropertyName) { Status = CR_INVALID_POINTER; goto Clean0; } if(!pulLength) { Status = CR_INVALID_POINTER; goto Clean0; } if(!Buffer && *pulLength) { Status = CR_INVALID_POINTER; goto Clean0; } if(INVALID_FLAGS(ulFlags, CM_CUSTOMDEVPROP_BITS)) { Status = CR_INVALID_FLAG; 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 // ulSizeID = SIZECHARS(pDeviceID); Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst, pDeviceID, &ulSizeID ); if(!Success || INVALID_DEVINST(pDeviceID)) { Status = CR_INVALID_DEVINST; goto Clean0; } if(!Buffer) { Buffer = &NullBuffer; } RpcTryExcept { // // call rpc service entry point // Status = PNP_GetCustomDevProp( hBinding, // rpc binding handle pDeviceID, // string representation of device instance pszCustomPropertyName, // name of the property &ulTempDataType, // receives registry data type Buffer, // receives registry data &ulTransferLen, // input/output buffer size pulLength, // bytes copied (or bytes required) ulFlags); // flags (e.g., merge-multi-sz?) } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_GetCustomDevProp caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept if(pulRegDataType) { // // 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: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Get_DevNode_Custom_Property_ExW //------------------------------------------------------------------- // 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); } CMAPI CONFIGRET WINAPI CM_Get_Device_Interface_AliasA( IN LPCSTR pszDeviceInterface, IN LPGUID AliasInterfaceGuid, OUT LPSTR pszAliasDeviceInterface, IN OUT PULONG pulLength, IN ULONG ulFlags ) { return CM_Get_Device_Interface_Alias_ExA(pszDeviceInterface, AliasInterfaceGuid, pszAliasDeviceInterface, pulLength, ulFlags, NULL); } CMAPI CONFIGRET WINAPI CM_Get_Device_Interface_AliasW( IN LPCWSTR pszDeviceInterface, IN LPGUID AliasInterfaceGuid, OUT LPWSTR pszAliasDeviceInterface, IN OUT PULONG pulLength, IN ULONG ulFlags ) { return CM_Get_Device_Interface_Alias_ExW(pszDeviceInterface, AliasInterfaceGuid, pszAliasDeviceInterface, pulLength, ulFlags, NULL); } CMAPI CONFIGRET WINAPI CM_Get_Device_Interface_ListA( IN LPGUID InterfaceClassGuid, IN DEVINSTID_A pDeviceID OPTIONAL, OUT PCHAR Buffer, IN ULONG BufferLen, IN ULONG ulFlags ) { return CM_Get_Device_Interface_List_ExA(InterfaceClassGuid, pDeviceID, Buffer, BufferLen, ulFlags, NULL); } CMAPI CONFIGRET WINAPI CM_Get_Device_Interface_ListW( IN LPGUID InterfaceClassGuid, IN DEVINSTID_W pDeviceID OPTIONAL, OUT PWCHAR Buffer, IN ULONG BufferLen, IN ULONG ulFlags ) { return CM_Get_Device_Interface_List_ExW(InterfaceClassGuid, pDeviceID, Buffer, BufferLen, ulFlags, NULL); } CMAPI CONFIGRET WINAPI CM_Get_Device_Interface_List_SizeA( IN PULONG pulLen, IN LPGUID InterfaceClassGuid, IN DEVINSTID_A pDeviceID OPTIONAL, IN ULONG ulFlags ) { return CM_Get_Device_Interface_List_Size_ExA(pulLen, InterfaceClassGuid, pDeviceID, ulFlags, NULL); } CMAPI CONFIGRET WINAPI CM_Get_Device_Interface_List_SizeW( IN PULONG pulLen, IN LPGUID InterfaceClassGuid, IN DEVINSTID_W pDeviceID OPTIONAL, IN ULONG ulFlags ) { return CM_Get_Device_Interface_List_Size_ExW(pulLen, InterfaceClassGuid, pDeviceID, ulFlags, NULL); } CMAPI CONFIGRET WINAPI CM_Register_Device_InterfaceA( IN DEVINST dnDevInst, IN LPGUID InterfaceClassGuid, IN LPCSTR pszReference OPTIONAL, OUT LPSTR pszDeviceInterface, IN OUT PULONG pulLength, IN ULONG ulFlags ) { return CM_Register_Device_Interface_ExA(dnDevInst, InterfaceClassGuid, pszReference, pszDeviceInterface, pulLength, ulFlags, NULL); } CMAPI CONFIGRET WINAPI CM_Register_Device_InterfaceW( IN DEVINST dnDevInst, IN LPGUID InterfaceClassGuid, IN LPCWSTR pszReference OPTIONAL, OUT LPWSTR pszDeviceInterface, IN OUT PULONG pulLength, IN ULONG ulFlags ) { return CM_Register_Device_Interface_ExW(dnDevInst, InterfaceClassGuid, pszReference, pszDeviceInterface, pulLength, ulFlags, NULL); } CMAPI CONFIGRET WINAPI CM_Unregister_Device_InterfaceA( IN LPCSTR pszDeviceInterface, IN ULONG ulFlags ) { return CM_Unregister_Device_Interface_ExA(pszDeviceInterface, ulFlags, NULL); } CMAPI CONFIGRET WINAPI CM_Unregister_Device_InterfaceW( IN LPCWSTR pszDeviceInterface, IN ULONG ulFlags ) { return CM_Unregister_Device_Interface_ExW(pszDeviceInterface, ulFlags, NULL); } CMAPI CONFIGRET WINAPI CM_Get_DevNode_Custom_PropertyW( IN DEVINST dnDevInst, IN PCWSTR pszCustomPropertyName, OUT PULONG pulRegDataType OPTIONAL, OUT PVOID Buffer OPTIONAL, IN OUT PULONG pulLength, IN ULONG ulFlags ) { return CM_Get_DevNode_Custom_Property_ExW(dnDevInst, pszCustomPropertyName, pulRegDataType, Buffer, pulLength, ulFlags, NULL ); } CMAPI CONFIGRET WINAPI CM_Get_DevNode_Custom_PropertyA( IN DEVINST dnDevInst, IN PCSTR pszCustomPropertyName, OUT PULONG pulRegDataType OPTIONAL, OUT PVOID Buffer OPTIONAL, IN OUT PULONG pulLength, IN ULONG ulFlags ) { return CM_Get_DevNode_Custom_Property_ExA(dnDevInst, pszCustomPropertyName, pulRegDataType, Buffer, pulLength, 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, UniLen; PWSTR pUniBuffer; // // validate essential parameters only // if (!ARGUMENT_PRESENT(pulLength)) { return CR_INVALID_POINTER; } // // examine datatype to see if need to convert return data // ulDataType = GetPropertyDataType(ulProperty); // // for all string type registry properties, we pass a Unicode buffer and // convert back to caller's ANSI buffer on return. since the Unicode -> // ANSI conversion may involve DBCS chars, we can't make any assumptions // about the size of the required ANSI buffer relative to the size of the // require Unicode buffer, so we must always get the Unicode string buffer // and convert it whether a buffer was actually supplied by the caller or // not. // if (ulDataType == REG_SZ || ulDataType == REG_MULTI_SZ || ulDataType == REG_EXPAND_SZ) { // // first, call the Wide version with a zero-length buffer to retrieve // the size required for the Unicode property. // UniLen = 0; Status = CM_Get_DevNode_Registry_Property_ExW(dnDevInst, ulProperty, pulRegDataType, NULL, &UniLen, ulFlags, hMachine); if (Status != CR_BUFFER_SMALL) { return Status; } // // allocate the required buffer. // pUniBuffer = pSetupMalloc(UniLen); if (pUniBuffer == NULL) { return CR_OUT_OF_MEMORY; } // // call the Wide version to retrieve the Unicode property. // Status = CM_Get_DevNode_Registry_Property_ExW(dnDevInst, ulProperty, pulRegDataType, pUniBuffer, &UniLen, ulFlags, hMachine); // // We specifically allocated the buffer of the required size, so it // should always be large enough. // ASSERT(Status != CR_BUFFER_SMALL); if (Status == CR_SUCCESS) { // // do the ANSI conversion or retrieve the ANSI buffer size required. // this may be a single-sz or multi-sz string, so we pass in the // length, and let PnPUnicodeToMultiByte convert the entire buffer. // Status = PnPUnicodeToMultiByte(pUniBuffer, UniLen, Buffer, pulLength); } pSetupFree(pUniBuffer); } 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 ((!ARGUMENT_PRESENT(Buffer)) && (ulLength != 0)) { return CR_INVALID_POINTER; } if (!ARGUMENT_PRESENT(Buffer)) { // // 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 (pSetupCaptureAndConvertAnsiArg(Buffer, &pUniBuffer) == NO_ERROR) { UniSize = (lstrlen(pUniBuffer)+1) * sizeof(WCHAR); Status = CM_Set_DevNode_Registry_Property_ExW(dnDevInst, ulProperty, pUniBuffer, UniSize, ulFlags, hMachine); pSetupFree(pUniBuffer); } else { Status = CR_INVALID_DATA; } } else if (ulDataType == REG_MULTI_SZ) { // // must convert the multi_sz list to unicode first // UniBufferSize = ulLength * sizeof(WCHAR); pUniBuffer = pSetupMalloc(UniBufferSize); if (pUniBuffer == NULL) { return CR_OUT_OF_MEMORY; } for (pAnsiString = (PSTR)Buffer, pUniNext = pUniBuffer; *pAnsiString; pAnsiString += lstrlenA(pAnsiString) + 1) { if (pSetupCaptureAndConvertAnsiArg(pAnsiString, &pUniString) == NO_ERROR) { UniSize += (lstrlen(pUniString)+1) * sizeof(WCHAR); if (UniSize >= UniBufferSize) { pSetupFree(pUniString); pSetupFree(pUniBuffer); return CR_INVALID_DATA; } lstrcpy(pUniNext, pUniString); pUniNext += lstrlen(pUniNext) + 1; pSetupFree(pUniString); } else { pSetupFree(pUniBuffer); return CR_INVALID_DATA; } } *(pUniNext++) = L'\0'; // add second null term Status = CM_Set_DevNode_Registry_Property_ExW(dnDevInst, ulProperty, pUniBuffer, UniBufferSize, ulFlags, hMachine); pSetupFree(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_Get_Class_Registry_PropertyA( IN LPGUID pClassGuid, 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, UniLen; PWSTR pUniBuffer; // // validate essential parameters only // if (!ARGUMENT_PRESENT(pulLength)) { return CR_INVALID_POINTER; } // // examine datatype to see if need to convert return data // ulDataType = GetPropertyDataType(ulProperty); // // for all string type registry properties, we pass a Unicode buffer and // convert back to caller's ANSI buffer on return. since the Unicode -> // ANSI conversion may involve DBCS chars, we can't make any assumptions // about the size of the required ANSI buffer relative to the size of the // require Unicode buffer, so we must always get the Unicode string buffer // and convert it whether a buffer was actually supplied by the caller or // not. // if (ulDataType == REG_SZ || ulDataType == REG_MULTI_SZ || ulDataType == REG_EXPAND_SZ) { // // first, call the Wide version with a zero-length buffer to retrieve // the size required for the Unicode property. // UniLen = 0; Status = CM_Get_Class_Registry_PropertyW(pClassGuid, ulProperty, pulRegDataType, NULL, &UniLen, ulFlags, hMachine); if (Status != CR_BUFFER_SMALL) { return Status; } // // allocate the required buffer. // pUniBuffer = pSetupMalloc(UniLen); if (pUniBuffer == NULL) { return CR_OUT_OF_MEMORY; } // // call the Wide version to retrieve the Unicode property. // Status = CM_Get_Class_Registry_PropertyW(pClassGuid, ulProperty, pulRegDataType, pUniBuffer, &UniLen, ulFlags, hMachine); // // We specifically allocated the buffer of the required size, so it // should always be large enough. // ASSERT(Status != CR_BUFFER_SMALL); if (Status == CR_SUCCESS) { // // do the ANSI conversion or retrieve the ANSI buffer size required. // this may be a single-sz or multi-sz string, so we pass in the // length, and let PnPUnicodeToMultiByte convert the entire buffer. // Status = PnPUnicodeToMultiByte(pUniBuffer, UniLen, Buffer, pulLength); } pSetupFree(pUniBuffer); } else { // // for the non-string registry data types, just pass call // on through to the Wide version // Status = CM_Get_Class_Registry_PropertyW(pClassGuid, ulProperty, pulRegDataType, Buffer, pulLength, ulFlags, hMachine); } return Status; } // CM_Get_Class_Registry_PropertyA CONFIGRET CM_Set_Class_Registry_PropertyA( IN LPGUID pClassGuid, 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 ((!ARGUMENT_PRESENT(Buffer)) && (ulLength != 0)) { return CR_INVALID_POINTER; } if (!ARGUMENT_PRESENT(Buffer)) { // // No need to convert the parameter // return CM_Set_Class_Registry_PropertyW(pClassGuid, 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 (pSetupCaptureAndConvertAnsiArg(Buffer, &pUniBuffer) == NO_ERROR) { UniSize = (lstrlen(pUniBuffer)+1) * sizeof(WCHAR); Status = CM_Set_Class_Registry_PropertyW(pClassGuid, ulProperty, pUniBuffer, UniSize, ulFlags, hMachine); pSetupFree(pUniBuffer); } else { Status = CR_INVALID_DATA; } } else if (ulDataType == REG_MULTI_SZ) { // // must convert the multi_sz list to unicode first // UniBufferSize = ulLength * sizeof(WCHAR); pUniBuffer = pSetupMalloc(UniBufferSize); if (pUniBuffer == NULL) { return CR_OUT_OF_MEMORY; } for (pAnsiString = (PSTR)Buffer, pUniNext = pUniBuffer; *pAnsiString; pAnsiString += lstrlenA(pAnsiString) + 1) { if (pSetupCaptureAndConvertAnsiArg(pAnsiString, &pUniString) == NO_ERROR) { UniSize += (lstrlen(pUniString)+1) * sizeof(WCHAR); if (UniSize >= UniBufferSize) { pSetupFree(pUniString); pSetupFree(pUniBuffer); return CR_INVALID_DATA; } lstrcpy(pUniNext, pUniString); pUniNext += lstrlen(pUniNext) + 1; pSetupFree(pUniString); } else { pSetupFree(pUniBuffer); return CR_INVALID_DATA; } } *(pUniNext++) = L'\0'; // add second null term Status = CM_Set_Class_Registry_PropertyW(pClassGuid, ulProperty, pUniBuffer, UniBufferSize, ulFlags, hMachine); pSetupFree(pUniBuffer); } else { Status = CM_Set_Class_Registry_PropertyW(pClassGuid, ulProperty, Buffer, ulLength, ulFlags, hMachine); } return Status; } // CM_Set_Class_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; PWSTR pUniClassName = NULL; if (ARGUMENT_PRESENT(pszClassName)) { if (pSetupCaptureAndConvertAnsiArg(pszClassName, &pUniClassName) != NO_ERROR) { return CR_INVALID_DATA; } } Status = CM_Open_Class_Key_ExW(ClassGuid, pUniClassName, samDesired, Disposition, phkClass, ulFlags, hMachine); if (pUniClassName) { pSetupFree(pUniClassName); } 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 UniLen = MAX_CLASS_NAME_LEN; // // validate essential parameters only // if ((!ARGUMENT_PRESENT(Buffer)) || (!ARGUMENT_PRESENT(pulLength))) { return CR_INVALID_POINTER; } // // call the wide version, passing a unicode buffer as a parameter // Status = CM_Get_Class_Name_ExW(ClassGuid, UniBuffer, &UniLen, ulFlags, hMachine); // // We should never return a class name longer than MAX_CLASS_NAME_LEN. // ASSERT(Status != CR_BUFFER_SMALL); // // convert the unicode buffer to an ansi string and copy to the // caller's buffer // if (Status == CR_SUCCESS) { Status = PnPUnicodeToMultiByte(UniBuffer, (lstrlenW(UniBuffer)+1)*sizeof(WCHAR), Buffer, pulLength); } 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 UniLen = MAX_GUID_STRING_LEN; // // validate essential parameters only // if ((!ARGUMENT_PRESENT(pszKeyName)) || (!ARGUMENT_PRESENT(pulLength))) { return CR_INVALID_POINTER; } // // call the wide version, passing a unicode buffer as a parameter // Status = CM_Get_Class_Key_Name_ExW(ClassGuid, UniBuffer, &UniLen, ulFlags, hMachine); // // We should never return a class key name longer than MAX_GUID_STRING_LEN. // ASSERT(Status != CR_BUFFER_SMALL); // // convert the unicode buffer to an ansi string and copy to the // caller's buffer // if (Status == CR_SUCCESS) { Status = PnPUnicodeToMultiByte(UniBuffer, (lstrlenW(UniBuffer)+1)*sizeof(WCHAR), pszKeyName, pulLength); } return Status; } // CM_Get_Class_Key_Name_ExA CMAPI CONFIGRET WINAPI CM_Get_Device_Interface_Alias_ExA( IN LPCSTR pszDeviceInterface, IN LPGUID AliasInterfaceGuid, OUT LPSTR pszAliasDeviceInterface, IN OUT PULONG pulLength, IN ULONG ulFlags, IN HMACHINE hMachine ) { CONFIGRET Status = CR_SUCCESS; PWSTR pUniDeviceInterface, pUniAliasDeviceInterface; ULONG UniLen; // // validate essential parameters only // if ((!ARGUMENT_PRESENT(pszDeviceInterface)) || (!ARGUMENT_PRESENT(pszAliasDeviceInterface)) || (!ARGUMENT_PRESENT(pulLength))) { return CR_INVALID_POINTER; } // // convert buffer string data to unicode to pass to wide version // if (pSetupCaptureAndConvertAnsiArg(pszDeviceInterface, &pUniDeviceInterface) != NO_ERROR) { return CR_INVALID_DATA; } // // first, call the Wide version with a zero-length buffer to retrieve // the size required for the Unicode property. // UniLen = 0; Status = CM_Get_Device_Interface_Alias_ExW(pUniDeviceInterface, AliasInterfaceGuid, NULL, &UniLen, ulFlags, hMachine); if (Status != CR_BUFFER_SMALL) { return Status; goto Clean0; } // // allocate the required buffer. // pUniAliasDeviceInterface = pSetupMalloc(UniLen); if (pUniAliasDeviceInterface == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } // // call the Wide version to retrieve the Unicode property. // Status = CM_Get_Device_Interface_Alias_ExW(pUniDeviceInterface, AliasInterfaceGuid, pUniAliasDeviceInterface, &UniLen, ulFlags, hMachine); // // We specifically allocated the buffer of the required size, so it should // always be large enough. // ASSERT(Status != CR_BUFFER_SMALL); if (Status == CR_SUCCESS) { // // do the ANSI conversion or retrieve the ANSI buffer size required. // Status = PnPUnicodeToMultiByte(pUniAliasDeviceInterface, (lstrlenW(pUniAliasDeviceInterface)+1)*sizeof(WCHAR), pszAliasDeviceInterface, pulLength); } pSetupFree(pUniAliasDeviceInterface); Clean0: pSetupFree(pUniDeviceInterface); return Status; } // CM_Get_Device_Interface_Alias_ExA CMAPI CONFIGRET WINAPI CM_Get_Device_Interface_List_ExA( IN LPGUID InterfaceClassGuid, IN DEVINSTID_A pDeviceID OPTIONAL, OUT PCHAR Buffer, IN ULONG BufferLen, IN ULONG ulFlags, IN HMACHINE hMachine ) { CONFIGRET Status = CR_SUCCESS; PWSTR pUniBuffer, pUniDeviceID = NULL; ULONG ulAnsiBufferLen; // // validate essential parameters only // if ((!ARGUMENT_PRESENT(Buffer)) || (BufferLen == 0)) { return CR_INVALID_POINTER; } if (ARGUMENT_PRESENT(pDeviceID)) { // // if a filter string was passed in, convert to UNICODE before // passing on to the wide version // if (pSetupCaptureAndConvertAnsiArg(pDeviceID, &pUniDeviceID) != NO_ERROR) { return CR_INVALID_DEVICE_ID; } ASSERT(pUniDeviceID != NULL); } else { ASSERT(pUniDeviceID == NULL); } // // prepare a larger buffer to hold the unicode formatted // multi_sz data returned by CM_Get_Device_Interface_List_ExW. // pUniBuffer = pSetupMalloc(BufferLen * sizeof(WCHAR)); if (pUniBuffer == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } *pUniBuffer = L'\0'; // // call the wide version // Status = CM_Get_Device_Interface_List_ExW(InterfaceClassGuid, pUniDeviceID, pUniBuffer, BufferLen, // size in chars ulFlags, hMachine); // // convert the unicode buffer to an ansi string and copy to the // caller's buffer // if (Status == CR_SUCCESS) { ulAnsiBufferLen = BufferLen; Status = PnPUnicodeToMultiByte(pUniBuffer, BufferLen*sizeof(WCHAR), Buffer, &ulAnsiBufferLen); } pSetupFree(pUniBuffer); Clean0: if (pUniDeviceID) { pSetupFree(pUniDeviceID); } return Status; } // CM_Get_Device_Interface_List_ExA CMAPI CONFIGRET WINAPI CM_Get_Device_Interface_List_Size_ExA( IN PULONG pulLen, IN LPGUID InterfaceClassGuid, IN DEVINSTID_A pDeviceID OPTIONAL, IN ULONG ulFlags, IN HMACHINE hMachine ) { CONFIGRET Status = CR_SUCCESS, tmpStatus; PWSTR pUniDeviceID = NULL, pUniDeviceInterfaceList; ULONG UniLen; // // validate essential parameters only // if (!ARGUMENT_PRESENT(pulLen)) { return CR_INVALID_POINTER; } if (ARGUMENT_PRESENT(pDeviceID)) { // // if a device ID string was passed in, convert to UNICODE before // passing on to the wide version // if (pSetupCaptureAndConvertAnsiArg(pDeviceID, &pUniDeviceID) != NO_ERROR) { return CR_INVALID_DEVICE_ID; } ASSERT(pUniDeviceID != NULL); } else { ASSERT(pUniDeviceID == NULL); } // // first, call the Wide version to retrieve the size required for the // Unicode device interface list. // UniLen = 0; Status = CM_Get_Device_Interface_List_Size_ExW(&UniLen, InterfaceClassGuid, pUniDeviceID, ulFlags, hMachine); if (Status != CR_SUCCESS) { goto Clean0; } // // allocate the required buffer. // pUniDeviceInterfaceList = pSetupMalloc(UniLen*sizeof(WCHAR)); if (pUniDeviceInterfaceList == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } // // call the Wide version to retrieve the Unicode device interface list. // Status = CM_Get_Device_Interface_List_ExW(InterfaceClassGuid, pUniDeviceID, pUniDeviceInterfaceList, UniLen, ulFlags, hMachine); // // We specifically allocated the buffer of the required size, so it should // always be large enough. // ASSERT(Status != CR_BUFFER_SMALL); if (Status == CR_SUCCESS) { // // retrieve the size, in bytes, of the ANSI buffer size required to // convert this list. since this is a multi-sz string, we pass in the // length and let PnPUnicodeToMultiByte convert the entire buffer. // tmpStatus = PnPUnicodeToMultiByte(pUniDeviceInterfaceList, UniLen*sizeof(WCHAR), NULL, pulLen); ASSERT(tmpStatus == CR_BUFFER_SMALL); } pSetupFree(pUniDeviceInterfaceList); Clean0: if (pUniDeviceID) { pSetupFree(pUniDeviceID); } return Status; } // CM_Get_Device_Interface_List_Size_ExA CMAPI CONFIGRET WINAPI CM_Register_Device_Interface_ExA( IN DEVINST dnDevInst, IN LPGUID InterfaceClassGuid, IN LPCSTR pszReference OPTIONAL, OUT LPSTR pszDeviceInterface, IN OUT PULONG pulLength, IN ULONG ulFlags, IN HMACHINE hMachine ) { CONFIGRET Status = CR_SUCCESS; PWSTR pUniReference = NULL, pUniDeviceInterface = NULL; ULONG UniLen; try { // // validate essential parameters only // if ((!ARGUMENT_PRESENT(pulLength)) || (!ARGUMENT_PRESENT(pszDeviceInterface))) { return CR_INVALID_POINTER; } // // if a device reference string was passed in, convert to Unicode before // passing on to the wide version // if (ARGUMENT_PRESENT(pszReference)) { if (pSetupCaptureAndConvertAnsiArg(pszReference, &pUniReference) != NO_ERROR) { return CR_INVALID_DATA; } } // // pass a Unicode buffer instead and convert back to caller's ANSI buffer on // return // UniLen = *pulLength; pUniDeviceInterface = pSetupMalloc(UniLen*sizeof(WCHAR)); if (pUniDeviceInterface == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } Status = CM_Register_Device_Interface_ExW(dnDevInst, InterfaceClassGuid, pUniReference, pUniDeviceInterface, &UniLen, ulFlags, hMachine); if (Status == CR_SUCCESS) { // // if the call succeeded, convert the Unicode string to ANSI // Status = PnPUnicodeToMultiByte(pUniDeviceInterface, (lstrlenW(pUniDeviceInterface)+1)*sizeof(WCHAR), pszDeviceInterface, pulLength); } else if (Status == CR_BUFFER_SMALL) { // // returned size is in chars // *pulLength = UniLen; } Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } if (pUniDeviceInterface) { pSetupFree(pUniDeviceInterface); } if (pUniReference) { pSetupFree(pUniReference); } return Status; } // CM_Register_Device_Interface_ExA CMAPI CONFIGRET WINAPI CM_Unregister_Device_Interface_ExA( IN LPCSTR pszDeviceInterface, IN ULONG ulFlags, IN HMACHINE hMachine ) { CONFIGRET Status = CR_SUCCESS; PWSTR pUniDeviceInterface = NULL; try { // // validate essential parameters only // if (!ARGUMENT_PRESENT(pszDeviceInterface)) { return CR_INVALID_POINTER; } // // convert buffer string data to unicode and pass to wide version // if (pSetupCaptureAndConvertAnsiArg(pszDeviceInterface, &pUniDeviceInterface) == NO_ERROR) { Status = CM_Unregister_Device_Interface_ExW(pUniDeviceInterface, ulFlags, hMachine); } else { Status = CR_INVALID_DATA; } } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } if (pUniDeviceInterface) { pSetupFree(pUniDeviceInterface); } return Status; } // CM_Unregister_Device_Interface_ExA CMAPI CONFIGRET WINAPI CM_Get_DevNode_Custom_Property_ExA( IN DEVINST dnDevInst, IN PCSTR pszCustomPropertyName, OUT PULONG pulRegDataType OPTIONAL, OUT PVOID Buffer OPTIONAL, IN OUT PULONG pulLength, IN ULONG ulFlags, IN HMACHINE hMachine ) { DWORD Win32Status; CONFIGRET Status = CR_SUCCESS; PWSTR UnicodeCustomPropName = NULL; DWORD UniLen; PBYTE pUniBuffer = NULL; PSTR pAnsiBuffer = NULL; ULONG ulDataType; ULONG ulAnsiBufferLen; PWSTR pUniString; try { // // Validate parameters not validated by upcoming call to Unicode API // (CM_Get_DevNode_Registry_Property_ExW). // if(!ARGUMENT_PRESENT(pulLength)) { Status = CR_INVALID_POINTER; goto Clean0; } if((!ARGUMENT_PRESENT(Buffer)) && (*pulLength != 0)) { Status = CR_INVALID_POINTER; goto Clean0; } if(pszCustomPropertyName) { // // Convert property name to Unicode. // Win32Status = pSetupCaptureAndConvertAnsiArg(pszCustomPropertyName, &UnicodeCustomPropName ); if(Win32Status != NO_ERROR) { // // This routine guarantees that the returned unicode string // pointer will be null upon failure, so we don't have to reset // it here--just bail. // if(Win32Status == ERROR_NOT_ENOUGH_MEMORY) { Status = CR_OUT_OF_MEMORY; } else { Status = CR_INVALID_POINTER; } goto Clean0; } } else { Status = CR_INVALID_POINTER; goto Clean0; } // // Unfortunately, we have no clue as to whether or not the requested // property is a string (thus requiring conversion from Unicode to // ANSI). Therefore, we'll retrieve the data (if any) in its entirety, // then convert to ANSI if necessary. Only then can we determine the // data size (and whether it can be returned to the caller). // // Start out with a reasonable guess as to buffer size in an attempt to // avoid calling the Unicode get-property API twice... // UniLen = 1024; do { pUniBuffer = pSetupMalloc(UniLen); if(!pUniBuffer) { Status = CR_OUT_OF_MEMORY; goto Clean0; } Status = CM_Get_DevNode_Custom_Property_ExW(dnDevInst, UnicodeCustomPropName, &ulDataType, pUniBuffer, &UniLen, ulFlags, hMachine ); if(Status != CR_SUCCESS) { pSetupFree(pUniBuffer); pUniBuffer = NULL; } } while(Status == CR_BUFFER_SMALL); if(Status != CR_SUCCESS) { goto Clean0; } // // If we get to here, we successfully retrieved the property. // if(pulRegDataType) { *pulRegDataType = ulDataType; } if(UniLen == 0) { // // We retrieved an empty buffer--no need to worry about // transferring any data into caller's buffer. // *pulLength = 0; goto Clean0; } switch(ulDataType) { case REG_MULTI_SZ : case REG_SZ : case REG_EXPAND_SZ : // // Worst case, an ANSI buffer large enough to hold the results // would be the same size as the Unicode results. // pAnsiBuffer = pSetupMalloc(UniLen); if(!pAnsiBuffer) { Status = CR_OUT_OF_MEMORY; goto Clean0; } // // do the ANSI conversion or retrieve the ANSI buffer size required. // this may be a single-sz or multi-sz string, so we pass in the // length, and let PnPUnicodeToMultiByte convert the entire buffer. // ulAnsiBufferLen = *pulLength; Status = PnPUnicodeToMultiByte((PWSTR)pUniBuffer, UniLen, pAnsiBuffer, &ulAnsiBufferLen); if(ulAnsiBufferLen > *pulLength) { ASSERT(Status == CR_BUFFER_SMALL); Status = CR_BUFFER_SMALL; } else { // // Copy ANSI string(s) into caller's buffer. // CopyMemory(Buffer, pAnsiBuffer, ulAnsiBufferLen); } *pulLength = ulAnsiBufferLen; break; default : // // buffer doesn't contain text, no conversion necessary. // if(UniLen > *pulLength) { Status = CR_BUFFER_SMALL; } else { CopyMemory(Buffer, pUniBuffer, UniLen); } *pulLength = UniLen; } Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; // // Reference the following variables so the compiler will respect // statement ordering w.r.t. their assignment. // pUniBuffer = pUniBuffer; pAnsiBuffer = pAnsiBuffer; } if(UnicodeCustomPropName) { pSetupFree(UnicodeCustomPropName); } if(pUniBuffer) { pSetupFree(pUniBuffer); } if(pAnsiBuffer) { pSetupFree(pAnsiBuffer); } return Status; } // CM_Get_DevNode_Custom_Property_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_BINARY is the default. --*/ { switch(ulProperty) { 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: case CM_DRP_LOCATION_INFORMATION: case CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME: case CM_DRP_ENUMERATOR_NAME: case CM_DRP_SECURITY_SDS: // and CM_CRP_SECURITY_SDS case CM_DRP_UI_NUMBER_DESC_FORMAT: return REG_SZ; case CM_DRP_HARDWAREID: case CM_DRP_COMPATIBLEIDS: case CM_DRP_UPPERFILTERS: case CM_DRP_LOWERFILTERS: return REG_MULTI_SZ; case CM_DRP_CONFIGFLAGS: case CM_DRP_CAPABILITIES: case CM_DRP_UI_NUMBER: case CM_DRP_LEGACYBUSTYPE: case CM_DRP_BUSNUMBER: case CM_DRP_CHARACTERISTICS: // and CM_CRP_CHARACTERISTICS case CM_DRP_EXCLUSIVE: // and CM_CRP_EXCLUSIVE case CM_DRP_DEVTYPE: // and CM_CRP_DEVTYPE case CM_DRP_ADDRESS: case CM_DRP_REMOVAL_POLICY: case CM_DRP_REMOVAL_POLICY_HW_DEFAULT: case CM_DRP_REMOVAL_POLICY_OVERRIDE: case CM_DRP_INSTALL_STATE: return REG_DWORD; case CM_DRP_BUSTYPEGUID: case CM_DRP_SECURITY: // and CM_CRP_SECURITY return REG_BINARY; case CM_DRP_DEVICE_POWER_DATA: return REG_BINARY; default: // // We should never get here! // ASSERT(0); return REG_BINARY; } } // GetPropertyDataType