/*++

Copyright (c) 1995-2001  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_Setup_DevNode
               CM_Disable_DevNode
               CM_Enable_DevNode
               CM_Get_DevNode_Status
               CM_Set_DevNode_Problem
               CM_Reenumerate_DevNode
               CM_Query_And_Remove_SubTree
               CM_Uninstall_DevNode
               CM_Request_Device_Eject
               CM_Add_ID
               CM_Register_Device_Driver

    This module also contains the following API routines which are
    not implemented.

               CM_Move_DevNode
               CM_Query_Remove_Subtree
               CM_Remove_SubTree

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 "cfgi.h"

#include "setupapi.h"
#include "spapip.h"
#include <pnpmgr.h>



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\<pDeviceID>, 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;
    WCHAR       ParentID[MAX_DEVICE_ID_LEN];
    WCHAR       szNewDeviceID[MAX_DEVICE_ID_LEN];
    PVOID       hStringTable = NULL;
    handle_t    hBinding = NULL;
    ULONG       ulLen=MAX_DEVICE_ID_LEN;
    BOOL        Success;

    try {
        //
        // validate parameters
        //
        if (!ARGUMENT_PRESENT(pdnDevInst)) {
            Status = CR_INVALID_POINTER;
            goto Clean0;
        }

        if (dnParent == 0) {
            Status = CR_INVALID_DEVINST;
            goto Clean0;
        }

        //
        // the length of the supplied device id string must be shorter than
        // MAX_DEVICE_ID_LEN chars so that there is also room for the NULL term
        // char in a buffer of this size.  (many of the CM_ APIs make different
        // assumptions about the consideration of the NULL term char in
        // MAX_DEVICE_ID_LEN; account for the NULL term char to be safe)
        //
        if ((!ARGUMENT_PRESENT(pDeviceID)) ||
            (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;
        }

        //
        // Windows NT 5.0 does not support CM_CREATE_DEVNODE_NO_WAIT_INSTALL
        //
        if (ulFlags & CM_CREATE_DEVNODE_NO_WAIT_INSTALL) {
            Status = CR_INVALID_FLAG;
            goto Clean0;
        }

        //
        // setup rpc binding handle and string table handle
        //
        if (!PnPGetGlobalHandles(hMachine, &hStringTable, &hBinding)) {
            Status = CR_FAILURE;
            goto Clean0;
        }

        //
        // Initialize the caller's devnode.  This will have the side effect of
        // generating an exception before we actually do anything if the caller
        // supplied a bogus address.
        //
        *pdnDevInst = 0;

        //
        // 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, dnParent,ParentID,&ulLen);
        if (Success == FALSE || INVALID_DEVINST(ParentID)) {
            Status = CR_INVALID_DEVNODE;
            goto Clean0;
        }

        //
        // make sure the new device instance is properly formatted
        //
        CopyFixedUpDeviceId(szNewDeviceID, pDeviceID, lstrlen(pDeviceID));

        //
        // If not requesting instance generation, then it must be a
        // valid device instance path.
        //
        if (!(ulFlags & CM_CREATE_DEVINST_GENERATE_ID)) {
            if ((!*szNewDeviceID) ||
                (!IsLegalDeviceId(szNewDeviceID))) {
                Status = CR_INVALID_DEVINST;
                goto Clean0;
            }
        }

        RpcTryExcept {
            //
            // call rpc service entry point
            //
            Status = PNP_CreateDevInst(
                hBinding,               // rpc binding handle
                szNewDeviceID,          // device instance to create
                ParentID,               // parent device instance
                MAX_DEVICE_ID_LEN,      // max length of szNewDeviceID
                ulFlags);               // flags
        }
        RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
            KdPrintEx((DPFLTR_PNPMGR_ID,
                       DBGF_ERRORS,
                       "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
        //

        ASSERT(*szNewDeviceID && IsLegalDeviceId(szNewDeviceID));

        *pdnDevInst = pSetupStringTableAddString(hStringTable, szNewDeviceID,
                                           STRTAB_CASE_SENSITIVE);

        if (*pdnDevInst == 0) {
            Status = CR_NO_SUCH_DEVNODE;
        }

    Clean0:
        NOTHING;

    } except(EXCEPTION_EXECUTE_HANDLER) {
        Status = CR_FAILURE;
    }

    return Status;

} // CM_Create_DevNode_ExW



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.

   ** THIS ROUTINE IS NOT IMPLEMENTED **

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:

   ** PRESENTLY, ALWAYS RETURNS CR_CALL_NOT_IMPLEMENTED **

   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.)

--*/

