text: /*++
  
  Copyright (c) Microsoft Corporation.  All rights reserved.
  
  Module Name:
  
      traverse.c
  
  Abstract:
  
      This module contains the API routines that perform hardware tree
      traversal.
                 CM_Locate_DevNode
                 CM_Get_Parent
                 CM_Get_Child
                 CM_Get_Sibling
                 CM_Get_Device_ID_Size
                 CM_Get_Device_ID
                 CM_Enumerate_Enumerators
                 CM_Get_Device_ID_List
                 CM_Get_Device_ID_List_Size
                 CM_Get_Depth
  
  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"
  #pragma hdrstop
  #include "cfgi.h"
  
  
  
  CONFIGRET
  CM_Locate_DevNode_ExW(
      OUT PDEVINST    pdnDevInst,
      IN  DEVINSTID_W pDeviceID,       OPTIONAL
      IN  ULONG       ulFlags,
      IN  HMACHINE    hMachine
      )
  
  /*++
  
  Routine Description:
  
     This routine retrieves the handle of the device instance that
     corresponds to a specified device identifier.
  
  Parameters:
  
     pdnDevInst     Supplies the address of the variable that receives the
                    handle of a device instance.
  
     pDeviceID      Supplies the address of a null-terminated string specifying
                    a device identifier.  If this parameter is NULL, the API
                    retrieves a handle to the device instance at the root of
                    the hardware tree.
  
     ulFlags        Supplies flags specifying options for locating the device
                    instance.  May be a combination of the following values:
  
                    CM_LOCATE_DEVNODE_NORMAL  - Locate only device instances
                       that are currently alive from the ConfigMgr's point of
                       view.
                    CM_LOCATE_DEVNODE_PHANTOM - Allows a device instance handle
                       to be returned for a device instance that is not
                       currently alive, but that does exist in the registry.
                       This may be used with other CM APIs that require a
                       devnode handle, but for which there currently is none
                       for a particular device (e.g., you want to set a device
                       registry property for a device not currently present).
                       This flag does not allow you to locate phantom devnodes
                       created by using CM_Create_DevNode with the
                       CM_CREATE_DEVNODE_PHANTOM flag (such device instances
                       are only accessible by the caller who holds the devnode
                       handle returned from that API).
  
     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,
        CR_NO_SUCH_DEVNODE,
        CR_REMOTE_COMM_FAILURE,
        CR_MACHINE_UNAVAILABLE,
        CR_FAILURE.
  
  --*/
  
  {
      CONFIGRET Status = CR_SUCCESS;
      WCHAR     szFixedUpDeviceID[MAX_DEVICE_ID_LEN];
      PVOID     hStringTable = NULL;
      handle_t  hBinding = NULL;
      size_t    DeviceIDLen = 0;
  
  
      try {
          //
          // validate input parameters
          //
          if (!ARGUMENT_PRESENT(pdnDevInst)) {
              Status = CR_INVALID_POINTER;
              goto Clean0;
          }
  
          if (INVALID_FLAGS(ulFlags, CM_LOCATE_DEVNODE_BITS)) {
              Status = CR_INVALID_FLAG;
              goto Clean0;
          }
  
          //
          // CM_LOCATE_DEVNODE_NOVALIDATION is not supported on NT
          //
          if (IS_FLAG_SET(ulFlags, CM_LOCATE_DEVNODE_NOVALIDATION)) {
              Status = CR_INVALID_FLAG;
              goto Clean0;
          }
  
          //
          // initialize output parameters
          //
          *pdnDevInst = 0;
  
          //
          // setup rpc binding handle and string table handle
          //
          if (!PnPGetGlobalHandles(hMachine, &hStringTable, &hBinding)) {
              Status = CR_FAILURE;
              goto Clean0;
          }
  
          //
          // if a device instance was specified, make sure it's not too long
          //
          if (ARGUMENT_PRESENT(pDeviceID)) {
  
              if (FAILED(StringCchLength(
                             pDeviceID,
                             MAX_DEVICE_ID_LEN,
                             &DeviceIDLen))) {
                  Status = CR_INVALID_DEVICE_ID;
                  goto Clean0;
              }
  
              ASSERT(DeviceIDLen < MAX_DEVICE_ID_LEN);
          }
  
          //------------------------------------------------------------------
          // if the device instance is NULL or it's a zero-length string, then
          // retreive the root device instance
          //------------------------------------------------------------------
  
          if ((!ARGUMENT_PRESENT(pDeviceID)) || (DeviceIDLen == 0)) {
  
              //
              // No special privileges are required by the server
              //
  
              RpcTryExcept {
                  //
                  // call rpc service entry point
                  //
                  Status = PNP_GetRootDeviceInstance(
                      hBinding,              // rpc binding handle
                      szFixedUpDeviceID,     // return device instance string
                      MAX_DEVICE_ID_LEN);    // length of DeviceInstanceID
              }
              RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
                  KdPrintEx((DPFLTR_PNPMGR_ID,
                             DBGF_ERRORS,
                             "PNP_GetRootDeviceInstance caused an exception (%d)\n",
                             RpcExceptionCode()));
  
                  Status = MapRpcExceptionToCR(RpcExceptionCode());
              }
              RpcEndExcept
          }
  
          //------------------------------------------------------------------
          // if the device instance was specified, validate the string
          //------------------------------------------------------------------
  
          else {
              //
              // first see if the format of the device id string is valid, this
              // can be done on the client side
              //
              if (!IsLegalDeviceId(pDeviceID)) {
                  Status = CR_INVALID_DEVICE_ID;
                  goto Clean0;
              }
  
              //
              // Next, fix up the device ID string for consistency (uppercase, etc)
              //
              CopyFixedUpDeviceId(szFixedUpDeviceID,
                                  pDeviceID,
                                  (DWORD)DeviceIDLen);
  
              //
              // finally, validate the presense of the device ID string, this must
              // be done by the server
              //
  
              //
              // No special privileges are required by the server
              //
  
              RpcTryExcept {
                  //
                  // call rpc service entry point
                  //
                  Status = PNP_ValidateDeviceInstance(
                      hBinding,               // rpc binding handle
                      szFixedUpDeviceID,      // device id
                      ulFlags);               // locate flag
              }
              RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
                  KdPrintEx((DPFLTR_PNPMGR_ID,
                             DBGF_ERRORS,
                             "PNP_ValidateDeviceInstance caused an exception (%d)\n",
                             RpcExceptionCode()));
  
                  Status = MapRpcExceptionToCR(RpcExceptionCode());
              }
              RpcEndExcept
          }
  
          if (Status != CR_SUCCESS) {
              goto Clean0;
          }
  
  
          //------------------------------------------------------------------
          // In either case, if we're successful then we have a valid device
          // ID. Use the string table to assign a unique DevNode to this
          // device id (if it's already in the string table, it just retrieves
          // the existing unique value)
          //------------------------------------------------------------------
  
          ASSERT(*szFixedUpDeviceID && IsLegalDeviceId(szFixedUpDeviceID));
  
          *pdnDevInst = pSetupStringTableAddString(hStringTable,
                                             szFixedUpDeviceID,
                                             STRTAB_CASE_SENSITIVE);
          if (*pdnDevInst == (DEVINST)(-1)) {
              Status = CR_FAILURE;
          }
  
      Clean0:
          NOTHING;
  
      } except(EXCEPTION_EXECUTE_HANDLER) {
          Status = CR_FAILURE;
      }
  
      return Status;
  
  } // CM_Locate_DevNode_ExW
  
  
  
  CONFIGRET
  CM_Get_Parent_Ex(
      OUT PDEVINST pdnDevInst,
      IN  DEVINST  dnDevInst,
      IN  ULONG    ulFlags,
      IN  HMACHINE hMachine
      )
  
  /*++
  
  Routine Description:
  
     This routine retrieves the handle of the parent of a device instance.
  
  Parameters:
  
     pdnDevInst     Supplies the address of the variable that receives a
                    handle to the parent device instance.
  
     dnDevInst      Supplies the handle of the child device instance string.
  
     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_INVALID_FLAG,
        CR_INVALID_POINTER,
        CR_NO_SUCH_DEVNODE,
        CR_REMOTE_COMM_FAILURE,
        CR_MACHINE_UNAVAILABLE,
        CR_FAILURE.
  
  --*/
  
  {
      CONFIGRET   Status = CR_SUCCESS;
      WCHAR       szDeviceID[MAX_DEVICE_ID_LEN],
                  pDeviceID [MAX_DEVICE_ID_LEN];
      ULONG       ulSize = MAX_DEVICE_ID_LEN;
      PVOID       hStringTable = NULL;
      handle_t    hBinding = NULL;
      BOOL        Success;
  
  
      try {
          //
          // validate input parameters
          //
          if (dnDevInst == 0) {
              Status = CR_INVALID_DEVINST;
              goto Clean0;
          }
  
          if (!ARGUMENT_PRESENT(pdnDevInst)) {
              Status = CR_INVALID_POINTER;
              goto Clean0;
          }
  
          if (INVALID_FLAGS(ulFlags, 0)) {
              Status = CR_INVALID_FLAG;
              goto Clean0;
          }
  
          //
          // initialize output parameters
          //
          *pdnDevInst = 0;
  
          //
          // 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 dnDevInst
          //
          Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,pDeviceID,&ulSize);
          if (Success == FALSE  ||  INVALID_DEVINST(pDeviceID)) {
              Status = CR_INVALID_DEVINST;     // "input" devinst doesn't exist
              goto Clean0;
          }
  
          ulSize = MAX_DEVICE_ID_LEN;
  
          //
          // No special privileges are required by the server
          //
  
          RpcTryExcept {
              //
              // call rpc service entry point
              //
              Status = PNP_GetRelatedDeviceInstance(
                  hBinding,               // rpc binding handle
                  PNP_GET_PARENT_DEVICE_INSTANCE,    // requested action
                  pDeviceID,              // base device instance
                  szDeviceID,             // returns parent device instance
                  &ulSize,
                  ulFlags);               // not used
          }
          RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
              KdPrintEx((DPFLTR_PNPMGR_ID,
                         DBGF_ERRORS,
                         "PNP_GetRelatedDeviceInstance caused an exception (%d)\n",
                         RpcExceptionCode()));
  
              Status = MapRpcExceptionToCR(RpcExceptionCode());
          }
          RpcEndExcept
  
          if (Status != CR_SUCCESS) {
              goto Clean0;
          }
  
          //
          // add the returned device id to the string table so I can get a
          // devnode id for it (if it's already in the string table, the
          // existing id will be returned)
          //
          CharUpper(szDeviceID);
  
          ASSERT(*szDeviceID && IsLegalDeviceId(szDeviceID));
  
          *pdnDevInst = pSetupStringTableAddString(hStringTable,
                                             szDeviceID,
                                             STRTAB_CASE_SENSITIVE);
          if (*pdnDevInst == (DEVINST)(-1)) {
              Status = CR_FAILURE;
          }
  
  
      Clean0:
          NOTHING;
  
      } except(EXCEPTION_EXECUTE_HANDLER) {
          Status = CR_FAILURE;
      }
  
      return Status;
  
  } // CM_Get_Parent_Ex
  
  
  
  CONFIGRET
  CM_Get_Child_Ex(
      OUT PDEVINST pdnDevInst,
      IN  DEVINST  dnDevInst,
      IN  ULONG    ulFlags,
      IN  HMACHINE hMachine
      )
  
  /*++
  
  Routine Description:
  
     This routine retrieves the first child of a given device instance.
  
  Parameters:
  
     pdnDevInst     Supplies the address of the variable that receives the
                    handle of the device instance.
  
     dnDevInst      Supplies the handle of the parent device instance.
  
     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_INVALID_FLAG,
        CR_INVALID_POINTER,
        CR_NO_SUCH_DEVNODE,
        CR_REMOTE_COMM_FAILURE,
        CR_MACHINE_UNAVAILABLE,
        CR_FAILURE.
  
  --*/
  
  {
      CONFIGRET   Status = CR_SUCCESS;
      WCHAR       szDeviceID[MAX_DEVICE_ID_LEN],
                  pDeviceID [MAX_DEVICE_ID_LEN];
      ULONG       ulSize = MAX_DEVICE_ID_LEN;
      PVOID       hStringTable = NULL;
      handle_t    hBinding = NULL;
      BOOL        Success;
  
  
      try {
          //
          // validate input parameters
          //
          if (dnDevInst == 0) {
              Status = CR_INVALID_DEVINST;
              goto Clean0;
          }
  
          if (!ARGUMENT_PRESENT(pdnDevInst)) {
              Status = CR_INVALID_POINTER;
              goto Clean0;
          }
  
          if (INVALID_FLAGS(ulFlags, 0)) {
              Status = CR_INVALID_FLAG;
              goto Clean0;
          }
  
          //
          // initialize output parameters
          //
          *pdnDevInst = 0;
  
          //
          // 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 dnDevInst
          //
          Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,pDeviceID,&ulSize);
          if (Success == FALSE  ||  INVALID_DEVINST(pDeviceID)) {
              Status = CR_INVALID_DEVINST;     // "input" devinst doesn't exist
              goto Clean0;
          }
  
          ulSize = MAX_DEVICE_ID_LEN;
  
          //
          // No special privileges are required by the server
          //
  
          RpcTryExcept {
              //
              // call rpc service entry point
              //
              Status = PNP_GetRelatedDeviceInstance(
                  hBinding,               // rpc binding handle
                  PNP_GET_CHILD_DEVICE_INSTANCE,    // requested action
                  pDeviceID,              // base device instance
                  szDeviceID,             // child device instance
                  &ulSize,
                  ulFlags);               // not used
          }
          RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
              KdPrintEx((DPFLTR_PNPMGR_ID,
                         DBGF_ERRORS,
                         "PNP_GetRelatedDeviceInstance caused an exception (%d)\n",
                         RpcExceptionCode()));
  
              Status = MapRpcExceptionToCR(RpcExceptionCode());
          }
          RpcEndExcept
  
          if (Status != CR_SUCCESS) {
              goto Clean0;
          }
  
          //
          // add the returned device id to the string table so I can get a
          // devnode id for it (if it's already in the string table, the
          // existing id will be returned)
          //
          CharUpper(szDeviceID);
  
          ASSERT(*szDeviceID && IsLegalDeviceId(szDeviceID));
  
          *pdnDevInst = pSetupStringTableAddString(hStringTable,
                                             szDeviceID,
                                             STRTAB_CASE_SENSITIVE);
          if (*pdnDevInst == (DEVINST)(-1)) {
              Status = CR_FAILURE;
          }
  
      Clean0:
          NOTHING;
  
      } except(EXCEPTION_EXECUTE_HANDLER) {
          Status = CR_FAILURE;
      }
  
      return Status;
  
  } // CM_Get_Child_Ex
  
  
  
  CONFIGRET
  CM_Get_Sibling_Ex(
      OUT PDEVINST pdnDevInst,
      IN  DEVINST  dnDevInst,
      IN  ULONG    ulFlags,
      IN  HMACHINE hMachine
      )
  
  /*++
  
  Routine Description:
  
     This routine retrieves the sibling of a device instance.
  
     This API can be called in a loop to retrieve all the siblings of a
     device instance.  When the API returns CR_NO_SUCH_DEVNODE, there are no
     more siblings to enumerate.  In order to enumerate all children of a
     device instance, this loop must start with the device instance retrieved
     by calling CM_Get_Child to get the first sibling.
  
  Parameters:
  
     pdnDevInst     Supplies the address of the variable that receives a
                    handle to the sibling device  instance.
  
     dnDevInst      Supplies the handle of a device instance.
  
     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_INVALID_FLAG,
        CR_INVALID_POINTER,
        CR_NO_SUCH_DEVNODE,
        CR_REMOTE_COMM_FAILURE,
        CR_MACHINE_UNAVAILABLE,
        CR_FAILURE.
  
  --*/
  
  {
      CONFIGRET   Status = CR_SUCCESS;
      WCHAR       szDeviceID[MAX_DEVICE_ID_LEN],
                  pDeviceID [MAX_DEVICE_ID_LEN];
      ULONG       ulSize = MAX_DEVICE_ID_LEN;
      PVOID       hStringTable = NULL;
      handle_t    hBinding = NULL;
      BOOL        Success;
  
  
      try {
          //
          // validate input parameters
          //
          if (dnDevInst == 0) {
              Status = CR_INVALID_DEVINST;
              goto Clean0;
          }
  
          if (!ARGUMENT_PRESENT(pdnDevInst)) {
              Status = CR_INVALID_POINTER;
              goto Clean0;
          }
  
          if (INVALID_FLAGS(ulFlags, 0)) {
              Status = CR_INVALID_FLAG;
              goto Clean0;
          }
  
          //
          // initialize output parameters
          //
          *pdnDevInst = 0;
  
          //
          // 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 dnDevInst
          //
          Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,pDeviceID,&ulSize);
          if (Success == FALSE  ||  INVALID_DEVINST(pDeviceID)) {
              Status = CR_INVALID_DEVINST;     // "input" devinst doesn't exist
              goto Clean0;
          }
  
          ulSize = MAX_DEVICE_ID_LEN;
  
          //
          // No special privileges are required by the server
          //
  
          RpcTryExcept {
              //
              // call rpc service entry point
              //
              Status = PNP_GetRelatedDeviceInstance(
                  hBinding,               // rpc binding handle
                  PNP_GET_SIBLING_DEVICE_INSTANCE,    // requested action
                  pDeviceID,              // base device instance
                  szDeviceID,             // sibling device instance
                  &ulSize,
                  ulFlags);               // not used
          }
          RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
              KdPrintEx((DPFLTR_PNPMGR_ID,
                         DBGF_ERRORS,
                         "PNP_GetRelatedDeviceInstance caused an exception (%d)\n",
                         RpcExceptionCode()));
  
              Status = MapRpcExceptionToCR(RpcExceptionCode());
          }
          RpcEndExcept
  
          if (Status != CR_SUCCESS) {
              goto Clean0;
          }
  
          //
          // add the returned device id to the string table so I can get a
          // devnode id for it (if it's already in the string table, the
          // existing id will be returned)
          //
          CharUpper(szDeviceID);
  
          ASSERT(*szDeviceID && IsLegalDeviceId(szDeviceID));
  
          *pdnDevInst = pSetupStringTableAddString(hStringTable,
                                             szDeviceID,
                                             STRTAB_CASE_SENSITIVE);
          if (*pdnDevInst == (DEVINST)(-1)) {
              Status = CR_FAILURE;
          }
  
      Clean0:
          NOTHING;
  
      } except(EXCEPTION_EXECUTE_HANDLER) {
          Status = CR_FAILURE;
      }
  
      return Status;
  
  } // CM_Get_Sibling_Ex
  
  
  
  CONFIGRET
  CM_Get_Device_ID_Size_Ex(
      OUT PULONG   pulLen,
      IN  DEVINST  dnDevInst,
      IN  ULONG    ulFlags,
      IN  HMACHINE hMachine
      )
  
  /*++
  
  Routine Description:
  
     This routine retrieves the size of a device identifier from a
     device instance.
  
  Parameters:
  
     pulLen      Supplies the address of the variable that receives the size
                 in characters, not including the terminating NULL, of the
                 device identifier.  The API sets the variable to 0 if no
                 identifier exists.  The size is always less than or equal to
                 MAX_DEVICE_ID_LEN.
  
     dnDevInst   Supplies the handle of the device instance.
  
     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_INVALID_FLAG,
        CR_INVALID_POINTER,
        CR_REMOTE_COMM_FAILURE,
        CR_MACHINE_UNAVAILABLE,
        CR_FAILURE.
  --*/
  
  {
      CONFIGRET   Status = CR_SUCCESS;
      WCHAR       pDeviceID [MAX_DEVICE_ID_LEN];
      PVOID       hStringTable = NULL;
      BOOL        Success;
      DWORD       ulLen;
  
  
      try {
          //
          // validate parameters
          //
          if (dnDevInst == 0) {
              Status = CR_INVALID_DEVINST;
              goto Clean0;
          }
  
          if (!ARGUMENT_PRESENT(pulLen)) {
              Status = CR_INVALID_POINTER;
              goto Clean0;
          }
  
          if (INVALID_FLAGS(ulFlags, 0)) {
              Status = CR_INVALID_FLAG;
              goto Clean0;
          }
  
          //
          // setup string table handle
          //
          if (!PnPGetGlobalHandles(hMachine, &hStringTable, NULL)) {
              Status = CR_FAILURE;
              goto Clean0;
          }
  
          //
          // retrieve the string form of the device id string
          // use private ulLen, since we know this is valid
          //
          ulLen = MAX_DEVICE_ID_LEN;
          Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,pDeviceID,&ulLen);
          if (Success == FALSE  ||  INVALID_DEVINST(pDeviceID)) {
              *pulLen = 0;
              Status = CR_INVALID_DEVINST;
          }
          //
          // discount the terminating NULL char,
          // included in the size reported by pSetupStringTableStringFromIdEx
          //
          *pulLen = ulLen - 1;
  
      Clean0:
          NOTHING;
  
      } except(EXCEPTION_EXECUTE_HANDLER) {
          Status = CR_FAILURE;
      }
  
      return Status;
  
  } // CM_Get_Device_ID_Size_Ex
  
  
  
  CONFIGRET
  CM_Get_Device_ID_ExW(
      IN  DEVINST  dnDevInst,
      OUT PWCHAR   Buffer,
      IN  ULONG    BufferLen,
      IN  ULONG    ulFlags,
      IN  HMACHINE hMachine
      )
  
  /*++
  
  Routine Description:
  
     This routine retrieves the device identifier for a device instance.
  
  Parameters:
  
     dnDevNode   Supplies the handle of the device instance for which to
                 retrieve the device identifier.
  
     Buffer      Supplies the address of the buffer that receives the device
                 identifier.  If this buffer is larger than the device
                 identifier, the API appends a null-terminating character to
                 the data.  If it is smaller than the device identifier, the API
                 fills it with as much of the device identifier as will fit
                 and returns CR_BUFFER_SMALL.
  
     BufferLen   Supplies the size, in characters, of the buffer for the device
                 identifier.
  
     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_BUFFER_SMALL,
        CR_INVALID_DEVNODE,
        CR_INVALID_FLAG,
        CR_INVALID_POINTER,
        CR_REMOTE_COMM_FAILURE,
        CR_MACHINE_UNAVAILABLE,
        CR_FAILURE.
  
  --*/
  
  {
      CONFIGRET   Status = CR_SUCCESS;
      WCHAR       pDeviceID [MAX_DEVICE_ID_LEN];
      ULONG       ulLength = MAX_DEVICE_ID_LEN;
      PVOID       hStringTable = NULL;
      BOOL        Success;
      size_t      DeviceIDLen = 0;
  
  
      try {
          //
          // validate parameters
          //
          if (dnDevInst == 0) {
              Status = CR_INVALID_DEVINST;
              goto Clean0;
          }
  
          if ((!ARGUMENT_PRESENT(Buffer)) || (BufferLen == 0)) {
              Status = CR_INVALID_POINTER;
              goto Clean0;
          }
  
          if (INVALID_FLAGS(ulFlags, 0)) {
              Status = CR_INVALID_FLAG;
              goto Clean0;
          }
  
          //
          // initialize output parameters
          //
          *Buffer = L'\0';
  
          //
          // setup string table handle
          //
          if (!PnPGetGlobalHandles(hMachine, &hStringTable, NULL)) {
              Status = CR_FAILURE;
              goto Clean0;
          }
  
          //
          // retrieve the string form of the device id string
          //
          Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,pDeviceID,&ulLength);
          if (Success == FALSE || INVALID_DEVINST(pDeviceID)) {
              Status = CR_INVALID_DEVNODE;
              goto Clean0;
          }
  
          if (FAILED(StringCchLength(
                         pDeviceID,
                         MAX_DEVICE_ID_LEN,
                         &DeviceIDLen))) {
              Status = CR_FAILURE;
              goto Clean0;
          }
  
          //
          // copy as much of the the device id string as possible to the user
          // buffer.  include the NULL term char, if there is room.
          //
          CopyMemory(Buffer, pDeviceID,
                     min((DeviceIDLen+1)*sizeof(WCHAR),BufferLen*sizeof(WCHAR)));
  
          //
          // if the length of device id string (without NULL termination) is
          // longer than the supplied buffer, report CR_BUFFER_SMALL.
          // if the Buffer is large enough to hold the string, without
          // NULL-termination, we'll report success, per specification.
          //
          if (DeviceIDLen > BufferLen) {
              Status = CR_BUFFER_SMALL;
          }
  
      Clean0:
          NOTHING;
  
      } except(EXCEPTION_EXECUTE_HANDLER) {
          Status = CR_FAILURE;
      }
  
      return Status;
  
  } // CM_Get_Device_ID_ExW
  
  
  
  CONFIGRET
  CM_Enumerate_Enumerators_ExW(
      IN ULONG      ulEnumIndex,
      OUT PWCHAR    Buffer,
      IN OUT PULONG pulLength,
      IN ULONG      ulFlags,
      IN HMACHINE   hMachine
      )
  
  /*++
  
  Routine Description:
  
     This routine enumerates the enumerator subkeys under the Enum branch
     (e.g., Root, PCI, etc.).  These names should not be used to access the
     registry directly, but may be used as input to the CM_Get_Device_ID_List
     routine.  To enumerate enumerator subkey names, an application should
     initially call the CM_Enumerate_Enumerators function with the ulEnumIndex
     parameter set to zero. The application should then increment the
     ulEnumIndex parameter and call CM_Enumerate_Enumerators until there are
     no more subkeys (until the function returns CR_NO_SUCH_VALUE).
  
  Parameters:
  
     ulEnumIndex Supplies the index of the enumerator subkey name to retrieve.
  
     Buffer      Supplies the address of the character buffer that receives
                 the enumerator subkey name whose index is specified by
                 ulEnumIndex.
  
     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 is'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,
        CR_NO_SUCH_VALUE,
        CR_REGISTRY_ERROR,
        CR_REMOTE_COMM_FAILURE,
        CR_MACHINE_UNAVAILABLE,
        CR_FAILURE.
  
  --*/
  
  {
      CONFIGRET   Status = CR_SUCCESS;
      handle_t    hBinding = NULL;
  
  
      try {
          //
          // validate input parameters
          //
          if ((!ARGUMENT_PRESENT(Buffer)) ||
              (!ARGUMENT_PRESENT(pulLength))) {
              Status = CR_INVALID_POINTER;
              goto Clean0;
          }
  
          if (INVALID_FLAGS(ulFlags, 0)) {
              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_EnumerateSubKeys(
                  hBinding,               // rpc binding handle
                  PNP_ENUMERATOR_SUBKEYS, // subkeys of enum branch
                  ulEnumIndex,            // index of enumerator to enumerate
                  Buffer,                 // will contain enumerator name
                  *pulLength,             // max length of Buffer in chars
                  pulLength,              // chars copied (or chars 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
  
      Clean0:
          NOTHING;
  
      } except(EXCEPTION_EXECUTE_HANDLER) {
          Status = CR_FAILURE;
      }
  
      return Status;
  
  } // CM_Enumerate_Enumerators_ExW
  
  
  
  CONFIGRET
  CM_Get_Device_ID_List_ExW(
      IN PCWSTR   pszFilter,    OPTIONAL
      OUT PWCHAR  Buffer,
      IN ULONG    BufferLen,
      IN ULONG    ulFlags,
      IN HMACHINE hMachine
      )
  
  /*++
  
  Routine Description:
     This routine retrieve a list of all device IDs (device instance names)
     stored in the system.
  
  Parameters:
  
     pszFilter      This string filters the list of device IDs returned.  Its
                    interpretation is dependent on the ulFlags specified.   If
                    CM_GETDEVID_FILTER_ENUMERATORS is specified, then this
                    value can be either the name of an enumerator or the name
                    of an enumerator plus the device id.  If
                    CM_GETDEVID_FILTER_SERVICE is specified, then this value
                    is a service name.
  
     Buffer         Supplies the address of the character buffer that receives
                    the device ID list.  Each device ID is null-terminated, with
                    an extra NULL at the end.
  
     BufferLen      Supplies the size, in characters, of the Buffer.  This size
                    may be ascertained by calling CM_Get_Device_ID_List_Size.
  
     ulFlags        Must be either CM_GETDEVID_FILTER_ENUMERATOR or
                    CM_GETDEVID_FILTER_SERVICE.  The flags value controls how
                    the pszFilter string is used.
  
     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,
        CR_REGISTRY_ERROR,
        CR_REMOTE_COMM_FAILURE,
        CR_MACHINE_UNAVAILABLE,
        CR_FAILURE.
  
  --*/
  
  {
      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_GETIDLIST_FILTER_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_GetDeviceList(
                  hBinding,            // RPC Binding Handle
                  pszFilter,           // 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_GetDeviceList caused an exception (%d)\n",
                         RpcExceptionCode()));
  
              Status = MapRpcExceptionToCR(RpcExceptionCode());
          }
          RpcEndExcept
  
      Clean0:
          NOTHING;
  
      } except(EXCEPTION_EXECUTE_HANDLER) {
          Status = CR_FAILURE;
      }
  
      return Status;
  
  } // CM_Get_Device_ID_List_ExW
  
  
  
  CONFIGRET
  CM_Get_Device_ID_List_Size_ExW(
      OUT PULONG  pulLen,
      IN PCWSTR   pszFilter,   OPTIONAL
      IN ULONG    ulFlags,
      IN HMACHINE hMachine
      )
  
  /*++
  
  Routine Description:
  
     This routine retrieves the size, in characters, of a list of device
     identifiers.  It may be used to supply the buffer size necessary for a
     call to CM_Get_Device_ID_List.
  
  Parameters:
  
     pulLen         Supplies the address of the variable that receives the
                    size, in characters, required to store a list of all device
                    identifiers (possibly limited to those existing under the
                    pszEnumerator subkey described below).  The size reflects
                    a list of null-terminated device identifiers, with an extra
                    null at the end.  For efficiency, this number represents an
                    upper bound on the size required, and the actual list size
                    may be slightly smaller.
  
     pszFilter      This string filters the list of device IDs returned.  Its
                    interpretation is dependent on the ulFlags specified.   If
                    CM_GETDEVID_FILTER_ENUMERATORS is specified, then this
                    value can be either the name of an enumerator or the name
                    of an enumerator plus the device id.  If
                    CM_GETDEVID_FILTER_SERVICE is specified, then this value
                    is a service name.
  
     ulFlags        Must be either CM_GETDEVID_FILTER_ENUMERATOR or
                    CM_GETDEVID_FILTER_SERVICE.  The flags value controls how
                    the pszFilter string is used.
  
     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_REGISTRY_ERROR,
        CR_REMOTE_COMM_FAILURE,
        CR_MACHINE_UNAVAILABLE,
        CR_FAILURE.
  
  --*/
  
  {
      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_GETIDLIST_FILTER_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_GetDeviceListSize(
                  hBinding,       // rpc binding handle
                  pszFilter,      // Enumerator subkey, optional
                  pulLen,         // length of device list in chars
                  ulFlags);       // filter flag
          }
          RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
              KdPrintEx((DPFLTR_PNPMGR_ID,
                         DBGF_ERRORS,
                         "PNP_GetDeviceListSize caused an exception (%d)\n",
                         RpcExceptionCode()));
  
              Status = MapRpcExceptionToCR(RpcExceptionCode());
          }
          RpcEndExcept
  
      Clean0:
          NOTHING;
  
      } except(EXCEPTION_EXECUTE_HANDLER) {
          Status = CR_FAILURE;
      }
  
      return Status;
  
  } // CM_Get_Device_ID_List_SizeW
  
  
  
  CONFIGRET
  CM_Get_Depth_Ex(
      OUT PULONG   pulDepth,
      IN  DEVINST  dnDevInst,
      IN  ULONG    ulFlags,
      IN  HMACHINE hMachine
      )
  
  /*++
  
  Routine Description:
  
     This routine retrieves the depth of a device instance in the
     hardware tree.
  
  Parameters:
  
     pulDepth    Supplies the address of the variable that receives the
                 depth of the device instance.  This value is 0 to designate
                 the root of the tree, 1 to designate a child of the root,
                 and so on.
  
     dnDevNode   Supplies the handle of a 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_DEVNODE,
        CR_INVALID_FLAG, or
        CR_INVALID_POINTER.
  
  --*/
  
  {
      CONFIGRET   Status = CR_SUCCESS;
      WCHAR       pDeviceID [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(pulDepth)) {
              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,pDeviceID,&ulLen);
          if (Success == FALSE || INVALID_DEVINST(pDeviceID)) {
              Status = CR_INVALID_DEVINST;
              goto Clean0;
          }
  
          //
          // No special privileges are required by the server
          //
  
          RpcTryExcept {
              //
              // call rpc service entry point
              //
              Status = PNP_GetDepth(
                  hBinding,     // rpc binding handle
                  pDeviceID,    // device instance
                  pulDepth,     // returns the depth
                  ulFlags);     // not used
          }
          RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
              KdPrintEx((DPFLTR_PNPMGR_ID,
                         DBGF_ERRORS,
                         "PNP_GetDepth caused an exception (%d)\n",
                         RpcExceptionCode()));
  
              Status = MapRpcExceptionToCR(RpcExceptionCode());
          }
          RpcEndExcept
  
      Clean0:
          NOTHING;
  
      } except(EXCEPTION_EXECUTE_HANDLER) {
          Status = CR_FAILURE;
      }
  
      return Status;
  
  } // CM_Get_Depth
  
  
  
  //-------------------------------------------------------------------
  // Local Stubs
  //-------------------------------------------------------------------
  
  
  CONFIGRET
  CM_Locate_DevNodeW(
      OUT PDEVINST    pdnDevInst,
      IN  DEVINSTID_W pDeviceID,       OPTIONAL
      IN  ULONG       ulFlags
      )
  {
      return CM_Locate_DevNode_ExW(pdnDevInst, pDeviceID, ulFlags, NULL);
  }
  
  
  CONFIGRET
  CM_Locate_DevNodeA(
      OUT PDEVINST    pdnDevInst,
      IN  DEVINSTID_A pDeviceID,       OPTIONAL
      IN  ULONG       ulFlags
      )
  {
      return CM_Locate_DevNode_ExA(pdnDevInst, pDeviceID, ulFlags, NULL);
  }
  
  
  CONFIGRET
  CM_Get_Parent(
      OUT PDEVINST pdnDevInst,
      IN  DEVINST  dnDevInst,
      IN  ULONG    ulFlags
      )
  {
      return CM_Get_Parent_Ex(pdnDevInst, dnDevInst, ulFlags, NULL);
  }
  
  
  CONFIGRET
  CM_Get_Child(
      OUT PDEVINST pdnDevInst,
      IN  DEVINST  dnDevInst,
      IN  ULONG    ulFlags
      )
  {
      return CM_Get_Child_Ex(pdnDevInst, dnDevInst, ulFlags, NULL);
  }
  
  
  CONFIGRET
  CM_Get_Sibling(
      OUT PDEVINST pdnDevInst,
      IN  DEVINST  dnDevInst,
      IN  ULONG    ulFlags
      )
  {
      return CM_Get_Sibling_Ex(pdnDevInst, dnDevInst, ulFlags, NULL);
  }
  
  
  CONFIGRET
  CM_Get_Device_ID_Size(
      OUT PULONG  pulLen,
      IN  DEVINST dnDevInst,
      IN  ULONG   ulFlags
      )
  {
      return CM_Get_Device_ID_Size_Ex(pulLen, dnDevInst, ulFlags, NULL);
  }
  
  
  CONFIGRET
  CM_Get_Device_IDW(
      IN  DEVINST dnDevInst,
      OUT PWCHAR  Buffer,
      IN  ULONG   BufferLen,
      IN  ULONG   ulFlags
      )
  {
      return CM_Get_Device_ID_ExW(dnDevInst, Buffer, BufferLen, ulFlags, NULL);
  }
  
  
  CONFIGRET
  CM_Get_Device_IDA(
      IN  DEVINST dnDevInst,
      OUT PCHAR   Buffer,
      IN  ULONG   BufferLen,
      IN  ULONG   ulFlags
      )
  {
      return CM_Get_Device_ID_ExA(dnDevInst, Buffer, BufferLen, ulFlags, NULL);
  }
  
  
  CONFIGRET
  CM_Enumerate_EnumeratorsW(
      IN ULONG      ulEnumIndex,
      OUT PWCHAR    Buffer,
      IN OUT PULONG pulLength,
      IN ULONG      ulFlags
      )
  {
      return CM_Enumerate_Enumerators_ExW(ulEnumIndex, Buffer, pulLength,
                                          ulFlags, NULL);
  }
  
  
  CONFIGRET
  CM_Enumerate_EnumeratorsA(
      IN ULONG      ulEnumIndex,
      OUT PCHAR     Buffer,
      IN OUT PULONG pulLength,
      IN ULONG      ulFlags
      )
  {
      return CM_Enumerate_Enumerators_ExA(ulEnumIndex, Buffer, pulLength,
                                          ulFlags, NULL);
  }
  
  
  CONFIGRET
  CM_Get_Device_ID_ListW(
      IN PCWSTR  pszFilter,    OPTIONAL
      OUT PWCHAR Buffer,
      IN ULONG   BufferLen,
      IN ULONG   ulFlags
      )
  {
      return CM_Get_Device_ID_List_ExW(pszFilter, Buffer, BufferLen,
                                       ulFlags, NULL);
  }
  
  
  CONFIGRET
  CM_Get_Device_ID_ListA(
      IN PCSTR   pszFilter,    OPTIONAL
      OUT PCHAR  Buffer,
      IN ULONG   BufferLen,
      IN ULONG   ulFlags
      )
  {
      return CM_Get_Device_ID_List_ExA(pszFilter, Buffer, BufferLen,
                                       ulFlags, NULL);
  }
  
  
  CONFIGRET
  CM_Get_Device_ID_List_SizeW(
      OUT PULONG pulLen,
      IN PCWSTR  pszFilter,   OPTIONAL
      IN ULONG   ulFlags
      )
  {
      return CM_Get_Device_ID_List_Size_ExW(pulLen, pszFilter, ulFlags, NULL);
  }
  
  
  CONFIGRET
  CM_Get_Device_ID_List_SizeA(
      OUT PULONG pulLen,
      IN PCSTR   pszFilter,   OPTIONAL
      IN ULONG   ulFlags
      )
  {
      return CM_Get_Device_ID_List_Size_ExA(pulLen, pszFilter, ulFlags, NULL);
  }
  
  
  CONFIGRET
  CM_Get_Depth(
      OUT PULONG   pulDepth,
      IN  DEVINST  dnDevInst,
      IN  ULONG    ulFlags
      )
  {
      return CM_Get_Depth_Ex(pulDepth, dnDevInst, ulFlags, NULL);
  }
  
  
  
  //-------------------------------------------------------------------
  // ANSI STUBS
  //-------------------------------------------------------------------
  
  
  CONFIGRET
  CM_Locate_DevNode_ExA(
      OUT PDEVINST    pdnDevInst,
      IN  DEVINSTID_A pDeviceID,    OPTIONAL
      IN  ULONG       ulFlags,
      IN  HMACHINE    hMachine
      )
  {
      CONFIGRET Status = CR_SUCCESS;
  
  
      if (!ARGUMENT_PRESENT(pDeviceID)) {
          //
          // If the DEVINSTID parameter is NULL, then no conversion is necessary,
          // just call the wide version
          //
          Status = CM_Locate_DevNode_ExW(pdnDevInst,
                                         NULL,
                                         ulFlags,
                                         hMachine);
      } else {
          //
          // if a device id string was passed in, convert to UNICODE before
          // passing on to the wide version
          //
          PWSTR pUniDeviceID = NULL;
  
          if (pSetupCaptureAndConvertAnsiArg(pDeviceID, &pUniDeviceID) == NO_ERROR) {
  
              Status = CM_Locate_DevNode_ExW(pdnDevInst,
                                             pUniDeviceID,
                                             ulFlags,
                                             hMachine);
  
              pSetupFree(pUniDeviceID);
  
          } else {
              Status = CR_INVALID_DEVICE_ID;
          }
      }
  
      return Status;
  
  } // CM_Locate_DevNode_ExA
  
  
  
  CONFIGRET
  CM_Get_Device_ID_ExA(
      IN  DEVINST  dnDevInst,
      OUT PCHAR    Buffer,
      IN  ULONG    BufferLen,
      IN  ULONG    ulFlags,
      IN  HMACHINE hMachine
      )
  {
      CONFIGRET Status = CR_SUCCESS;
      WCHAR     UniBuffer[MAX_DEVICE_ID_LEN];
      ULONG     ulAnsiBufferLen;
      size_t    UniBufferLen = 0;
  
      //
      // validate essential parameters only
      //
      if ((!ARGUMENT_PRESENT(Buffer)) || (BufferLen == 0)) {
          return CR_INVALID_POINTER;
      }
  
      //
      // call the wide version, passing a unicode buffer as a parameter
      //
      Status = CM_Get_Device_ID_ExW(dnDevInst,
                                    UniBuffer,
                                    MAX_DEVICE_ID_LEN,
                                    ulFlags,
                                    hMachine);
  
      //
      // We should never return a DeviceId longer than MAX_DEVICE_ID_LEN.
      //
      ASSERT(Status != CR_BUFFER_SMALL);
  
      if (Status == CR_SUCCESS) {
  
          //
          // if the call succeeded, convert the device id to ansi before returning
          //
  
          if (FAILED(StringCchLength(
                         UniBuffer,
                         MAX_DEVICE_ID_LEN,
                         &UniBufferLen))) {
              return CR_FAILURE;
          }
  
          ASSERT(UniBufferLen < MAX_DEVICE_ID_LEN);
  
          ulAnsiBufferLen = BufferLen;
  
          Status =
              PnPUnicodeToMultiByte(
                  UniBuffer,
                  (DWORD)((UniBufferLen + 1)*sizeof(WCHAR)),
                  Buffer,
                  &ulAnsiBufferLen);
      }
  
      return Status;
  
  } // CM_Get_Device_ID_ExA
  
  
  
  
  CONFIGRET
  CM_Enumerate_Enumerators_ExA(
      IN ULONG      ulEnumIndex,
      OUT PCHAR     Buffer,
      IN OUT PULONG pulLength,
      IN ULONG      ulFlags,
      IN HMACHINE   hMachine
      )
  {
      CONFIGRET Status = CR_SUCCESS;
      WCHAR     UniBuffer[MAX_DEVICE_ID_LEN];
      ULONG     UniLen = MAX_DEVICE_ID_LEN;
  
      //
      // validate parameters
      //
      if ((!ARGUMENT_PRESENT(Buffer)) ||
          (!ARGUMENT_PRESENT(pulLength))) {
          return CR_INVALID_POINTER;
      }
  
      //
      // call the wide version, passing a unicode buffer as a parameter
      //
      Status = CM_Enumerate_Enumerators_ExW(ulEnumIndex,
                                            UniBuffer,
                                            &UniLen,
                                            ulFlags,
                                            hMachine);
  
      ASSERT(Status != CR_BUFFER_SMALL);
  
      if (Status == CR_SUCCESS) {
          //
          // convert the unicode buffer to an ansi string and copy to the caller's
          // buffer
          //
          Status =
              PnPUnicodeToMultiByte(
                  UniBuffer,
                  UniLen*sizeof(WCHAR),
                  Buffer,
                  pulLength);
      }
  
      return Status;
  
  } // CM_Enumerate_Enumerators_ExA
  
  
  
  CONFIGRET
  CM_Get_Device_ID_List_ExA(
        IN PCSTR    pszFilter,    OPTIONAL
        OUT PCHAR   Buffer,
        IN ULONG    BufferLen,
        IN ULONG    ulFlags,
        IN HMACHINE hMachine
        )
  {
      CONFIGRET Status = CR_SUCCESS;
      PWSTR     pUniBuffer, pUniFilter = NULL;
      ULONG     ulAnsiBufferLen;
  
      //
      // validate input parameters
      //
      if ((!ARGUMENT_PRESENT(Buffer)) || (BufferLen == 0)) {
          return CR_INVALID_POINTER;
      }
  
      if (ARGUMENT_PRESENT(pszFilter)) {
          //
          // if a filter string was passed in, convert to UNICODE before
          // passing on to the wide version
          //
          if (pSetupCaptureAndConvertAnsiArg(pszFilter, &pUniFilter) != NO_ERROR) {
              return CR_INVALID_DATA;
          }
          ASSERT(pUniFilter != NULL);
      }
  
      //
      // prepare a larger buffer to hold the unicode formatted
      // multi_sz data returned by CM_Get_Device_ID_List.
      //
      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_ID_List_ExW(pUniFilter,
                                         pUniBuffer,
                                         BufferLen,   // size in chars
                                         ulFlags,
                                         hMachine);
      if (Status == CR_SUCCESS) {
  
          //
          // if the call succeeded, must convert the multi_sz list to ansi before
          // returning
          //
  
          ulAnsiBufferLen = BufferLen;
  
          Status =
              PnPUnicodeToMultiByte(
                  pUniBuffer,
                  BufferLen*sizeof(WCHAR),
                  Buffer,
                  &ulAnsiBufferLen);
      }
  
      pSetupFree(pUniBuffer);
  
   Clean0:
  
      if (pUniFilter) {
          pSetupFree(pUniFilter);
      }
  
      return Status;
  
  } // CM_Get_Device_ID_List_ExA
  
  
  
  CONFIGRET
  CM_Get_Device_ID_List_Size_ExA(
      OUT PULONG  pulLen,
      IN PCSTR    pszFilter,   OPTIONAL
      IN ULONG    ulFlags,
      IN HMACHINE hMachine
      )
  {
      CONFIGRET Status = CR_SUCCESS;
  
  
      if (!ARGUMENT_PRESENT(pszFilter)) {
          //
          // If the filter parameter is NULL, then no conversion is necessary,
          // just call the wide version
          //
          Status = CM_Get_Device_ID_List_Size_ExW(pulLen,
                                                  NULL,
                                                  ulFlags,
                                                  hMachine);
      } else {
          //
          // if a filter string was passed in, convert to UNICODE before
          // passing on to the wide version
          //
          PWSTR pUniFilter = NULL;
  
          if (pSetupCaptureAndConvertAnsiArg(pszFilter, &pUniFilter) == NO_ERROR) {
  
              Status = CM_Get_Device_ID_List_Size_ExW(pulLen,
                                                      pUniFilter,
                                                      ulFlags,
                                                      hMachine);
              pSetupFree(pUniFilter);
  
          } else {
              Status = CR_INVALID_DATA;
          }
      }
  
      return Status;
  
  } // CM_Get_Device_ID_List_Size_ExA
  
  
  