/*++ 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 CM_Get_DevNode_Custom_Property 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" #pragma hdrstop #include "cfgi.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; size_t sdsLen = 0; 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; } // // There really isn't a max length defined for security // descriptor strings, but it had better be less than // STRSAFE_MAX_CCH (INT_MAX). // if (FAILED(StringCchLength( sds, STRSAFE_MAX_CCH, &sdsLen))) { Status = CR_FAILURE; leave; } ulTransferLen = (ULONG)((sdsLen + 1)*sizeof(WCHAR)); 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 // CopyMemory(Buffer, sds, ulTransferLen); *pulLength = ulTransferLen; } } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; // // Reference the following variables so the compiler will respect // statement ordering w.r.t. their assignment. // tmpBuffer = tmpBuffer; sds = sds; } 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 - When calling a PNP RPC server stub routine that marshalls // data between client and server buffers, be very careful to check // the corresponding server stub definition in the IDL file for the // [in] and/or [out] attributes of the parameters, and which // parameters are used to describe the [size_is] and/or [length_is] // attributes of a buffer. Each of the PNP RPC server stub routines // behave in differently, so make sure you know what you are doing!! // // // PNP_GetDeviceRegProp - // // Note that the [in,out] ulTransferLen parameter is used for // *both* the [size_is] *and* [length_is] attributes of the [out] // Buffer. This means that upon entry [in] ulTransferLen is used // to specify the amount of memory the stubs must allocate for the // Buffer parameter, while on exit [out], its value indicates the // amount of data the stubs should marshall back to the client (or // 0 if no data is to be marshalled). Note that no data is // marshalled by the stubs to the server since Buffer is [out] // only. // // The [in,out] pulLength parameter should also be set on entry // [in] to the size of the Buffer. On exit [out], this value // contains either the amount of data marshalled back to the // client by the server (if a transfer occurred) or the size that // is required for a successful transfer. This value should be // passed back in the callers pulLength parameter. // // // Even though we may specify 0 bytes as the value of the [size_is] // attribute for the [out] Buffer, the Buffer itself must not be // NULL. If the caller supplied a NULL Buffer, supply a local // pointer to the stubs instead. // ulTransferLen = *pulLength; if (Buffer == NULL) { Buffer = &NullBuffer; } // // No special privileges are required by the server // 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_SZ, make sure it is NULL terminated // if ((ulRegDataType == REG_SZ) && (ulLength != 0)) { HRESULT hr; size_t BufferLen = 0; // // Check the length of the string specified, up to the number of // bytes specified. // hr = StringCbLength(Buffer, ulLength, &BufferLen); if (SUCCEEDED(hr)) { // // The specified buffer was NULL terminated before ulLength // bytes. Use the specified buffer, specifying the length of // the string we just calculated. The registry APIs will store // the size of the REG_SZ value data exactly as specified // (regardless of the actual string length), so we need to make // sure this is correct. // ASSERT(BufferLen < ulLength); ulLength = (ULONG)(BufferLen + sizeof(WCHAR)); } else { // // The specified buffer was not NULL terminated before ulLength // bytes. Allocate a new buffer that can hold a NULL terminated // version of the string data supplied. // BufferLen = ulLength + sizeof(WCHAR); Buffer3 = pSetupMalloc((DWORD)BufferLen); if (Buffer3 == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } // // Copy the source data to the destination buffer, up to the // length of the destination, including terminating NULL. // // Ignore the return error because the source buffer may not be // NULL terminated, immediately beyond the number of bytes // specified as its length (ulLength), in which case truncation // will occur (at the ulLength originally specified for the // source), and an error is returned. This is ok, since we are // guaranteed that the destination buffer is NULL terminated. // StringCbCopy(Buffer3, BufferLen, Buffer); Buffer = Buffer3; ulLength = (ULONG)BufferLen; } } // // 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 != L'\0')) { while ((tmpBuffer < bufferEnd) && (*tmpBuffer != L'\0')) { tmpBuffer++; } if (tmpBuffer >= bufferEnd) { ulNewLength += sizeof(WCHAR); } else { tmpBuffer++; } } if (tmpBuffer >= bufferEnd) { ulNewLength += sizeof(WCHAR); } 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; } CopyMemory(Buffer3, Buffer, ulLength); ZeroMemory((PUCHAR)Buffer3 + ulLength, ulNewLength - ulLength); Buffer = Buffer3; } ulLength = ulNewLength; } // // NOTE - When calling a PNP RPC server stub routine that marshalls // data between client and server buffers, be very careful to check // the corresponding server stub definition in the IDL file for the // [in] and/or [out] attributes of the parameters, and which // parameters are used to describe the [size_is] and/or [length_is] // attributes of a buffer. Each of the PNP RPC server stub routines // behave in differently, so make sure you know what you are doing!! // // // PNP_SetDeviceRegProp - // // Note that the [in] ulLength parameter is used for the [size_is] // attributes of the [in] Buffer. This indicates both the amount of // memory the stubs must allocate, as well as the amount of data that // must be marshalled. Note that no data is marshalled by the stubs // to the client since Buffer is [in] only. // // // Even though we may specify 0 bytes as the value of the [size_is] // attribute for the [in] Buffer, the Buffer itself must not be // NULL. If the caller supplied a NULL Buffer, supply a local // pointer to the stubs instead. // if (Buffer == NULL) { Buffer = &NullBuffer; } // // Special privileges are no longer required by the server. // // Note that with previous versions of the PlugPlay RPC server, // SE_LOAD_DRIVER_PRIVILEGE was required for this operation. We do not // need to enable the privilege for local callers, since this version of // CFGMGR32 should match a local version of UMPNPMGR that does not // require the privilege. For remote calls, it's not always possible // for us to enable the privilege anyways, since the client may not have // the privilege on the local machine, but may as authenticated on the // server. The server typically sees all privileges that a remote // caller has as "enabled by default", so we are not required to enable // the privilege here either. // RpcTryExcept { // // call rpc service entry point // Status = PNP_SetDeviceRegProp( hBinding, // rpc binding handle pDeviceID, // string representation of devinst ulProperty, // string name for property ulRegDataType, // specifies registry data type (LPBYTE)Buffer, // specifies registry data ulLength, // specifies amount of data 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; // // Reference the following variables so the compiler will respect // statement ordering w.r.t. their assignment. // Buffer2 = Buffer2; Buffer3 = Buffer3; } 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; 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; size_t sdsLen = 0; 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; } // // There really isn't a max length defined for security // descriptor strings, but it had better be less than // STRSAFE_MAX_CCH (INT_MAX). // if (FAILED(StringCchLength( sds, STRSAFE_MAX_CCH, &sdsLen))) { Status = CR_FAILURE; leave; } ulTransferLen = (ULONG)((sdsLen + 1)*sizeof(WCHAR)); 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 // CopyMemory(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 - When calling a PNP RPC server stub routine that marshalls // data between client and server buffers, be very careful to check // the corresponding server stub definition in the IDL file for the // [in] and/or [out] attributes of the parameters, and which // parameters are used to describe the [size_is] and/or [length_is] // attributes of a buffer. Each of the PNP RPC server stub routines // behave in differently, so make sure you know what you are doing!! // // // PNP_GetClassRegProp - // // Note that the [in,out] ulTransferLen parameter is used for // *both* the [size_is] *and* [length_is] attributes of the [out] // Buffer. This means that upon entry [in] ulTransferLen is used // to specify the amount of memory the stubs must allocate for the // Buffer parameter, while on exit [out], its value indicates the // amount of data the stubs should marshall back to the client (or // 0 if no data is to be marshalled). Note that no data is // marshalled by the stubs to the server since Buffer is [out] // only. // // The [in,out] pulLength parameter should also be set on entry // [in] to the size of the Buffer. On exit [out], this value // contains either the amount of data marshalled back to the // client by the server (if a transfer occurred) or the size that // is required for a successful transfer. This value should be // passed back in the callers pulLength parameter. // // // Even though we may specify 0 bytes as the value of the [size_is] // attribute for the [out] Buffer, the Buffer itself must not be // NULL. If the caller supplied a NULL Buffer, supply a local // pointer to the stubs instead. // ulTransferLen = *pulLength; if (Buffer == NULL) { Buffer = &NullBuffer; } // // No special privileges are required by the server // 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; BYTE NullBuffer = 0x0; handle_t hBinding = NULL; 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_SZ, make sure it is NULL terminated // if ((ulRegDataType == REG_SZ) && (ulLength != 0)) { HRESULT hr; size_t BufferLen = 0; // // Check the length of the string specified, up to the number of // bytes specified. // hr = StringCbLength(Buffer, ulLength, &BufferLen); if (SUCCEEDED(hr)) { // // The specified buffer was NULL terminated before ulLength // bytes. Use the specified buffer, specifying the length of // the string we just calculated. The registry APIs will store // the size of the REG_SZ value data exactly as specified // (regardless of the actual string length), so we need to make // sure this is correct. // ASSERT(BufferLen < ulLength); ulLength = (ULONG)(BufferLen + sizeof(WCHAR)); } else { // // The specified buffer was not NULL terminated before ulLength // bytes. Allocate a new buffer that can hold a NULL terminated // version of the string data supplied. // BufferLen = ulLength + sizeof(WCHAR); Buffer3 = pSetupMalloc((DWORD)BufferLen); if (Buffer3 == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } // // Copy the source data to the destination buffer, up to the // length of the destination, including terminating NULL. // // Ignore the return error because the source buffer may not be // NULL terminated, immediately beyond the number of bytes // specified as its length (ulLength), in which case truncation // will occur (at the ulLength originally specified for the // source), and an error is returned. This is ok, since we are // guaranteed that the destination buffer is NULL terminated. // StringCbCopy(Buffer3, BufferLen, Buffer); Buffer = Buffer3; ulLength = (ULONG)BufferLen; } } // // 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 != L'\0')) { while ((tmpBuffer < bufferEnd) && (*tmpBuffer != L'\0')) { tmpBuffer++; } if (tmpBuffer >= bufferEnd) { ulNewLength += sizeof(WCHAR); } else { tmpBuffer++; } } if (tmpBuffer >= bufferEnd) { ulNewLength += sizeof(WCHAR); } 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; } CopyMemory(Buffer3, Buffer, ulLength); ZeroMemory((PUCHAR)Buffer3 + ulLength, ulNewLength - ulLength); Buffer = Buffer3; } ulLength = ulNewLength; } // // NOTE - When calling a PNP RPC server stub routine that marshalls // data between client and server buffers, be very careful to check // the corresponding server stub definition in the IDL file for the // [in] and/or [out] attributes of the parameters, and which // parameters are used to describe the [size_is] and/or [length_is] // attributes of a buffer. Each of the PNP RPC server stub routines // behave in differently, so make sure you know what you are doing!! // // // PNP_SetClassRegProp - // // Note that the [in] ulLength parameter is used for the [size_is] // attributes of the [in] Buffer. This indicates both the amount of // memory the stubs must allocate, as well as the amount of data that // must be marshalled. Note that no data is marshalled by the stubs // to the client since Buffer is [in] only. // // // Even though we may specify 0 bytes as the value of the [size_is] // attribute for the [in] Buffer, the Buffer itself must not be // NULL. If the caller supplied a NULL Buffer, supply a local // pointer to the stubs instead. // if (Buffer == NULL) { Buffer = &NullBuffer; } // // Special privileges are no longer required by the server. // // Note that with previous versions of the PlugPlay RPC server, // SE_LOAD_DRIVER_PRIVILEGE was required for this operation. We do not // need to enable the privilege for local callers, since this version of // CFGMGR32 should match a local version of UMPNPMGR that does not // require the privilege. For remote calls, it's not always possible // for us to enable the privilege anyways, since the client may not have // the privilege on the local machine, but may as authenticated on the // server. The server typically sees all privileges that a remote // caller has as "enabled by default", so we are not required to enable // the privilege here either. // RpcTryExcept { // // call rpc service entry point // Status = PNP_SetClassRegProp( hBinding, // rpc binding handle szStringGuid, // string representation of class ulProperty, // string name for property ulRegDataType, // specifies registry data type (LPBYTE)Buffer, // specifies registry data ulLength, // specifies amount of data 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; // // Reference the following variables so the compiler will respect // statement ordering w.r.t. their assignment. // Buffer2 = Buffer2; Buffer3 = Buffer3; } 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; } // // current user key can't be remoted // if ((hBinding != hLocalBindingHandle) && (ulFlags & CM_REGISTRY_USER)) { Status = CR_ACCESS_DENIED; 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) { // // current user key specified // hBranchKey = HKEY_CURRENT_USER; } else { // // all other cases go to HKLM // 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; } if (!PnPRetrieveMachineName(hMachine, pszMachineName)) { Status = CR_INVALID_MACHINENAME; goto Clean0; } // // 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. // // note that in some cases, GetDevNodeKeyPath may call // PNP_GetClassInstance or PNP_SetDeviceRegProp to set values or create // keys on the server, in which case special write access would be // required by the server, but no special privilges are required. // Status = GetDevNodeKeyPath( hBinding, pDeviceID, ulFlags, ulHardwareProfile, pszKey, MAX_CM_PATH, pszPrivateKey, MAX_CM_PATH, (Disposition == RegDisposition_OpenAlways)); // // Failed to get devnode key path components // if (Status != CR_SUCCESS) { goto Clean0; } // // Build the full key path // ASSERT(pszKey[0] != L'\0'); ASSERT(pszPrivateKey[0] != L'\0'); if (FAILED(StringCchCat( pszKey, MAX_CM_PATH, L"\\"))) { Status = CR_FAILURE; goto Clean0; } if (FAILED(StringCchCat( pszKey, MAX_CM_PATH, pszPrivateKey))) { Status = CR_FAILURE; goto Clean0; } 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 // // // Special privileges are no longer required by the server. // // Note that with previous versions of the PlugPlay RPC // server, SE_LOAD_DRIVER_PRIVILEGE was required for this // operation. We do not need to enable the privilege for // local callers, since this version of CFGMGR32 should // match a local version of UMPNPMGR that does not require // the privilege. For remote calls, it's not always // possible for us to enable the privilege anyways, since // the client may not have the privilege on the local // machine, but may as authenticated on the server. The // server typically sees all privileges that a remote caller // has as "enabled by default", so we are not required to // enable the privilege here either. // 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; hKey = hKey; hRemoteKey = hRemoteKey; } 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; PVOID hStringTable = NULL; handle_t hBinding = NULL; HKEY hKey = NULL; PWSTR pszParentKey = NULL, pszChildKey = NULL, pszRegStr = NULL; WCHAR pDeviceID[MAX_DEVICE_ID_LEN]; ULONG 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; } // // can't specify both user-specific and config-specific flags // if ((ulFlags & CM_REGISTRY_USER) && (ulFlags & CM_REGISTRY_CONFIG)) { Status = CR_INVALID_FLAG; 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, MAX_CM_PATH, pszChildKey, MAX_CM_PATH, FALSE); if (Status != CR_SUCCESS) { goto Clean0; } //------------------------------------------------------------------ // For both hardware and software USER keys, the client must be // granted access to delete (the keys are in the user's own hive). // The server side does not access any HKEY_CURRENT_USER keys. //------------------------------------------------------------------ if (ulFlags & CM_REGISTRY_USER) { // // Not config-specific, just delete the specified key on the client. // ASSERT(!(ulFlags & CM_REGISTRY_CONFIG)); 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, since that side has the code to make the key // volatile if necessary instead of deleting. Also, access to // these registry keys is granted to SYSTEM only. //------------------------------------------------------------------ else { // // If not config-specific, just set the flags to 0; it will not be // used by the server. // if (!(ulFlags & CM_REGISTRY_CONFIG)) { ulHardwareProfile = 0; } // // NOTICE-2002/03/11-jamesca: Logic for profile-specific behavior. // If the config-specific flag was specified, and a specific // hardware profile was targeted, GetDevNodeKeyPath formed the exact // config-specific parent/child key paths for the specified hardware // profile above, and the server will ignore the profile specified. // If the config-specific flag was specified, but the hardware // profile targeted was 0 (current profile) or 0xFFFFFFFF (all // profiles), the parent/child paths formed contain printf-style // replacement characters, and ulHardwareProfile is relevant to the // server to determine which of the two cases it is handling. // // // Special privileges are no longer required by the server. // // Note that with previous versions of the PlugPlay RPC server, // SE_LOAD_DRIVER_PRIVILEGE was required for this operation. We do // not need to enable the privilege for local callers, since this // version of CFGMGR32 should match a local version of UMPNPMGR that // does not require the privilege. For remote calls, it's not // always possible for us to enable the privilege anyways, since the // client may not have the privilege on the local machine, but may // as authenticated on the server. The server typically sees all // privileges that a remote caller has as "enabled by default", so // we are not required to enable the privilege here either. // 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. // hKey = hKey; 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; size_t ClassNameLen = 0; 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) && (ARGUMENT_PRESENT(pszClassName))) { Status = CR_INVALID_DATA; goto Clean0; } // // if a class name was specified, make sure it's valid. // if (ARGUMENT_PRESENT(pszClassName)) { if (FAILED(StringCchLength( pszClassName, MAX_CLASS_NAME_LEN, &ClassNameLen))) { Status = CR_INVALID_DATA; goto Clean0; } ASSERT(ClassNameLen < MAX_CLASS_NAME_LEN); if (ClassNameLen == 0) { 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 // if (!PnPGetGlobalHandles(hMachine, &hStringTable, NULL)) { Status = CR_FAILURE; goto Clean0; } // // retrieve machine name // pszMachineName = pSetupMalloc((MAX_PATH + 3)*sizeof(WCHAR)); if (pszMachineName == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } if (!PnPRetrieveMachineName(hMachine, pszMachineName)) { Status = CR_INVALID_MACHINENAME; goto Clean0; } // // 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) { // // Form the path for the "DeviceClasses" key // if (FAILED(StringCchCopy( pszRegStr, MAX_PATH, pszRegPathDeviceClass))) { Status = CR_FAILURE; goto Clean0; } } else { // // Form the path for the "Class" key // if (FAILED(StringCchCopy( pszRegStr, MAX_PATH, pszRegPathClass))) { Status = CR_FAILURE; goto Clean0; } } if (ARGUMENT_PRESENT(ClassGuid)) { // // Optional class guid was specified // WCHAR szStringGuid[MAX_GUID_STRING_LEN]; if (pSetupStringFromGuid( ClassGuid, szStringGuid, MAX_GUID_STRING_LEN) != NO_ERROR) { ASSERT(0); Status = CR_FAILURE; goto Clean0; } // // Append "\{ClassGUID}" to the existing path // if (FAILED(StringCchCat( pszRegStr, MAX_PATH, L"\\"))) { Status = CR_FAILURE; goto Clean0; } if (FAILED(StringCchCat( pszRegStr, MAX_PATH, szStringGuid))) { Status = CR_FAILURE; goto Clean0; } } // // 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 ((ARGUMENT_PRESENT(pszClassName)) && (RegStatus == ERROR_SUCCESS)) { RegSetValueEx(*phkClass, pszRegValueClass, 0, REG_SZ, (LPBYTE)pszClassName, (DWORD)((ClassNameLen+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; hRemoteKey = hRemoteKey; } 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; } // // No special privileges are required by the server // 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 PWCHAR 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; } // // No special privileges are required by the server // 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; // // 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; } // // Special privileges are no longer required by the server. // // Note that with previous versions of the PlugPlay RPC server, // SE_LOAD_DRIVER_PRIVILEGE was required for this operation. We do not // need to enable the privilege for local callers, since this version of // CFGMGR32 should match a local version of UMPNPMGR that does not // require the privilege. For remote calls, it's not always possible // for us to enable the privilege anyways, since the client may not have // the privilege on the local machine, but may as authenticated on the // server. The server typically sees all privileges that a remote // caller has as "enabled by default", so we are not required to enable // the privilege here either. // 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 = L'\0'; ulTransferLen = *pulLength; // // setup rpc binding handle // if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // No special privileges are required by the server // 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 = L'\0'; // // setup rpc binding handle // if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // No special privileges are required by the server // 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; } // // No special privileges are required by the server // 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; // // Special privileges are no longer required by the server. // // Note that with previous versions of the PlugPlay RPC server, // SE_LOAD_DRIVER_PRIVILEGE was required for this operation. We do not // need to enable the privilege for local callers, since this version of // CFGMGR32 should match a local version of UMPNPMGR that does not // require the privilege. For remote calls, it's not always possible // for us to enable the privilege anyways, since the client may not have // the privilege on the local machine, but may as authenticated on the // server. The server typically sees all privileges that a remote // caller has as "enabled by default", so we are not required to enable // the privilege here either. // 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; } // // Special privileges are no longer required by the server. // // Note that with previous versions of the PlugPlay RPC server, // SE_LOAD_DRIVER_PRIVILEGE was required for this operation. We do not // need to enable the privilege for local callers, since this version of // CFGMGR32 should match a local version of UMPNPMGR that does not // require the privilege. For remote calls, it's not always possible // for us to enable the privilege anyways, since the client may not have // the privilege on the local machine, but may as authenticated on the // server. The server typically sees all privileges that a remote // caller has as "enabled by default", so we are not required to enable // the privilege here either. // 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 = 0, ulTransferLen = 0; BYTE NullBuffer = 0; handle_t hBinding; PVOID hStringTable; BOOL Success; try { // // validate parameters // if(dnDevInst == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } if(!ARGUMENT_PRESENT(pszCustomPropertyName)) { 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, 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; } // // NOTE - When calling a PNP RPC server stub routine that marshalls // data between client and server buffers, be very careful to check // the corresponding server stub definition in the IDL file for the // [in] and/or [out] attributes of the parameters, and which // parameters are used to describe the [size_is] and/or [length_is] // attributes of a buffer. Each of the PNP RPC server stub routines // behave in differently, so make sure you know what you are doing!! // // // PNP_GetCustomDevProp - // // Note that ulTransferLen is specified as an [out] parameter // only. Since Buffer also has the [out] attribute only, // ulTransferLen is NOT used or needed on entry to indicate how // much data to marshall to the server. The value of // ulTransferLen is set by the server, and is only used upon // return to indicate how much data to marshall back to the client // Buffer. // // // Even though we may specify 0 bytes as the value of the [size_is] // attribute for the [out] Buffer, the Buffer itself must not be // NULL. If the caller supplied a NULL Buffer, supply a local // pointer to the stubs instead. // if(Buffer == NULL) { Buffer = &NullBuffer; } // // No special privileges are required by the server // 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; } if ((ulProperty < CM_DRP_MIN) || (ulProperty > CM_DRP_MAX)) { return CR_INVALID_PROPERTY; } // // 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, UniBufferSize = 0; PWSTR pUniBuffer = NULL; PSTR pAnsiString = NULL; // // validate essential parameters only // if ((!ARGUMENT_PRESENT(Buffer)) && (ulLength != 0)) { return CR_INVALID_POINTER; } if ((ulProperty < CM_DRP_MIN) || (ulProperty > CM_DRP_MAX)) { return CR_INVALID_PROPERTY; } 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) || (ulDataType == REG_MULTI_SZ)) { pAnsiString = (PSTR)Buffer; // // determine the size of the Unicode buffer required to convert buffer // string data to unicode // UniBufferSize = 0; Status = PnPMultiByteToUnicode((PSTR)pAnsiString, ulLength, NULL, &UniBufferSize); ASSERT(Status == CR_BUFFER_SMALL); if (Status != CR_BUFFER_SMALL) { return Status; } // // convert the buffer string data to unicode and pass to wide version // pUniBuffer = pSetupMalloc(UniBufferSize); if (pUniBuffer == NULL) { return CR_OUT_OF_MEMORY; } Status = PnPMultiByteToUnicode((PSTR)pAnsiString, ulLength, pUniBuffer, &UniBufferSize); ASSERT(Status != CR_BUFFER_SMALL); Status = CM_Set_DevNode_Registry_Property_ExW(dnDevInst, ulProperty, pUniBuffer, UniBufferSize, ulFlags, hMachine); pSetupFree(pUniBuffer); } else { // // no need to convert // 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; } if ((ulProperty < CM_CRP_MIN) || (ulProperty > CM_CRP_MAX)) { return CR_INVALID_PROPERTY; } // // 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, UniBufferSize = 0; PWSTR pUniBuffer = NULL; PSTR pAnsiString = NULL; // // validate essential parameters only // if ((!ARGUMENT_PRESENT(Buffer)) && (ulLength != 0)) { return CR_INVALID_POINTER; } if ((ulProperty < CM_CRP_MIN) || (ulProperty > CM_CRP_MAX)) { return CR_INVALID_PROPERTY; } 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) || (ulDataType == REG_MULTI_SZ)) { pAnsiString = (PSTR)Buffer; // // determine the size of the Unicode buffer required to convert buffer // string data to unicode // UniBufferSize = 0; Status = PnPMultiByteToUnicode((PSTR)pAnsiString, ulLength, NULL, &UniBufferSize); ASSERT(Status == CR_BUFFER_SMALL); if (Status != CR_BUFFER_SMALL) { return Status; } // // convert the buffer string data to unicode and pass to wide version // pUniBuffer = pSetupMalloc(UniBufferSize); if (pUniBuffer == NULL) { return CR_OUT_OF_MEMORY; } Status = PnPMultiByteToUnicode((PSTR)pAnsiString, ulLength, pUniBuffer, &UniBufferSize); ASSERT(Status != CR_BUFFER_SMALL); 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); if (Status == CR_SUCCESS) { // // do the ANSI conversion or retrieve the ANSI buffer size required. // Status = PnPUnicodeToMultiByte( UniBuffer, UniLen*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); if (Status == CR_SUCCESS) { // // do the ANSI conversion or retrieve the ANSI buffer size required. // Status = PnPUnicodeToMultiByte( UniBuffer, UniLen*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, UniLen*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); if (Status == CR_SUCCESS) { // // do the ANSI conversion or retrieve the ANSI buffer size required. // 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. // *pulLen = 0; 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))) { Status = CR_INVALID_POINTER; goto Clean0; } // // 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) { pUniReference = NULL; Status = CR_INVALID_DATA; goto Clean0; } } // // 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, UniLen*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; // // Reference the following variables so the compiler will respect // statement ordering w.r.t. their assignment. // pUniDeviceInterface = pUniDeviceInterface; pUniReference = pUniReference; } 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)) { Status = CR_INVALID_POINTER; goto Clean0; } // // 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; } 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. // pUniDeviceInterface = pUniDeviceInterface; } 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; 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. // UnicodeCustomPropName = UnicodeCustomPropName; 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