{
    UNREFERENCED_PARAMETER(dnFromDevInst);
    UNREFERENCED_PARAMETER(dnToDevInst);
    UNREFERENCED_PARAMETER(ulFlags);
    UNREFERENCED_PARAMETER(hMachine);

    return CR_CALL_NOT_IMPLEMENTED;

} // 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;
    WCHAR       DeviceID[MAX_DEVICE_ID_LEN];
    PVOID       hStringTable = NULL;
    handle_t    hBinding = NULL;
    ULONG       ulLen=MAX_DEVICE_ID_LEN;
    BOOL        Success;

    try {
        //
        // validate parameters
        //
        if (INVALID_FLAGS(ulFlags, CM_SETUP_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
        //
        Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,DeviceID,&ulLen);
        if (Success == FALSE || INVALID_DEVINST(DeviceID)) {
            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
                DeviceID,               // device instance to create
                NULL);                  // (not used)
        }
        RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
            KdPrintEx((DPFLTR_PNPMGR_ID,
                       DBGF_ERRORS,
                       "PNP_DeviceInstanceAction caused an exception (%d)\n",
                       RpcExceptionCode()));

            Status = MapRpcExceptionToCR(RpcExceptionCode());
        }
        RpcEndExcept

    Clean0:
        NOTHING;

    } 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     May be one of CM_DISABLE_BITS.

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;
    WCHAR           DeviceID[MAX_DEVICE_ID_LEN];
    ULONG           ulLen = MAX_DEVICE_ID_LEN;
    PVOID           hStringTable = NULL;
    handle_t        hBinding = NULL;
    BOOL            Success;
    PNP_VETO_TYPE   vetoType, *pVetoType;
    WCHAR           vetoName[MAX_DEVICE_ID_LEN], *pszVetoName;
    ULONG           ulNameLength;


    try {
        //
        // 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
        //
        Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,DeviceID,&ulLen);
        if (Success == FALSE || INVALID_DEVINST(DeviceID)) {
            Status = CR_INVALID_DEVINST;
            goto Clean0;
        }

        if (ulFlags & CM_DISABLE_UI_NOT_OK) {
            vetoType = PNP_VetoTypeUnknown;
            pVetoType = &vetoType;
            vetoName[0] = L'\0';
            pszVetoName = &vetoName[0];
            ulNameLength = MAX_DEVICE_ID_LEN;
        } else {
            pVetoType = NULL;
            pszVetoName = NULL;
            ulNameLength = 0;
        }

        RpcTryExcept {
            //
            // call rpc service entry point
            //
            Status = PNP_DisableDevInst(
                hBinding,                // rpc binding handle
                DeviceID,                // device instance to create
                pVetoType,
                pszVetoName,
                ulNameLength,
                ulFlags);                // requested minor action (not used)
        }
        RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
            KdPrintEx((DPFLTR_PNPMGR_ID,
                       DBGF_ERRORS,
                       "PNP_DisableDevInst caused an exception (%d)\n",
                       RpcExceptionCode()));

            Status = MapRpcExceptionToCR(RpcExceptionCode());
        }
        RpcEndExcept

    Clean0:
        NOTHING;

    } 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;
    WCHAR       DeviceID[MAX_DEVICE_ID_LEN];
    ULONG       ulLen = MAX_DEVICE_ID_LEN;
    PVOID       hStringTable = NULL;
    handle_t    hBinding = NULL;
    BOOL        Success;


    try {
        //
        // 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
        //
        Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,DeviceID,&ulLen);
        if (Success == FALSE || INVALID_DEVINST(DeviceID)) {
            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)
                DeviceID,               // device instance to create
                NULL);                  // (not used)
        }
        RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
            KdPrintEx((DPFLTR_PNPMGR_ID,
                       DBGF_ERRORS,
                       "PNP_DeviceInstanceAction caused an exception (%d)\n",
                       RpcExceptionCode()));

            Status = MapRpcExceptionToCR(RpcExceptionCode());
        }
        RpcEndExcept

    Clean0:
        NOTHING;

    } 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;
    WCHAR       DeviceID [MAX_DEVICE_ID_LEN];
    ULONG       ulLen = MAX_DEVICE_ID_LEN;
    PVOID       hStringTable = NULL;
    handle_t    hBinding = NULL;
    BOOL        Success;


    try {
        //
        // validate parameters
        //
        if (dnDevInst == 0) {
            Status = CR_INVALID_DEVINST;
            goto Clean0;
        }

        if ((!ARGUMENT_PRESENT(pulStatus)) ||
            (!ARGUMENT_PRESENT(pulProblemNumber))) {
            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
        //
        Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,DeviceID,&ulLen);
        if (Success == FALSE) {
            Status = CR_INVALID_DEVINST;
            goto Clean0;
        }

        RpcTryExcept {
            //
            // call rpc service entry point
            //
            Status = PNP_GetDeviceStatus(
                hBinding,               // rpc binding handle
                DeviceID,               // device instance to get status for
                pulStatus,              // return StatusFlags here
                pulProblemNumber,       // return Problem here
                ulFlags);               // (not used)
        }
        RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
            KdPrintEx((DPFLTR_PNPMGR_ID,
                       DBGF_ERRORS,
                       "PNP_GetDeviceStatus caused an exception (%d)\n",
                       RpcExceptionCode()));

            Status = MapRpcExceptionToCR(RpcExceptionCode());
        }
        RpcEndExcept

    Clean0:
        NOTHING;

    } except(EXCEPTION_EXECUTE_HANDLER) {
        Status = CR_FAILURE;
    }

    return Status;

} // CM_Get_DevNode_Status_Ex



