【问题标题】:Delphi - How to get list of USB removable hard drives and memory sticks?Delphi - 如何获取 USB 可移动硬盘和记忆棒的列表?
【发布时间】:2011-04-12 16:53:07
【问题描述】:

在我的应用程序 (Delphi) 中,我需要列出所有 USB 存储设备。这些可以是闪存棒外部存储驱动器。

有一个Jvcl 组件JvDriveCombo,它具有DriveType 属性-问题是如果我选择DriveType := Fixed,那么除了外部驱动器之外,它还列出了内部驱动器(C:\D:\ 等)。但是,我只想列出外部驱动器。

我相信有 DeviceIoControl 功能(我在 MSDN 上看到过),但我不知道如何使用它。

我想知道是否有人可以帮助我列出 USB 存储设备的正确方法/代码?

谢谢。

编辑:

我刚刚找到了一些示例代码并将其发布在这里:

uses .... jwawinbase, JwaWinIoctl;

procedure TForm1.Button1Click(Sender: TObject);
var
  DriveCmdStr: string;
  DriveHandle: THandle;
  ADriveLetter: string;
  hp: STORAGE_HOTPLUG_INFO;
  rlen: DWORD;
begin

  ADriveLetter := 'H';
  DriveCmdStr := Format('\\.\%s:', [ADriveLetter]);
  DriveHandle := CreateFile(PChar(DriveCmdStr), GENERIC_READ, FILE_SHARE_WRITE,
    nil, OPEN_EXISTING, 0, 0);

  if DriveHandle = INVALID_HANDLE_VALUE then
    Exit;

  DeviceIoControl(DriveHandle, IOCTL_STORAGE_GET_HOTPLUG_INFO, nil, 0, @hp,
    SizeOf(hp), @rlen, nil);

  CloseHandle(DriveHandle);

  if hp.MediaRemovable then
    showmessage('media removable');

end;

现在我只想知道如何枚举所有驱动器号。哪个功能最有效?

