/*++ Copyright (c) 1995 Microsoft Corporation Module Name: devnode.c Abstract: This module contains the API routines that operate directly on device instances (or DevNodes, in Win95 terminology). CM_Create_DevNode CM_Move_DevNode CM_Setup_DevNode CM_Disble_DevNode CM_Enable_DevNode CM_Get_DevNode_Status CM_Reenumerate_DevNode CM_Query_Remove_Subtree CM_Remove_SubTree CM_Add_ID Author: Paula Tomlinson (paulat) 6-20-1995 Environment: User mode only. Revision History: 6-Jun-1995 paulat Creation and initial implementation. --*/ // // includes // #include "precomp.h" #include "setupapi.h" #include "spapip.h" // // Private prototypes // // // global data // CONFIGRET CM_Create_DevNode_ExW( OUT PDEVINST pdnDevInst, IN DEVINSTID_W pDeviceID, IN DEVINST dnParent, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine creates a new device instance in the hardware tree. Parameters: pdnDevNode Supplies the address of the variable that receives a handle to the new device instance. pDeviceID Supplies a pointer to a NULL-terminated string specifying the device instance ID for this new device instance. This is the registry path (relative to the Enum branch) where this device instance will be located (e.g., Root\*PNP0500\0000). In Windows NT, this parameter is not optional. dnParent Supplies the handle of the device instance that is the parent of the device instance being created. ulFlags Supplies flags specifying options for the creation of the device instance. May be one of the following values: CM_CREATE_DEVNODE_NORMAL Create the device instance now, and perform installation for it at a later time. CM_CREATE_DEVNODE_NO_WAIT_INSTALL Create the device instance, and perform installation for it immediately. CM_CREATE_DEVNODE_PHANTOM Create a phantom device instance (i.e., a handle to a device instance that is not alive as far as the ConfigMgr APIs are concerned). This may be used for CM APIs that require a devnode handle, but for which no real devnode currently exists (e.g., registry property APIs). This flag may not be specified with CR_CREATE_DEVNODE_NORMAL or CR_CREATE_DEVNODE_NO_WAIT_INSTALL. A phantom devnode created in this manner is not accessible to other callers (i.e., CM_Locate_DevNode won't find it). However, callers attempting to create a devnode with the same name as this phantom devnode will not be able to do so (they will get CR_ALREADY_SUCH_DEVNODE). CM_CREATE_DEVNODE_GENERATE_ID Create a Root-enumerated devnode using a unique device instance ID generated from the supplied device ID in pDeviceID. If this flag is set, then pDeviceID is assumed to contain simply a device ID (i.e., no enumerator key prefix, and no device instance suffix). A unique 4-digit, base-10 identifier string will be created under Enum\Root\, and the devnode will be created based on that device instance ID. For instance, to add a new legacy COM port devnode, this API would be called with a pDeviceID of *PNP0500. Assuming there was already one COM port instance in the registry (instance 0000), the new device instance ID would be: Root\*PNP0500\0001 The caller may find out what device instance name was generated by calling CM_Get_Device_ID with the devnode returned from this API. 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_ALREADY_SUCH_DEVNODE, CR_INVALID_DEVICE_ID, CR_INVALID_DEVNODE, CR_INVALID_FLAG, CR_INVALID_POINTER, or CR_OUT_OF_MEMORY. --*/ { CONFIGRET Status = CR_SUCCESS; LPWSTR pParentID = NULL; WCHAR szNewDeviceID[MAX_DEVICE_ID_LEN]; PVOID hStringTable = NULL; handle_t hBinding = NULL; try { // // validate permission // if (!IsUserAdmin()) { Status = CR_ACCESS_DENIED; goto Clean0; } // // validate parameters // if (pdnDevInst == NULL) { Status = CR_INVALID_POINTER; goto Clean0; } if (dnParent == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } if (pDeviceID == NULL || lstrlen(pDeviceID) > MAX_DEVICE_ID_LEN) { Status = CR_INVALID_DEVICE_ID; goto Clean0; } if (INVALID_FLAGS(ulFlags, CM_CREATE_DEVNODE_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; } // // 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) // pParentID = StringTableStringFromId(hStringTable, dnParent); if (pParentID == NULL || INVALID_DEVINST(pParentID)) { Status = CR_INVALID_DEVNODE; goto Clean0; } // // make sure the new device instance is properly formatted // CopyFixedUpDeviceId(szNewDeviceID, pDeviceID, lstrlen(pDeviceID)); RpcTryExcept { // // call rpc service entry point // Status = PNP_CreateDevInst( hBinding, // rpc binding handle szNewDeviceID, // device instance to create pParentID, // parent device instance MAX_DEVICE_ID_LEN, // max length of szNewDeviceID ulFlags); // flags } RpcExcept (1) { PnPTrace( TEXT("PNP_CreateDevInst caused an exception (%d)\n"), RpcExceptionCode()); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept if (Status != CR_SUCCESS) { goto Clean0; } // // assign a unique device instance value to the newly created device // instance // *pdnDevInst = StringTableAddString(hStringTable, szNewDeviceID, STRTAB_CASE_SENSITIVE); if (*pdnDevInst == 0) { Status = CR_NO_SUCH_DEVNODE; } Clean0: ; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Create_DevNodeW CONFIGRET CM_Move_DevNode_Ex( IN DEVINST dnFromDevInst, IN DEVINST dnToDevInst, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine replaces a root-enumerated device instance by the valid non-root-enumerated device instance. The device installer uses this service when it detects that a non-root enumerated device instance is really the same as its root enumerated counterpart. This API migrates the old device instance to the new location, and marks the old location as having a problem. [TBD: what is this marking?] Parameters: dnFromDevNode Supplies the handle of the device instance that has been root enumerated. dnToDevNode Supplies the handle of the device instance that is a reenumeration (duplicate) of the root device instance. ulFlags Must be zero. Return Value: If the function succeeds, the return value is CR_SUCCESS. If the function fails, the return value is one of the following: CR_INVALID_FLAG, CR_INVALID_DEVNODE, CR_OUT_OF_MEMORY. (Windows 95 may also return CR_NOT_AT_APPY_TIME.) --*/ { CONFIGRET Status = CR_SUCCESS; LPWSTR pFromDeviceID = NULL, pToDeviceID = NULL; PVOID hStringTable = NULL; handle_t hBinding = NULL; try { // // validate permission // if (!IsUserAdmin()) { Status = CR_ACCESS_DENIED; goto Clean0; } // // validate parameters // if (INVALID_FLAGS(ulFlags, 0)) { Status = CR_INVALID_FLAG; goto Clean0; } if (dnFromDevInst == 0 || dnToDevInst == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } // // setup rpc binding handle and string table handle // if (!PnPGetGlobalHandles(hMachine, &hStringTable, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // retrieve the device instance ID strings associated with both device // instances passed in, both must be valid // pFromDeviceID = StringTableStringFromId(hStringTable, dnFromDevInst); if (pFromDeviceID == NULL || INVALID_DEVINST(pFromDeviceID)) { Status = CR_INVALID_DEVINST; goto Clean0; } pToDeviceID = StringTableStringFromId(hStringTable, dnToDevInst); if (pToDeviceID == NULL || INVALID_DEVINST(pToDeviceID)) { Status = CR_INVALID_DEVINST; goto Clean0; } RpcTryExcept { // // call rpc service entry point // Status = PNP_DeviceInstanceAction( hBinding, // rpc binding handle PNP_DEVINST_MOVE, // requested major action - MOVE ulFlags, // requested minor action (not used) pToDeviceID, // destination device instance pFromDeviceID); // source device instance } RpcExcept (1) { PnPTrace(TEXT( "PNP_DeviceInstanceAction caused an exception (%d)\n"), RpcExceptionCode()); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: ; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Move_DevNode_Ex CONFIGRET CM_Setup_DevNode_Ex( IN DEVINST dnDevInst, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine reenables and configures a specified device instance or retrieves information from its enumerator. Parameters: dnDevNode Supplies the handle of the device instance which may be reconfigured. ulFlags Supplies a flag indicating the action to take. Can be one of the following values: CM_SETUP_DEVNODE_READY Reenable the device instance that had a problem. CM_SETUP_DOWNLOAD Retrieve information about this device instance from its enumerator. Return Value: If the function succeeds, the return value is CR_SUCCESS. If the function fails, the return value is one of the following: CR_INVALID_FLAG, CR_INVALID_DEVNODE, CR_OUT_OF_MEMORY, or CR_FAILURE. --*/ { CONFIGRET Status = CR_SUCCESS; LPWSTR pDeviceID = NULL; PVOID hStringTable = NULL; handle_t hBinding = NULL; try { // // validate permission // if (!IsUserAdmin()) { Status = CR_ACCESS_DENIED; goto Clean0; } // // validate parameters // if (INVALID_FLAGS(ulFlags, CM_SETUP_DEVNODE_READY | CM_SETUP_DOWNLOAD)) { Status = CR_INVALID_FLAG; goto Clean0; } if (dnDevInst == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } // // setup rpc binding handle and string table handle // if (!PnPGetGlobalHandles(hMachine, &hStringTable, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // retrieve the device instance ID string associated with the devinst // pDeviceID = StringTableStringFromId(hStringTable, dnDevInst); if (pDeviceID == NULL || INVALID_DEVINST(pDeviceID)) { Status = CR_INVALID_DEVINST; goto Clean0; } RpcTryExcept { // // call rpc service entry point // Status = PNP_DeviceInstanceAction( hBinding, // rpc binding handle PNP_DEVINST_SETUP, // requested major action - SETUP ulFlags, // requested minor action pDeviceID, // device instance to create NULL); // (not used) } RpcExcept (1) { PnPTrace( TEXT("PNP_DeviceInstanceAction caused an exception (%d)\n"), RpcExceptionCode()); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: ; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Setup_DevNode_Ex CONFIGRET CM_Disable_DevNode_Ex( IN DEVINST dnDevInst, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine disables a device instance. Parameters: dnDevNode Supplies the handle of the device instance to be disabled. ulFlags Must be zero. Return Value: If the function succeeds, the return value is CR_SUCCESS. If the function fails, the return value is one of the following: CR_INVALID_FLAG, CR_NOT_DISABLEABLE, or CR_INVALID_DEVNODE. (Note, Windows 95 also may return CR_NOT_AT_APPY_TIME.) --*/ { CONFIGRET Status = CR_SUCCESS; LPWSTR pDeviceID = NULL; PVOID hStringTable = NULL; handle_t hBinding = NULL; try { // // validate permission // if (!IsUserAdmin()) { Status = CR_ACCESS_DENIED; goto Clean0; } // // validate parameters // if (INVALID_FLAGS(ulFlags, CM_DISABLE_BITS)) { Status = CR_INVALID_FLAG; goto Clean0; } if (dnDevInst == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } // // setup rpc binding handle and string table handle // if (!PnPGetGlobalHandles(hMachine, &hStringTable, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // retrieve the device instance ID string associated with the devinst // pDeviceID = StringTableStringFromId(hStringTable, dnDevInst); if (pDeviceID == NULL || INVALID_DEVINST(pDeviceID)) { Status = CR_INVALID_DEVINST; goto Clean0; } RpcTryExcept { // // call rpc service entry point // Status = PNP_DeviceInstanceAction( hBinding, // rpc binding handle PNP_DEVINST_DISABLE, // requested major action - DISABLE ulFlags, // requested minor action (not used) pDeviceID, // device instance to create NULL); // (not used) } RpcExcept (1) { PnPTrace( TEXT("PNP_DeviceInstanceAction caused an exception (%d)\n"), RpcExceptionCode()); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: ; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Disable_DevNode_Ex CONFIGRET CM_Enable_DevNode_Ex( IN DEVINST dnDevInst, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine enables a device instance. Parameters: dnDevNode Supplies the handle of the device instance to enable. ulFlags Must be zero. Return Value: If the function succeeds, the return value is CR_SUCCESS. If the function fails, the return value is one of the following: CR_INVALID_FLAG or CR_INVALID_DEVNODE. (Note, Windows 95 also may return CR_NOT_AT_APPY_TIME.) --*/ { CONFIGRET Status = CR_SUCCESS; LPWSTR pDeviceID = NULL; PVOID hStringTable = NULL; handle_t hBinding = NULL; try { // // validate permission // if (!IsUserAdmin()) { Status = CR_ACCESS_DENIED; goto Clean0; } // // validate parameters // if (INVALID_FLAGS(ulFlags, 0)) { Status = CR_INVALID_FLAG; goto Clean0; } if (dnDevInst == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } // // setup rpc binding handle and string table handle // if (!PnPGetGlobalHandles(hMachine, &hStringTable, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // retrieve the device instance ID string associated with the devinst // pDeviceID = StringTableStringFromId(hStringTable, dnDevInst); if (pDeviceID == NULL || INVALID_DEVINST(pDeviceID)) { Status = CR_INVALID_DEVINST; goto Clean0; } RpcTryExcept { // // call rpc service entry point // Status = PNP_DeviceInstanceAction( hBinding, // rpc binding handle PNP_DEVINST_ENABLE, // requested major action - ENABLE ulFlags, // requested minor action (not used) pDeviceID, // device instance to create NULL); // (not used) } RpcExcept (1) { PnPTrace( TEXT("PNP_DeviceInstanceAction caused an exception (%d)\n"), RpcExceptionCode()); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: ; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Enable_DevNode_Ex CONFIGRET CM_Get_DevNode_Status_Ex( OUT PULONG pulStatus, OUT PULONG pulProblemNumber, IN DEVINST dnDevInst, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine retrieves the status of a device instance. Parameters: pulStatus Supplies the address of the variable that receives the status flag of the device instance. Can be a combination of the DN_* values. pulProblemNumber Supplies the address of the variable that receives an identifier indicating the problem. Can be one of the CM_PROB_* values. dnDevNode Supplies the handle of the device instance for which to retrieve status. ulFlags Must be zero. Return Value: If the function succeeds, the return value is CR_SUCCESS. If the function fails, the return value is one of the following: CR_INVALID_DEVNODE, CR_INVALID_FLAG, or CR_INVALID_POINTER. --*/ { CONFIGRET Status = CR_SUCCESS; LPWSTR pDeviceID = NULL; PVOID hStringTable = NULL; handle_t hBinding = NULL; try { // // validate parameters // if (dnDevInst == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } if (pulStatus == NULL || pulProblemNumber == NULL) { Status = CR_INVALID_POINTER; 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; } // // retrieve the device instance ID string associated with the devinst // pDeviceID = StringTableStringFromId(hStringTable, dnDevInst); if (pDeviceID == NULL) { Status = CR_INVALID_DEVINST; goto Clean0; } RpcTryExcept { // // call rpc service entry point // Status = PNP_GetDeviceStatus( hBinding, // rpc binding handle pDeviceID, // device instance to get status for pulStatus, // return StatusFlags here pulProblemNumber, // return Problem here ulFlags); // (not used) } RpcExcept (1) { PnPTrace( TEXT("PNP_GetDeviceAction caused an exception (%d)\n"), RpcExceptionCode()); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: ; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Get_DevNode_Status_Ex CONFIGRET CM_Reenumerate_DevNode_Ex( IN DEVINST dnDevInst, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine causes the specified device instance to be enumerated (if it is enumerable). Parameters: dnDevNode Supplies the handle of the device instance to be enumerated. ulFlags Must be zero. Return Value: If the function succeeds, the return value is CR_SUCCESS. If the function fails, the return value is CR_INVALID_FLAG (i.e., the function does not fail). The device instance is not checked for validity. --*/ { CONFIGRET Status = CR_SUCCESS; LPWSTR pDeviceID = NULL; PVOID hStringTable = NULL; handle_t hBinding = NULL; try { // // validate permission // if (!IsUserAdmin()) { Status = CR_ACCESS_DENIED; goto Clean0; } // // validate parameters // if (INVALID_FLAGS(ulFlags, CM_REENUMERATE_BITS)) { Status = CR_INVALID_FLAG; goto Clean0; } if (dnDevInst == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } // // setup rpc binding handle and string table handle // if (!PnPGetGlobalHandles(hMachine, &hStringTable, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // retrieve the device instance ID string associated with the devinst // pDeviceID = StringTableStringFromId(hStringTable, dnDevInst); if (pDeviceID == NULL || INVALID_DEVINST(pDeviceID)) { Status = CR_INVALID_DEVINST; goto Clean0; } RpcTryExcept { // // call rpc service entry point // Status = PNP_DeviceInstanceAction( hBinding, // rpc binding handle PNP_DEVINST_REENUMERATE, // requested major action-REMOVESUBTREE ulFlags, // requested minor action pDeviceID, // device instance subtree to remove NULL); // (not used) } RpcExcept (1) { PnPTrace( TEXT("PNP_DeviceInstanceAction caused an exception (%d)\n"), RpcExceptionCode()); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: ; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Reenumerate_DevNode_Ex CONFIGRET CM_Query_Remove_SubTree_Ex( IN DEVINST dnAncestor, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine checks whether a device instance and its progeny can be removed. This API must be called before calling CM_Remove_SubTree to make sure applications prepare for the removal of the device or to give the applications a chance to deny the request to remove the device. If the removal happens “surprise style” (i.e., there’s no advanced warning or chance to veto), then this API should not be called before calling CM_Remove_SubTree. Parameters: dnAncestor Supplies the handle of the device instance at the root of the subtree to be removed. ulFlags Specifies whether UI should be presented for this action. Can be one of the following values: CM_QUERY_REMOVE_UI_OK - OK to present UI for query-removal. CM_QUERY_REMOVE_UI_NOT_OK -Don't present UI for query-removal. 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_REMOVE_VETOED. (Windows 95 may also return CR_NOT_AT_APPY_TIME.) --*/ { CONFIGRET Status = CR_SUCCESS; LPWSTR pDeviceID = NULL; PVOID hStringTable = NULL; handle_t hBinding = NULL; try { // // validate permission // if (!IsUserAdmin()) { Status = CR_ACCESS_DENIED; goto Clean0; } // // validate parameters // if (dnAncestor == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } if (INVALID_FLAGS(ulFlags, CM_QUERY_REMOVE_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 device instance ID string associated with the devinst // pDeviceID = StringTableStringFromId(hStringTable, dnAncestor); if (pDeviceID == NULL || INVALID_DEVINST(pDeviceID)) { Status = CR_INVALID_DEVINST; goto Clean0; } RpcTryExcept { // // call rpc service entry point // Status = PNP_DeviceInstanceAction( hBinding, // rpc binding handle PNP_DEVINST_QUERYREMOVE, // requested major action-QUERYREMOVE ulFlags, // requested minor action (not used) pDeviceID, // device instance subtree to remove NULL); // (not used) } RpcExcept (1) { PnPTrace( TEXT("PNP_DeviceInstanceAction caused an exception (%d)\n"), RpcExceptionCode()); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: ; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Query_Remove_SubTree_Ex CONFIGRET CM_Remove_SubTree_Ex( IN DEVINST dnAncestor, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine removes a device instance and its children from the running system. This API notifies each device instance in the subtree of the dnAncestor parameter of the device's removal. (On Windows NT, this means that each driver/service controlling a device in this subtree receives a device removal notification.) Parameters: dnAncestor Supplies the handle of the device instance that is being removed. ulFlags Must be either CM_REMOVE_UI_OK or CM_REMOVE_UI_NOT_OK. 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 or CR_INVALID_FLAG. (Windows 95 may also return CR_NOT_AT_APPY_TIME.) --*/ { CONFIGRET Status = CR_SUCCESS; LPWSTR pDeviceID = NULL; PVOID hStringTable = NULL; handle_t hBinding = NULL; try { // // validate permission // if (!IsUserAdmin()) { Status = CR_ACCESS_DENIED; goto Clean0; } // // validate parameters // if (dnAncestor == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } if (INVALID_FLAGS(ulFlags, CM_REMOVE_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 device instance ID string associated with the devinst // pDeviceID = StringTableStringFromId(hStringTable, dnAncestor); if (pDeviceID == NULL || INVALID_DEVINST(pDeviceID)) { Status = CR_INVALID_DEVINST; goto Clean0; } RpcTryExcept { // // call rpc service entry point // Status = PNP_DeviceInstanceAction( hBinding, // rpc binding handle PNP_DEVINST_REMOVESUBTREE, // requested major action-REMOVESUBTREE ulFlags, // requested minor action (not used) pDeviceID, // device instance subtree to remove NULL); // (not used) } RpcExcept (1) { PnPTrace( TEXT("PNP_DeviceInstanceAction caused an exception (%d)\n"), RpcExceptionCode()); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: ; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Remove_SubTree CONFIGRET CM_Uninstall_DevNode_Ex( IN DEVNODE dnDevInst, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine uninstalls a device instance (i.e., deletes its registry key(s) in the Enum branch). This API can only be called for phantom device instances, and the handle supplied is invalid after the call. This API does not attempt to delete all possible storage locations associated with the device instance. It will do a recursive delete on the devnode key, so that any subkeys will be removed. It will also delete the devnode key (and any subkeys) located in the Enum branch of each hardware profile. It will not delete any software keys or user keys (CM_Delete_DevNode_Key must be called to do that before calling this API). Parameters: dnPhantom Handle of a phantom device instance to uninstall. This handle is typically retrieved by a call to CM_Locate_DevNode or CM_Create_DevNode. ulFlags Must be zero. Return Value: If the function succeeds, the return value is CR_SUCCESS. If the function fails, the return value is one of the following: CR_INVALID_DEVNODE, CR_INVALID_FLAG, or CR_REGISTRY_ERROR --*/ { CONFIGRET Status = CR_SUCCESS; LPWSTR pDeviceID = NULL; PVOID hStringTable = NULL; handle_t hBinding = NULL; WCHAR szParentKey[MAX_DEVICE_ID_LEN], szChildKey[MAX_DEVICE_ID_LEN]; try { // // validate parameters // 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; } // // retrieve the device instance ID string associated with the devinst // pDeviceID = StringTableStringFromId(hStringTable, dnDevInst); if (pDeviceID == NULL || INVALID_DEVINST(pDeviceID)) { Status = CR_INVALID_DEVINST; goto Clean0; } RpcTryExcept { // // call rpc service entry point // Status = PNP_UninstallDevInst( hBinding, // rpc binding handle pDeviceID, // device instance to uninstall ulFlags); // (unused) } RpcExcept (1) { PnPTrace( TEXT("PNP_DeinstallDevInst caused an exception (%d)\n"), RpcExceptionCode()); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept //------------------------------------------------------------------ // after deleting the main hw key and the config specific hw keys, // cleanup the user hw key, which can only be done on the client // side. //------------------------------------------------------------------ // // form the user hardware registry key path // Status = GetDevNodeKeyPath(hBinding, pDeviceID, CM_REGISTRY_HARDWARE | CM_REGISTRY_USER, 0, szParentKey, szChildKey); if (Status != CR_SUCCESS) { goto Clean0; } // // delete the specified private user key // Status = DeletePrivateKey(HKEY_CURRENT_USER, szParentKey, szChildKey); Clean0: ; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Uninstall_DevNode CONFIGRET CM_Add_ID_ExW( IN DEVINST dnDevInst, IN PWSTR pszID, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine adds a device ID to a device instance’s HardwareID or CompatibleIDs list. Parameters: dnDevInst Handle of a device instance. This handle is typically retrieved by a call to CM_Locate_DevNode or CM_Create_DevNode. pszID Supplies a pointer to a NULL-terminated string specifying the ID to be added. ulFlags Supplies flags for the ID. May be one of the following values: ID Type Flags: CM_ADD_ID_HARDWARE The specified ID is a hardware ID. Add it to the device instance's HardwareID list. CM_ADD_ID_COMPATIBLE The specified ID is a compatible ID. Add it to the device instance's CompatibleIDs list. Return Value: If the function succeeds, the return value is CR_SUCCESS. If the function fails, the return value is a CR error code. --*/ { CONFIGRET Status = CR_SUCCESS; LPWSTR pDeviceID = NULL; PVOID hStringTable = NULL; handle_t hBinding = NULL; try { // // validate permission // if (!IsUserAdmin()) { Status = CR_ACCESS_DENIED; goto Clean0; } // // validate parameters // if (dnDevInst == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } if (pszID == NULL) { Status = CR_INVALID_POINTER; goto Clean0; } if (INVALID_FLAGS(ulFlags, CM_ADD_ID_HARDWARE | CM_ADD_ID_COMPATIBLE)) { 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 device instance ID string associated with the devinst // pDeviceID = StringTableStringFromId(hStringTable, dnDevInst); if (pDeviceID == NULL || INVALID_DEVINST(pDeviceID)) { Status = CR_INVALID_DEVINST; goto Clean0; } RpcTryExcept { // // call rpc service entry point // Status = PNP_AddID( hBinding, // rpc binding handle pDeviceID, // device instance pszID, // id to add ulFlags); // hardware or compatible } RpcExcept (1) { PnPTrace( TEXT("PNP_AddID caused an exception (%d)\n"), RpcExceptionCode()); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: ; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Add_ID_ExW #if 0 //------------------------------------------------------------------- // NOT IMPLEMENTED ROUTINES //------------------------------------------------------------------- CONFIGRET CM_Remove_Unmarked_Children_Ex( IN DEVINST dnDevInst, IN ULONG ulFlags, IN HMACHINE hMachine ) { return CR_CALL_NOT_IMPLEMENTED; } CONFIGRET CM_Reset_Children_Marks_Ex( IN DEVINST dnDevInst, IN ULONG ulFlags, IN HMACHINE hMachine ) { return CR_CALL_NOT_IMPLEMENTED; } #endif //------------------------------------------------------------------- // Local Stubs //------------------------------------------------------------------- CONFIGRET CM_Create_DevNodeW( OUT PDEVINST pdnDevInst, IN DEVINSTID_W pDeviceID, IN DEVINST dnParent, IN ULONG ulFlags ) { return CM_Create_DevNode_ExW(pdnDevInst, pDeviceID, dnParent, ulFlags, NULL); } CONFIGRET CM_Create_DevNodeA( OUT PDEVINST pdnDevInst, IN DEVINSTID_A pDeviceID, IN DEVINST dnParent, IN ULONG ulFlags ) { return CM_Create_DevNode_ExA(pdnDevInst, pDeviceID, dnParent, ulFlags, NULL); } CONFIGRET CM_Move_DevNode( IN DEVINST dnFromDevInst, IN DEVINST dnToDevInst, IN ULONG ulFlags ) { return CM_Move_DevNode_Ex(dnFromDevInst, dnToDevInst, ulFlags, NULL); } CONFIGRET CM_Setup_DevNode( IN DEVINST dnDevInst, IN ULONG ulFlags ) { return CM_Setup_DevNode_Ex(dnDevInst, ulFlags, NULL); } CONFIGRET CM_Disable_DevNode( IN DEVINST dnDevInst, IN ULONG ulFlags ) { return CM_Disable_DevNode_Ex(dnDevInst, ulFlags, NULL); } CONFIGRET CM_Enable_DevNode( IN DEVINST dnDevInst, IN ULONG ulFlags ) { return CM_Enable_DevNode_Ex(dnDevInst, ulFlags, NULL); } CONFIGRET CM_Get_DevNode_Status( OUT PULONG pulStatus, OUT PULONG pulProblemNumber, IN DEVINST dnDevInst, IN ULONG ulFlags ) { return CM_Get_DevNode_Status_Ex(pulStatus, pulProblemNumber, dnDevInst, ulFlags, NULL); } CONFIGRET CM_Reenumerate_DevNode( IN DEVINST dnDevInst, IN ULONG ulFlags ) { return CM_Reenumerate_DevNode_Ex(dnDevInst, ulFlags, NULL); } CONFIGRET CM_Remove_SubTree( IN DEVINST dnAncestor, IN ULONG ulFlags ) { return CM_Remove_SubTree_Ex(dnAncestor, ulFlags, NULL); } CONFIGRET CM_Uninstall_DevNode( IN DEVNODE dnPhantom, IN ULONG ulFlags ) { return CM_Uninstall_DevNode_Ex(dnPhantom, ulFlags, NULL); } CONFIGRET CM_Add_IDW( IN DEVINST dnDevInst, IN PWSTR pszID, IN ULONG ulFlags ) { return CM_Add_ID_ExW(dnDevInst, pszID, ulFlags, NULL); } CONFIGRET CM_Add_IDA( IN DEVINST dnDevInst, IN PSTR pszID, IN ULONG ulFlags ) { return CM_Add_ID_ExA(dnDevInst, pszID, ulFlags, NULL); } CONFIGRET CM_Query_Remove_SubTree( IN DEVINST dnAncestor, IN ULONG ulFlags ) { return CM_Query_Remove_SubTree_Ex(dnAncestor, ulFlags, NULL); } //------------------------------------------------------------------- // ANSI STUBS //------------------------------------------------------------------- CONFIGRET CM_Add_ID_ExA( IN DEVINST dnDevInst, IN PSTR pszID, IN ULONG ulFlags, IN HMACHINE hMachine ) { CONFIGRET Status = CR_SUCCESS; PWSTR pUniID = NULL; if (CaptureAndConvertAnsiArg(pszID, &pUniID) == NO_ERROR) { Status = CM_Add_ID_ExW(dnDevInst, pUniID, ulFlags, hMachine); MyFree(pUniID); } else { Status = CR_INVALID_POINTER; } return Status; } // CM_Add_ID_ExA CONFIGRET CM_Create_DevNode_ExA( OUT PDEVINST pdnDevInst, IN DEVINSTID_A pDeviceID, IN DEVINST dnParent, IN ULONG ulFlags, IN HMACHINE hMachine ) { CONFIGRET Status = CR_SUCCESS; PWSTR pUniDeviceID = NULL; if (CaptureAndConvertAnsiArg(pDeviceID, &pUniDeviceID) == NO_ERROR) { Status = CM_Create_DevNode_ExW(pdnDevInst, pUniDeviceID, dnParent, ulFlags, hMachine); MyFree(pUniDeviceID); } else { Status = CR_INVALID_DEVICE_ID; } return Status; } // CM_Create_DevNode_ExA