CONFIGRET
CM_Set_DevNode_Problem_Ex(
    IN DEVINST   dnDevInst,
    IN ULONG     ulProblem,
    IN  ULONG    ulFlags,
    IN  HMACHINE hMachine
    )

/*++

Routine Description:

   This routine clears or set the problem of a device instance.

Parameters:

   dnDevNode    Supplies the handle of the device instance for which
                to set the problem.

   ulProblem    Supplies the new problem value.  Can be one of the
                CM_PROB_* values. If zero, the problem is cleared.

   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.

--*/

{
    CONFIGRET   Status = CR_SUCCESS;
    WCHAR       DeviceID[MAX_DEVICE_ID_LEN];
    PVOID       hStringTable = NULL;
    handle_t    hBinding = NULL;
    ULONG       ulLen = MAX_DEVICE_ID_LEN;
    BOOL        Success;

    try {
        //
        // validate parameters
        //
        if (dnDevInst == 0) {
            Status = CR_INVALID_DEVINST;
            goto Clean0;
        }

        if (INVALID_FLAGS(ulFlags, CM_SET_DEVNODE_PROBLEM_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
        //
        Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,DeviceID,&ulLen);
        if (Success == FALSE) {
            Status = CR_INVALID_DEVINST;
            goto Clean0;
        }

        RpcTryExcept {
            //
            // call rpc service entry point
            //
            Status = PNP_SetDeviceProblem(
                hBinding,   // rpc binding handle
                DeviceID,   // device instance
                ulProblem,  // specifies new Problem
                ulFlags);   // (not used)
        }
        RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
            KdPrintEx((DPFLTR_PNPMGR_ID,
                       DBGF_ERRORS,
                       "PNP_SetDeviceProblem caused an exception (%d)\n",
                       RpcExceptionCode()));

            Status = MapRpcExceptionToCR(RpcExceptionCode());
        }
        RpcEndExcept

    Clean0:
        NOTHING;

    } except(EXCEPTION_EXECUTE_HANDLER) {
        Status = CR_FAILURE;
    }

    return Status;

} // CM_Set_DevNode_Problem_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;
    WCHAR       DeviceID [MAX_DEVICE_ID_LEN];
    PVOID       hStringTable = NULL;
    handle_t    hBinding = NULL;
    ULONG       ulLen = MAX_DEVICE_ID_LEN;
    BOOL        Success;

    try {
        //
        // 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
        //
        Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,DeviceID,&ulLen);
        if (Success == FALSE || INVALID_DEVINST(DeviceID)) {
            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
                DeviceID,                  // device instance subtree to remove
                NULL);                     // (not used)
        }
        RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
            KdPrintEx((DPFLTR_PNPMGR_ID,
                       DBGF_ERRORS,
                       "PNP_DeviceInstanceAction caused an exception (%d)\n",
                       RpcExceptionCode()));

            Status = MapRpcExceptionToCR(RpcExceptionCode());
        }
        RpcEndExcept

    Clean0:
        NOTHING;

    } except(EXCEPTION_EXECUTE_HANDLER) {
        Status = CR_FAILURE;
    }

    return Status;

} // CM_Reenumerate_DevNode_Ex



