Search code examples
delphiusb-mass-storage

How can I detect whether a Garmin GPS device is connected in mass storage mode?


I am trying to figure out how to detect if a drive is a mass storage device. I can get the drive letters but cannot figure out how to detect what sort of device it is. I am trying to detect if a Garmin GPS receiver is connected to a PC in mass storage mode.


Solution

  • You can use the WMI for this task, exist several classes which can help you to determine that information, start with the Win32_USBHub checking for the value Mass Storage Device in the the property Description, also take a look in the Win32_PNPEntity class. If you don't have experience accesing the WMI from Delphi, try using the Wmi delphi code creator

    UPDATE

    To associate the values returned by the Win32_USBHub or Win32_USBControllerDevice WMI classes with a Disk Drive letter, you must follow the next steps

    1. Query for the Win32_USBControllerDevice class.
    2. Using the DeviceID extracted of the dependent property of each returned instance(record) check the Win32_PnPEntity class loinked to a Win32_DiskDrive using a WQL sentence like so : ASSOCIATORS OF {Win32_PnPEntity.DeviceID="DeviceID"} WHERE ResultClass = Win32_DiskDrive
    3. Now using Win32_DiskDriveToDiskPartition class you can found the link between the Disk Drive and the partition.
    4. Finally using the Win32_LogicalDiskToPartition class you can extract the Drive letter.

    Check this sample code

    {$APPTYPE CONSOLE}
    
    {$R *.res}
    
    uses
      Types,
      StrUtils,
      SysUtils,
      ActiveX,
      ComObj,
      Variants;
    
    procedure ScanUSBPnpDevices;
    const
      wbemFlagForwardOnly = $00000020;
    var
      FSWbemLocator  : OLEVariant;
      objWMIService  : OLEVariant;
    
      USBControllerDevices: OLEVariant;
      USBControllerDevice : OLEVariant;
      EnumUSBDevice       : IEnumvariant;
    
      PnPEntities         : OLEVariant;
      PnPEntity           : OLEVariant;
      EnumPnPEntity       : IEnumvariant;
    
      DiskDrives          : OLEVariant;
      DiskDrive           : OLEVariant;
      EnumDiskDrive       : IEnumvariant;
    
      DiskPartitions      : OLEVariant;
      DiskPartition       : OLEVariant;
      EnumDiskPartition   : IEnumvariant;
    
      iValue         : LongWord;
      DeviceID       : string;
      DiskDeviceID   : string;
      DiskPartID     : string;
    
      StringDynArray : TStringDynArray;
    begin;
      FSWbemLocator   := CreateOleObject('WbemScripting.SWbemLocator');
      objWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
      //This will scan all the usb device, you can filter this WQL sentece using any property of this class, to speed-up the process.
      USBControllerDevices := objWMIService.ExecQuery('SELECT Dependent FROM Win32_USBControllerDevice','WQL',wbemFlagForwardOnly);
      EnumUSBDEvice        := IUnknown(USBControllerDevices._NewEnum) as IEnumVariant;
      while EnumUSBDEvice.Next(1, USBControllerDevice, iValue) = 0 do
      begin
        StringDynArray:=SplitString(USBControllerDevice.Dependent, '=');
        DeviceID:=StringDynArray[1];
        Writeln(Format('USB Controller Device Device ID %s',[DeviceID]));
        PnPEntities     := objWMIService.ExecQuery(Format('ASSOCIATORS OF {Win32_PnPEntity.DeviceID=%s} WHERE ResultClass = Win32_DiskDrive',[DeviceID]),'WQL',wbemFlagForwardOnly);
        EnumPnPEntity   := IUnknown(PnPEntities._NewEnum) as IEnumVariant;
        while EnumPnPEntity.Next(1, PnPEntity, iValue) = 0 do
        begin
          //Escape the `\` chars in the DeviceID value because the '\' is a reserved character in WMI.
          DiskDeviceID   := PnPEntity.DeviceId;
          Writeln(Format('  Disk Drive Device ID %s',[DiskDeviceID]));
          DiskDeviceID   := StringReplace(DiskDeviceID,'\','\\',[rfReplaceAll]);;
          DiskDrives     := objWMIService.ExecQuery(Format('ASSOCIATORS OF {Win32_DiskDrive.DeviceID="%s"} WHERE AssocClass = Win32_DiskDriveToDiskPartition',[DiskDeviceID]),'WQL',wbemFlagForwardOnly);
          EnumDiskDrive  := IUnknown(DiskDrives._NewEnum) as IEnumVariant;
          while EnumDiskDrive.Next(1, DiskDrive, iValue) = 0 do
          begin
            DiskPartID:=DiskDrive.deviceID;
            Writeln(Format('    Disk Partition ID %s',[DiskPartID]));
            DiskPartitions:=objWMIService.ExecQuery(Format('ASSOCIATORS OF {Win32_DiskPartition.DeviceID="%s"} WHERE AssocClass = Win32_LogicalDiskToPartition',[DiskPartID]),'WQL',wbemFlagForwardOnly);
            EnumDiskPartition  := IUnknown(DiskPartitions._NewEnum) as IEnumVariant;
            while EnumDiskPartition.Next(1, DiskPartition, iValue) = 0 do
            begin
               Writeln(Format('      Drive Letter %s',[String(DiskPartition.DeviceID)]));
               DiskPartition:=Unassigned;
            end;
            DiskDrive:=Unassigned;
          end;
          PnPEntity:=Unassigned;
        end;
        USBControllerDevice:=Unassigned;
      end;
    end;
    
    begin
     try
        CoInitialize(nil);
        try
          ScanUSBPnpDevices;
        finally
          CoUninitialize;
        end;
     except
        on E:Exception do
            Writeln(E.Classname, ':', E.Message);
     end;
      Readln;
    end.
    

    Which will return some thing like this.

    USB Controller Device Device ID "USBSTOR\\DISK&VEN_HP&PROD_V100W&REV_1.00\\3S980
    62800DD&0"
      Disk Drive Device ID \\.\PHYSICALDRIVE1
        Disk Partition ID Disk #1, Partition #0
          Drive Letter F: