How do I detect that a drive is internal or connected to a USB port?
For example I now the letter of a partition from that drive. If I use GetDriveType on one connected to USB it says that it is fixed not removable.
I found some code with witch you can get informations from registry when such a device is connected to the computer. But it doesn't help me if the device is already connected when the program starts.
It is doable because there are programs (like WinSetupFromUsb) which detects them correctly.

Thank you in advance for any help.

Dani AI

Generated

already picked the right API family: Windows exposes a storage descriptor you can query and it includes a BusType field that tells you how the device is attached. is also right that drivers and enclosure controllers affect what the OS reports, so no single test is 100% reliable; use a couple of orthogonal checks for best results. (learn.microsoft.com)

A robust, practical workflow that avoids common pitfalls:

  • Map the volume/letter to physical disk(s) (volumes can span disks) using IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS.
  • For each physical disk, open the corresponding device (\.\PhysicalDriveN) and issue IOCTL_STORAGE_QUERY_PROPERTY (StorageDeviceProperty) to get a STORAGE_DEVICE_DESCRIPTOR; check the BusType value (BusTypeUsb).
  • If you start from a partition handle, IOCTL_STORAGE_GET_DEVICE_NUMBER or the volume extents call will get you the physical disk number first.
    This sequence makes the bus-type test act on the underlying disk FDO rather than a logical volume, which is more reliable. (learn.microsoft.com)

If BusType comes back as Unknown or as a non-USB bus (some bridges or card readers can mask USB), add a secondary check via WMI/SetupAPI: query Win32_DiskDrive (InterfaceType) or examine the PnP/hardware IDs (USB devices commonly show USBSTOR-generated IDs). Combining the IOCTL result with a WMI/PNP lookup catches many edge cases where a single API is misleading. (learn.microsoft.com)

Caveats and tips: some multi-slot readers, vendor USB<->SATA bridges and certain driver stacks will still misreport. Treat the result as a heuristic for decision logic (installers, mount rules, UI). If absolute certainty is required, maintain a small VID/PID whitelist for known devices or prompt the operator when tests disagree. Also consider the descriptor’s RemovableMedia flag if you care about “removable” vs “fixed” semantics. Test on representative hardware.

Recommended Answers

All 2 Replies

Oh, I already found how to do it:

type
   STORAGE_QUERY_TYPE = (PropertyStandardQuery = 0, PropertyExistsQuery, PropertyMaskQuery, PropertyQueryMaxDefined);
   TStorageQueryType = STORAGE_QUERY_TYPE;

   STORAGE_PROPERTY_ID = (StorageDeviceProperty = 0, StorageAdapterProperty);
   TStoragePropertyID = STORAGE_PROPERTY_ID;

   STORAGE_PROPERTY_QUERY = packed record
      PropertyId: STORAGE_PROPERTY_ID;
      QueryType: STORAGE_QUERY_TYPE;
      AdditionalParameters: array[0..9] of AnsiChar;
   end;
   TStoragePropertyQuery = STORAGE_PROPERTY_QUERY;

   STORAGE_BUS_TYPE = (BusTypeUnknown = 0, BusTypeScsi, BusTypeAtapi, BusTypeAta, BusType1394, BusTypeSsa, BusTypeFibre,
      BusTypeUsb, BusTypeRAID, BusTypeiScsi, BusTypeSas, BusTypeSata, BusTypeMaxReserved = $7F);
   TStorageBusType = STORAGE_BUS_TYPE;

   STORAGE_DEVICE_DESCRIPTOR = packed record
      Version: DWORD;
      Size: DWORD;
      DeviceType: Byte;
      DeviceTypeModifier: Byte;
      RemovableMedia: Boolean;
      CommandQueueing: Boolean;
      VendorIdOffset: DWORD;
      ProductIdOffset: DWORD;
      ProductRevisionOffset: DWORD;
      SerialNumberOffset: DWORD;
      BusType: STORAGE_BUS_TYPE;
      RawPropertiesLength: DWORD;
      RawDeviceProperties: array[0..0] of AnsiChar;
   end;
   TStorageDeviceDescriptor = STORAGE_DEVICE_DESCRIPTOR;

const
    IOCTL_STORAGE_QUERY_PROPERTY = $002D1400;

function IsOnUsb(Drive: AnsiChar): Boolean;
var
   H: THandle;
   Query: TStoragePropertyQuery;
   dwBytesReturned: DWORD;
   Buffer: array[0..1023] of Byte;
   sdd: TStorageDeviceDescriptor absolute Buffer;
   OldMode: UINT;
begin
   Result := False;

   OldMode := SetErrorMode(SEM_FAILCRITICALERRORS);
   try
      H := CreateFile(PChar(Format('\\.\%s:', [AnsiLowerCase(string(Drive))])), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil,
         OPEN_EXISTING, 0, 0);
      if H <> INVALID_HANDLE_VALUE then
      begin
         try
            dwBytesReturned := 0;
            FillChar(Query, SizeOf(Query), 0);
            FillChar(Buffer, SizeOf(Buffer), 0);
            sdd.Size := SizeOf(Buffer);
            Query.PropertyId := StorageDeviceProperty;
            Query.QueryType := PropertyStandardQuery;
            if DeviceIoControl(H, IOCTL_STORAGE_QUERY_PROPERTY, @Query, SizeOf(Query), @Buffer, SizeOf(Buffer), dwBytesReturned, nil) then
               Result := (sdd.BusType = BusTypeUsb){ and sdd.RemovableMedia};
         finally
            CloseHandle(H);
         end;
      end;
   finally
      SetErrorMode(OldMode);
   end;
end;

Sorry, I was very busy and I forgot to post it...

commented: Thanks for sharing. +7
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.