【问题讨论】:

    标签: delphi usb external hard-drive drives


    【解决方案1】:
    {$MINENUMSIZE 4}
    const
      IOCTL_STORAGE_QUERY_PROPERTY =  $002D1400;
    
    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;
    
    function GetBusType(Drive: AnsiChar): TStorageBusType;
    var
      H: THandle;
      Query: TStoragePropertyQuery;
      dwBytesReturned: DWORD;
      Buffer: array [0..1023] of Byte;
      sdd: TStorageDeviceDescriptor absolute Buffer;
      OldMode: UINT;
    begin
      Result := BusTypeUnknown;
    
      OldMode := SetErrorMode(SEM_FAILCRITICALERRORS);
      try
        H := CreateFile(PChar(Format('\\.\%s:', [AnsiLowerCase(Drive)])), 0, 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;
          finally
            CloseHandle(H);
          end;
        end;
      finally
        SetErrorMode(OldMode);
      end;
    end;
    
    
    procedure GetUsbDrives(List: TStrings);
    var
      DriveBits: set of 0..25;
      I: Integer;
      Drive: AnsiChar;
    begin
      List.BeginUpdate;
      try
        Cardinal(DriveBits) := GetLogicalDrives;
    
        for I := 0 to 25 do
          if I in DriveBits then
          begin
            Drive := Chr(Ord('a') + I);
            if GetBusType(Drive) = BusTypeUsb then
              List.Add(Drive);
          end;
      finally
        List.EndUpdate;
      end;
    end;
    

    【讨论】:

    • 完美,但“{$MINENUMSIZE 4}”真的有必要吗?
    • @Peter 好吧,BusType 字段应该占用 4 个字节。通常 Delphi 将只分配存储枚举值所需的字节数(在本例中为 1 个字节),除非您使用 $MINENUMSIZE 指令指定最小枚举大小。您还可以将BusType 声明为DWORD 并将其类型转换为STORAGE_BUS_TYPE
    【解决方案2】:

    您可以使用 WMI 访问此信息。如果您使用此 SQL,您可以访问有关已安装磁盘的信息。

    select * from Win32_diskdrive where size<>NULL
    

    此代码检索有关驱动器的信息。

    procedure  TForm1.DoInventario(aWSQL:string; var mmResult:TMemo);
    var
      Locator:ISWbemLocator;
      Services:ISWbemServices;
      SObject:ISWbemObject;
      ObjSet:ISWbemObjectSet;
      Enum:IEnumVariant;
      TempObj:OleVariant;
      Value:Cardinal;
      TS:TStrings;
    begin
    
      try
        Locator := CoSWbemLocator.Create();
        // Conectar con el Servicio de WMI
        Services := Locator.ConnectServer(
            STR_LOCALHOST,        {ordenador local}
            STR_CIM2_ROOT,        {root}
            STR_EMPTY, STR_EMPTY, {usuario y password -en local no son necesarios-}
            STR_EMPTY,STR_EMPTY, 0, nil);
        // Acceder a los datos
        ObjSet := Services.ExecQuery(aWSQL, 'WQL',
                    wbemFlagReturnImmediately and wbemFlagForwardOnly , nil);
        Enum :=  (ObjSet._NewEnum) as IEnumVariant;
        // Hemos encontrado algun objeto?
        while (Enum.Next(1, TempObj, Value) = S_OK) do begin
          SObject := IUnknown(TempObj) as ISWBemObject;
          // encontrado?
          if (SObject <> nil) then begin
            // Acceder a la propiedad
            SObject.Properties_;
            // Cargamos las propiedades
            TS := TStringList.Create();
            try
              TS.Add(SObject.GetObjectText_(0));
              // lo pasamos al memo
              mmResult.Lines.Text := mmResult.Lines.Text + TS.Text;
            finally
              FreeAndNil(TS);
            end;
          end;
        end;
      except
        // Recuperar excepciones
      end;
    
    end;
    

    您必须在使用中添加 ActiveX 和 WbemScripting_TLB(必须导入)。 有了这个,您可以访问磁盘的所有信息。

    要检索所有磁盘的盘符,您可以组合(检索可以使用相同的代码)对 Win32_LogicalDiskToPartitionWin32_DiskDrive 类的访问。

    select * from Win32_LogicalDiskToPartition
    select * from Win32_DiskDrive
    

    如果您搜索 WMI,您可以找到更多相关代码。

    问候。

    【讨论】:

    • 这是一个很好的解决方案,因为它允许捕获整个属性,如 cmd 对应项。
    【解决方案3】:

    我不确定您是否只是想枚举驱动器号?下面的 for 循环会执行此操作,遍历所有字母,无论该字母是否有驱动器。

    或者,如果您正在寻找一种不同的方法来查找可移动驱动器,那么下面也有一个功能。 (你的可能更好......)令人惊讶的是,在我的测试中,Windows.GetDriveType 并不认为 CD 驱动器是可移动的。正如人们所期望的那样,USB 驱动器被标记为可移动。

      Function RemovableDrive(Drive: char): Boolean;
      begin
        Result := (Windows.GetDriveType(PChar(Drive + ':\')) = Windows.Drive_Removable);
      end;
    
      procedure TForm1.Button1Click(Sender: TObject);
      var
        Drive: Char;
      begin
        for Drive := 'A' to 'Z' do
          Memo1.Lines.Add('Drive: ' + Drive + ' is ' + BoolToStr(RemovableDrive(Drive), TRUE));
      end;
    

    【讨论】:

    • 如果是外部 USB 硬盘(根据 Windows 是固定驱动器)则不起作用。所以现在你明白我发布原始问题的原因了:)
    猜你喜欢
    • 2013-01-29
    • 2011-08-28
    • 2017-02-25
    • 2018-07-05
    • 1970-01-01
    • 2013-09-14
    • 2020-08-17
    • 1970-01-01
    • 2018-10-20
    相关资源
    最近更新 更多