CONFIGRET
CM_Query_And_Remove_SubTree_ExW(
    IN  DEVINST         dnAncestor,
    OUT PPNP_VETO_TYPE  pVetoType,
    OUT LPWSTR          pszVetoName,
    IN  ULONG           ulNameLength,
    IN  ULONG           ulFlags,
    IN  HMACHINE        hMachine
    )

/*++

Routine Description:

   This routine checks whether a device instance and its progeny can be
   removed.  If the query isn't vetoed then a remove is done.  The replaces
   the old CM_Query_Remove_SubTree followed by 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_REMOVE_UI_OK       - OK to present UI for query-removal.
               CM_REMOVE_UI_NOT_OK   - Don't present UI for query-removal.
               CM_REMOVE_NO_RESTART  - Don't attempt to restart the devnode.

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;
    WCHAR       pDeviceID [MAX_DEVICE_ID_LEN];
    PVOID       hStringTable = NULL;
    handle_t    hBinding = NULL;
    ULONG       ulLen = MAX_DEVICE_ID_LEN;

    try {
        //
        // 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
        //
        pSetupStringTableStringFromIdEx(hStringTable, dnAncestor,pDeviceID,&ulLen);

        ASSERT(pDeviceID && *pDeviceID && IsLegalDeviceId(pDeviceID));

        if (pDeviceID == NULL || INVALID_DEVINST(pDeviceID)) {
            Status = CR_INVALID_DEVINST;
            goto Clean0;
        }

        if (ulNameLength == 0) {
            pszVetoName = NULL;
        }

        if (pszVetoName != NULL) {
            *pszVetoName = L'\0';
        }

        if (pVetoType != NULL) {
            *pVetoType = PNP_VetoTypeUnknown;
        }

        RpcTryExcept {
            //
            // call rpc service entry point
            //
            Status = PNP_QueryRemove(
                hBinding,                   // rpc binding handle
                pDeviceID,                  // device instance subtree to remove
                pVetoType,
                pszVetoName,
                ulNameLength,
                ulFlags);
        }
        RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
            KdPrintEx((DPFLTR_PNPMGR_ID,
                       DBGF_ERRORS,
                       "PNP_QueryRemove caused an exception (%d)\n",
                       RpcExceptionCode()));

            Status = MapRpcExceptionToCR(RpcExceptionCode());
        }
        RpcEndExcept

    Clean0:
        NOTHING;

    } except(EXCEPTION_EXECUTE_HANDLER) {
        Status = CR_FAILURE;
    }

    return Status;

} // CM_Query_And_Remove_SubTree_ExW



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:

   ** PRESENTLY, ALWAYS RETURNS CR_CALL_NOT_IMPLEMENTED **

   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.)

--*/

