nsresult
sbWinDeviceHasInterface(DEVINST     aDevInst,
                        const GUID* aGUID,
                        bool*     aHasInterface)
{
  // Validate arguments.
  NS_ENSURE_ARG_POINTER(aGUID);
  NS_ENSURE_ARG_POINTER(aHasInterface);

  // Function variables.
  nsresult rv;

  // Set default result.
  *aHasInterface = PR_FALSE;

  // Get the device info set and set it up for auto-disposal.
  nsAutoString deviceInstanceID;
  rv = sbWinGetDeviceInstanceID(aDevInst, deviceInstanceID);
  NS_ENSURE_SUCCESS(rv, rv);
  HDEVINFO devInfo = SetupDiGetClassDevsW(aGUID,
                                          deviceInstanceID.get(),
                                          NULL,
                                          DIGCF_DEVICEINTERFACE);
  NS_ENSURE_TRUE(devInfo != INVALID_HANDLE_VALUE, NS_ERROR_FAILURE);
  sbAutoHDEVINFO autoDevInfo(devInfo);

  // Get the device info for the device instance.  Device does not have the
  // interface if it does not have a device info data record.
  SP_DEVINFO_DATA devInfoData;
  rv = sbWinGetDevInfoData(aDevInst, devInfo, &devInfoData);
  if (NS_FAILED(rv))
    return NS_OK;

  // Get the device interface detail.  Device does not have the interface if it
  // does not have the interface detail.
  PSP_DEVICE_INTERFACE_DETAIL_DATA devIfDetailData;
  rv = sbWinGetDevInterfaceDetail(&devIfDetailData,
                                  devInfo,
                                  &devInfoData,
                                  aGUID);
  if (NS_FAILED(rv))
    return NS_OK;
  sbAutoNSMemPtr autoDevIfDetailData(devIfDetailData);

  // Device has the interface.
  *aHasInterface = PR_TRUE;

  return NS_OK;
}
nsresult
sbWinGetDevicePath(DEVINST     aDevInst,
                   const GUID* aGUID,
                   nsAString&  aDevicePath)
{
  // Validate arguments.
  NS_ENSURE_ARG_POINTER(aGUID);

  // Function variables.
  nsresult rv;

  // Get the device info set and set it up for auto-disposal.
  nsAutoString deviceInstanceID;
  rv = sbWinGetDeviceInstanceID(aDevInst, deviceInstanceID);
  NS_ENSURE_SUCCESS(rv, rv);
  HDEVINFO devInfo = SetupDiGetClassDevsW(aGUID,
                                          deviceInstanceID.get(),
                                          NULL,
                                          DIGCF_DEVICEINTERFACE);
  NS_ENSURE_TRUE(devInfo != INVALID_HANDLE_VALUE, NS_ERROR_FAILURE);
  sbAutoHDEVINFO autoDevInfo(devInfo);

  // Get the device info for the device instance.
  SP_DEVINFO_DATA devInfoData;
  rv = sbWinGetDevInfoData(aDevInst, devInfo, &devInfoData);
  NS_ENSURE_SUCCESS(rv, rv);

  // Get the device interface detail data for the device instance.
  PSP_DEVICE_INTERFACE_DETAIL_DATA devIfDetailData;
  rv = sbWinGetDevInterfaceDetail(&devIfDetailData,
                                  devInfo,
                                  &devInfoData,
                                  aGUID);
  NS_ENSURE_SUCCESS(rv, rv);
  sbAutoNSMemPtr autoDevIfDetailData(devIfDetailData);

  // Return results.
  aDevicePath.Assign(devIfDetailData->DevicePath);

  return NS_OK;
}
nsresult
sbWinFindDevicesByStorageDevNum(STORAGE_DEVICE_NUMBER* aStorageDevNum,
                                PRBool                 aMatchPartitionNumber,
                                const GUID*            aGUID,
                                nsTArray<DEVINST>&     aDevInstList)
{
  // Validate arguments.
  NS_ENSURE_ARG_POINTER(aStorageDevNum);
  NS_ENSURE_ARG_POINTER(aGUID);

  // Function variables.
  nsresult rv;

  // Get the interface device class info and set up for auto-disposal.
  HDEVINFO devInfo =
             SetupDiGetClassDevsW(aGUID,
                                  NULL,
                                  NULL,
                                  DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
  NS_ENSURE_TRUE(devInfo != INVALID_HANDLE_VALUE, NS_ERROR_FAILURE);
  sbAutoHDEVINFO autoDevInfo(devInfo);

  // Search for device instances with a matching storage device number.
  aDevInstList.Clear();
  DWORD devIndex = 0;
  while (1) {
    // Get the next device detail data and set it up for auto-disposal.
    PSP_DEVICE_INTERFACE_DETAIL_DATA devIfDetailData;
    SP_DEVINFO_DATA                  devInfoData;
    rv = sbWinGetDevDetail(&devIfDetailData,
                           &devInfoData,
                           devInfo,
                           aGUID,
                           devIndex++);
    if (rv == NS_ERROR_NOT_AVAILABLE)
      break;
    NS_ENSURE_SUCCESS(rv, rv);
    sbAutoMemPtr<SP_DEVICE_INTERFACE_DETAIL_DATA>
      autoDevIfDetailData(devIfDetailData);

    // Get the next storage device number.
    STORAGE_DEVICE_NUMBER storageDevNum;
    rv = sbWinGetStorageDevNum(devIfDetailData->DevicePath, &storageDevNum);
    if (NS_FAILED(rv))
      continue;

    // Skip device instance if it doesn't match the target storage device
    // number.
    if (storageDevNum.DeviceType != aStorageDevNum->DeviceType)
      continue;
    if (storageDevNum.DeviceNumber != aStorageDevNum->DeviceNumber)
      continue;
    if (aMatchPartitionNumber &&
        (storageDevNum.PartitionNumber != aStorageDevNum->PartitionNumber)) {
      continue;
    }

    // Add device instance to list.
    NS_ENSURE_TRUE(aDevInstList.AppendElement(devInfoData.DevInst),
                   NS_ERROR_OUT_OF_MEMORY);
  }

  return NS_OK;
}
nsresult
sbWinGetDevInterfaceDetail(PSP_DEVICE_INTERFACE_DETAIL_DATA* aDevIfDetailData,
                           HDEVINFO                          aDevInfo,
                           SP_DEVINFO_DATA*                  aDevInfoData,
                           const GUID*                       aGUID)
{
  // Validate arguments.
  NS_ENSURE_ARG_POINTER(aDevIfDetailData);
  NS_ENSURE_ARG_POINTER(aDevInfoData);
  NS_ENSURE_ARG_POINTER(aGUID);

  // Function variables.
  BOOL success;

  // Get the device interface data for the requested device and interface GUID.
  SP_DEVICE_INTERFACE_DATA devIfData;
  ZeroMemory(&devIfData, sizeof(SP_DEVICE_INTERFACE_DATA));
  devIfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
  success = SetupDiEnumDeviceInterfaces(aDevInfo,
                                        aDevInfoData,
                                        aGUID,
                                        0,
                                        &devIfData);
  if (!success)
    return NS_ERROR_NOT_AVAILABLE;

  // Get the required size of the device interface detail data.
  DWORD size = 0;
  success = SetupDiGetDeviceInterfaceDetailW(aDevInfo,
                                             &devIfData,
                                             NULL,
                                             0,
                                             &size,
                                             NULL);
  if (!success) {
    NS_ENSURE_TRUE(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
                   NS_ERROR_FAILURE);
  }
  NS_ENSURE_TRUE(size > 0, NS_ERROR_FAILURE);

  // Allocate the device interface detail data record and set it up for
  // auto-disposal.
  PSP_DEVICE_INTERFACE_DETAIL_DATA devIfDetailData;
  devIfDetailData =
    static_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(NS_Alloc(size));
  NS_ENSURE_TRUE(devIfDetailData, NS_ERROR_OUT_OF_MEMORY);
  sbAutoNSMemPtr autoDevIfDetailData(devIfDetailData);

  // Get the device interface details.
  devIfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
  success = SetupDiGetDeviceInterfaceDetailW(aDevInfo,
                                             &devIfData,
                                             devIfDetailData,
                                             size,
                                             NULL,
                                             NULL);
  NS_ENSURE_SUCCESS(success, NS_ERROR_FAILURE);

  // Return results.
  *aDevIfDetailData =
    static_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(autoDevIfDetailData.forget());

  return NS_OK;
}
nsresult
sbWinGetDevDetail(PSP_DEVICE_INTERFACE_DETAIL_DATA* aDevIfDetailData,
                  SP_DEVINFO_DATA*                  aDevInfoData,
                  HDEVINFO                          aDevInfo,
                  const GUID*                       aGUID,
                  DWORD                             aDevIndex)
{
  // Validate arguments.
  NS_ENSURE_ARG_POINTER(aDevIfDetailData);
  NS_ENSURE_ARG_POINTER(aDevInfoData);
  NS_ENSURE_ARG_POINTER(aGUID);

  // Function variables.
  BOOL success;

  // Set up to get the device interface detail data.  If not successful, there's
  // no more device interfaces to enumerate.
  SP_DEVICE_INTERFACE_DATA devIfData;
  ZeroMemory(&devIfData, sizeof(SP_DEVICE_INTERFACE_DATA));
  devIfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
  success = SetupDiEnumDeviceInterfaces(aDevInfo,
                                        NULL,
                                        aGUID,
                                        aDevIndex,
                                        &devIfData);
  if (!success)
    return NS_ERROR_NOT_AVAILABLE;

  // Get the required size of the device interface detail data.
  DWORD size = 0;
  success = SetupDiGetDeviceInterfaceDetailW(aDevInfo,
                                             &devIfData,
                                             NULL,
                                             0,
                                             &size,
                                             NULL);
  NS_ENSURE_TRUE(size > 0, NS_ERROR_FAILURE);

  // Allocate the device interface detail data record and set it up for
  // auto-disposal.
  PSP_DEVICE_INTERFACE_DETAIL_DATA
    devIfDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(size);
  NS_ENSURE_TRUE(devIfDetailData, NS_ERROR_OUT_OF_MEMORY);
  sbAutoMemPtr<SP_DEVICE_INTERFACE_DETAIL_DATA>
    autoDevIfDetailData(devIfDetailData);
  devIfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

  // Get the device interface details.
  ZeroMemory(aDevInfoData, sizeof(SP_DEVINFO_DATA));
  aDevInfoData->cbSize = sizeof(SP_DEVINFO_DATA);
  success = SetupDiGetDeviceInterfaceDetailW(aDevInfo,
                                             &devIfData,
                                             devIfDetailData,
                                             size,
                                             NULL,
                                             aDevInfoData);
  NS_ENSURE_SUCCESS(success, NS_ERROR_FAILURE);

  // Return results.
  *aDevIfDetailData =
    (PSP_DEVICE_INTERFACE_DETAIL_DATA) autoDevIfDetailData.forget();

  return NS_OK;
}