{
    UNREFERENCED_PARAMETER(dnAncestor);
    UNREFERENCED_PARAMETER(ulFlags);
    UNREFERENCED_PARAMETER(hMachine);

    return CR_CALL_NOT_IMPLEMENTED;

} // 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:

   ** PRESENTLY, ALWAYS RETURNS CR_CALL_NOT_IMPLEMENTED **

   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.)

--*/

{
    UNREFERENCED_PARAMETER(dnAncestor);
    UNREFERENCED_PARAMETER(ulFlags);
    UNREFERENCED_PARAMETER(hMachine);

    return CR_CALL_NOT_IMPLEMENTED;

} // CM_Remove_SubTree_Ex



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;
    PVOID       hStringTable = NULL;
    handle_t    hBinding = NULL;
    ULONG       ulLen = MAX_DEVICE_ID_LEN;
    WCHAR       szParentKey[MAX_CM_PATH], szChildKey[MAX_CM_PATH],
                DeviceID[MAX_DEVICE_ID_LEN];
    BOOL        Success;


    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
        //
        Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,DeviceID,&ulLen);
        if (Success == FALSE || INVALID_DEVINST(DeviceID)) {
            Status = CR_INVALID_DEVINST;
            goto Clean0;
        }

        RpcTryExcept {
            //
            // call rpc service entry point
            //
            Status = PNP_UninstallDevInst(
                hBinding,                  // rpc binding handle
                DeviceID,                  // device instance to uninstall
                ulFlags);                  // (unused)
        }
        RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
            KdPrintEx((DPFLTR_PNPMGR_ID,
                       DBGF_ERRORS,
                       "PNP_UninstallDevInst 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, DeviceID,
                                   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:
        NOTHING;

    } except(EXCEPTION_EXECUTE_HANDLER) {
        Status = CR_FAILURE;
    }

    return Status;

} // CM_Uninstall_DevNode_Ex



CONFIGRET
CM_Request_Device_Eject_ExW(
    IN DEVNODE          dnDevInst,
    OUT PPNP_VETO_TYPE  pVetoType,
    OUT LPWSTR          pszVetoName,
    IN  ULONG           ulNameLength,
    IN  ULONG           ulFlags,
    IN  HMACHINE        hMachine
    )

/*++

Routine Description:


Parameters:

   dnDevInst   Handle of a device instance.  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;
    PVOID       hStringTable = NULL;
    handle_t    hBinding = NULL;
    ULONG       ulLen = MAX_DEVICE_ID_LEN;
    WCHAR       DeviceID[MAX_DEVICE_ID_LEN];
    BOOL        Success;

    try {
        //
        // validate parameters
        //
        if (ulNameLength == 0) {
            pszVetoName = NULL;
        }

        if (pszVetoName != NULL) {
            *pszVetoName = L'\0';
        }

        if (pVetoType != NULL) {
            *pVetoType = PNP_VetoTypeUnknown;
        }

        if (dnDevInst == 0) {
            Status = CR_INVALID_DEVINST;
            goto Clean0;
        }

        //
        // no flags are currently valid
        //
        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
        //
        Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,DeviceID,&ulLen);
        if (Success == FALSE || INVALID_DEVINST(DeviceID)) {
            Status = CR_INVALID_DEVINST;
            goto Clean0;
        }

        RpcTryExcept {
            //
            // call rpc service entry point
            //
            Status = PNP_RequestDeviceEject(
                hBinding,                   // rpc binding handle
                DeviceID,                   // device instance subtree to remove
                pVetoType,
                pszVetoName,
                ulNameLength,
                ulFlags);
        }
        RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
            KdPrintEx((DPFLTR_PNPMGR_ID,
                       DBGF_ERRORS,
                       "PNP_RequestDeviceEject caused an exception (%d)\n",
                       RpcExceptionCode()));

            Status = MapRpcExceptionToCR(RpcExceptionCode());
        }
        RpcEndExcept

    Clean0:
        NOTHING;

    } except(EXCEPTION_EXECUTE_HANDLER) {
        Status = CR_FAILURE;
    }

    return Status;

} // CM_Request_Device_Eject_ExW



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;
    WCHAR       DeviceID[MAX_DEVICE_ID_LEN];
    PVOID       hStringTable = NULL;
    handle_t    hBinding = NULL;
    ULONG       ulLen = MAX_DEVICE_ID_LEN;
    BOOL        Success;

    try {
        //
        // validate parameters
        //
        if (dnDevInst == 0) {
            Status = CR_INVALID_DEVINST;
            goto Clean0;
        }

        if (!ARGUMENT_PRESENT(pszID)) {
            Status = CR_INVALID_POINTER;
            goto Clean0;
        }

        if (INVALID_FLAGS(ulFlags, CM_ADD_ID_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
        //
        Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,DeviceID,&ulLen);
        if (Success == FALSE || INVALID_DEVINST(DeviceID)) {
            Status = CR_INVALID_DEVINST;
            goto Clean0;
        }

        RpcTryExcept {
            //
            // call rpc service entry point
            //
            Status = PNP_AddID(
                hBinding,                  // rpc binding handle
                DeviceID,                 // device instance
                pszID,                     // id to add
                ulFlags);                  // hardware or compatible
        }
        RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
            KdPrintEx((DPFLTR_PNPMGR_ID,
                       DBGF_ERRORS,
                       "PNP_AddID caused an exception (%d)\n",
                       RpcExceptionCode()));

            Status = MapRpcExceptionToCR(RpcExceptionCode());
        }
        RpcEndExcept

    Clean0:
        NOTHING;

    } except(EXCEPTION_EXECUTE_HANDLER) {
        Status = CR_FAILURE;
    }

    return Status;

} // CM_Add_ID_ExW



CMAPI
CONFIGRET
CM_Register_Device_Driver_Ex(
    IN DEVINST  dnDevInst,
    IN ULONG    ulFlags,
    IN HMACHINE hMachine
    )

/*++

Routine Description:


   This routine registers the device driver for the specified device.

Parameters:

   dnDevInst   Handle of a device instance.  This handle is typically
               retrieved by a call to CM_Locate_DevNode or CM_Create_DevNode.

   ulFlags     Supplies flags for register the driver.  May be one of the
               following values:

                CM_REGISTER_DEVICE_DRIVER_STATIC
                CM_REGISTER_DEVICE_DRIVER_DISABLEABLE
                CM_REGISTER_DEVICE_DRIVER_REMOVABLE

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;
    WCHAR       DeviceID [MAX_DEVICE_ID_LEN];
    PVOID       hStringTable = NULL;
    handle_t    hBinding = NULL;
    ULONG       ulLen = MAX_DEVICE_ID_LEN;
    BOOL        Success;

    try {
        //
        // validate parameters
        //
        if (dnDevInst == 0) {
            Status = CR_INVALID_DEVINST;
            goto Clean0;
        }

        if (INVALID_FLAGS(ulFlags, CM_REGISTER_DEVICE_DRIVER_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
        //
        Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,DeviceID,&ulLen);
        if (Success == FALSE || INVALID_DEVINST(DeviceID)) {
            Status = CR_INVALID_DEVINST;
            goto Clean0;
        }

        RpcTryExcept {
            //
            // call rpc service entry point
            //
            Status = PNP_RegisterDriver(
                hBinding,                  // rpc binding handle
                DeviceID,                  // device instance
                ulFlags);                  // hardware or compatible
        }
        RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
            KdPrintEx((DPFLTR_PNPMGR_ID,
                       DBGF_ERRORS,
                       "PNP_RegisterDriver caused an exception (%d)\n",
                       RpcExceptionCode()));

            Status = MapRpcExceptionToCR(RpcExceptionCode());
        }
        RpcEndExcept

    Clean0:
        NOTHING;

    } except(EXCEPTION_EXECUTE_HANDLER) {
        Status = CR_FAILURE;
    }

    return Status;

} // CM_Register_Device_Driver_Ex



//-------------------------------------------------------------------
// 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_Set_DevNode_Problem(
    IN DEVINST   dnDevInst,
    IN ULONG     ulProblem,
    IN  ULONG    ulFlags
    )
{
    return CM_Set_DevNode_Problem_Ex(dnDevInst, ulProblem, ulFlags, NULL);
}


CONFIGRET
CM_Reenumerate_DevNode(
    IN DEVINST dnDevInst,
    IN ULONG   ulFlags
    )
{
    return CM_Reenumerate_DevNode_Ex(dnDevInst, ulFlags, NULL);
}


CONFIGRET
CM_Query_And_Remove_SubTree(
    IN  DEVINST         dnAncestor,
    OUT PPNP_VETO_TYPE  pVetoType,
    OUT LPWSTR          pszVetoName,
    IN  ULONG                   ulNameLength,
    IN  ULONG           ulFlags
    )
{
    return CM_Query_And_Remove_SubTree_Ex( dnAncestor,
                                           pVetoType,
                                           pszVetoName,
                                           ulNameLength,
                                           ulFlags,
                                           NULL);
}


CONFIGRET
CM_Query_And_Remove_SubTreeA(
    IN  DEVINST         dnAncestor,
    OUT PPNP_VETO_TYPE  pVetoType,
    OUT LPSTR           pszVetoName,
    IN  ULONG           ulNameLength,
    IN  ULONG           ulFlags
    )
{
    return CM_Query_And_Remove_SubTree_ExA( dnAncestor,
                                            pVetoType,
                                            pszVetoName,
                                            ulNameLength,
                                            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);
}


CMAPI
CONFIGRET
CM_Register_Device_Driver(
    IN DEVINST  dnDevInst,
    IN ULONG    ulFlags
    )
{
    return CM_Register_Device_Driver_Ex(dnDevInst, ulFlags, NULL);
}


CONFIGRET
CM_Query_Remove_SubTree(
    IN DEVINST dnAncestor,
    IN ULONG   ulFlags
    )
{
    return CM_Query_Remove_SubTree_Ex(dnAncestor, ulFlags, NULL);
}


CONFIGRET
CM_Request_Device_EjectW(
    IN  DEVINST         dnDevInst,
    OUT PPNP_VETO_TYPE  pVetoType,
    OUT LPWSTR          pszVetoName,
    IN  ULONG           ulNameLength,
    IN  ULONG           ulFlags
    )
{
    return CM_Request_Device_Eject_ExW(dnDevInst,
                                       pVetoType,
                                       pszVetoName,
                                       ulNameLength,
                                       ulFlags,
                                       NULL);
}


CONFIGRET
CM_Request_Device_EjectA(
    IN  DEVNODE         dnDevInst,
    OUT PPNP_VETO_TYPE  pVetoType,
    OUT LPSTR           pszVetoName,
    IN  ULONG           ulNameLength,
    IN  ULONG           ulFlags
    )
{
    return CM_Request_Device_Eject_ExA( dnDevInst,
                                        pVetoType,
                                        pszVetoName,
                                        ulNameLength,
                                        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 (pSetupCaptureAndConvertAnsiArg(pszID, &pUniID) == NO_ERROR) {

        Status = CM_Add_ID_ExW(dnDevInst,
                               pUniID,
                               ulFlags,
                               hMachine);
        pSetupFree(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 (pSetupCaptureAndConvertAnsiArg(pDeviceID, &pUniDeviceID) == NO_ERROR) {

        Status = CM_Create_DevNode_ExW(pdnDevInst,
                                       pUniDeviceID,
                                       dnParent,
                                       ulFlags,
                                       hMachine);
        pSetupFree(pUniDeviceID);

    } else {
        Status = CR_INVALID_DEVICE_ID;
    }

    return Status;

} // CM_Create_DevNode_ExA



CONFIGRET
CM_Query_And_Remove_SubTree_ExA(
    IN  DEVINST         dnAncestor,
    OUT PPNP_VETO_TYPE  pVetoType,
    OUT LPSTR           pszVetoName,
    IN  ULONG           ulNameLength,
    IN  ULONG           ulFlags,
    IN  HMACHINE        hMachine
    )
{
    CONFIGRET Status = CR_SUCCESS;
    PWSTR     pUniVetoName = NULL;
    ULONG     ulAnsiBufferLen;

    //
    // validate essential parameters only
    //
    if ((!ARGUMENT_PRESENT(pszVetoName)) && (ulNameLength != 0)) {
        return CR_INVALID_POINTER;
    }

    if (ulNameLength != 0) {
        //
        // pass a Unicode buffer instead and convert back to caller's
        // ANSI buffer on return
        //
        pUniVetoName = pSetupMalloc(MAX_VETO_NAME_LENGTH*sizeof(WCHAR));
        if (pUniVetoName == NULL) {
            return CR_OUT_OF_MEMORY;
        }
    }

    //
    // call the wide version
    //
    Status = CM_Query_And_Remove_SubTree_ExW(dnAncestor,
                                             pVetoType,
                                             pUniVetoName,
                                             MAX_VETO_NAME_LENGTH,
                                             ulFlags,
                                             hMachine);

    //
    // We should never return a veto name longer than MAX_VETO_NAME_LENGTH.
    //
    ASSERT(Status != CR_BUFFER_SMALL);

    if (Status == CR_REMOVE_VETOED) {
        //
        // convert the unicode buffer to an ANSI string and copy to the caller's
        // buffer
        //
        ulAnsiBufferLen = ulNameLength;
        Status = PnPUnicodeToMultiByte(pUniVetoName,
                                       (lstrlenW(pUniVetoName)+1)*sizeof(WCHAR),
                                       pszVetoName,
                                       &ulAnsiBufferLen);
    }

    if (pUniVetoName != NULL) {
        pSetupFree(pUniVetoName);
    }

    return Status;

} // CM_Query_And_Remove_SubTree_ExA



CONFIGRET
CM_Request_Device_Eject_ExA(
    IN  DEVNODE         dnDevInst,
    OUT PPNP_VETO_TYPE  pVetoType,
    OUT LPSTR           pszVetoName,
    IN  ULONG           ulNameLength,
    IN  ULONG           ulFlags,
    IN  HMACHINE        hMachine
    )
{
    CONFIGRET Status = CR_SUCCESS;
    PWSTR     pUniVetoName = NULL;
    ULONG     ulAnsiBufferLen;

    //
    // validate essential parameters only
    //
    if ((!ARGUMENT_PRESENT(pszVetoName)) && (ulNameLength != 0)) {
        return CR_INVALID_POINTER;
    }

    if (ulNameLength != 0) {
        //
        // pass a Unicode buffer instead and convert back to caller's
        // ANSI buffer on return
        //
        pUniVetoName = pSetupMalloc(MAX_VETO_NAME_LENGTH*sizeof(WCHAR));
        if (pUniVetoName == NULL) {
            return CR_OUT_OF_MEMORY;
        }
    }

    //
    // call the wide version
    //
    Status = CM_Request_Device_Eject_ExW(dnDevInst,
                                         pVetoType,
                                         pUniVetoName,
                                         MAX_VETO_NAME_LENGTH,
                                         ulFlags,
                                         hMachine);

    //
    // We should never return a veto name longer than MAX_VETO_NAME_LENGTH.
    //
    ASSERT(Status != CR_BUFFER_SMALL);

    if (Status == CR_REMOVE_VETOED) {
        //
        // convert the unicode buffer to an ANSI string and copy to the caller's
        // buffer
        //
        ulAnsiBufferLen = ulNameLength;
        Status = PnPUnicodeToMultiByte(pUniVetoName,
                                       (lstrlenW(pUniVetoName)+1)*sizeof(WCHAR),
                                       pszVetoName,
                                       &ulAnsiBufferLen);
    }

    if (pUniVetoName != NULL) {
        pSetupFree(pUniVetoName);
    }

    return Status;

} // CM_Request_Device_Eject_ExA



