2010-09-15 74 views
12

在我的应用程序(Delphi)中,我需要列出所有USB存储设备。这些可以是闪存棒外部存储驱动器。德尔福 - 如何获得USB移动硬盘和记忆棒列表?

有一个JvclJvDriveCombo成分,并且它具有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; 

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

回答

12
{$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; 
+0

工程!感谢您的麻烦! – 2010-09-15 15:13:25

+1

完美,但是“{$ MINENUMSIZE 4}”真的有必要吗? – Peter 2011-09-21 05:38:08

+1

@Peter好吧,'BusType'字段应占用4个字节。通常Delphi只会分配尽可能多的字节来存储枚举的任何值(在本例中为1个字节),除非您使用'$ MINENUMSIZE'指令指定最小枚举大小。您也可以将'BusType'声明为'DWORD'并将其转换为'STORAGE_BUS_TYPE'。 – 2011-09-21 11:46:07

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; 
+0

如果是外置USB硬盘,根据Windows是固定驱动器,则不起作用。所以现在你看到我发布我原来的问题的原因:) – 2010-09-15 14:33:50

4

您可以访问使用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,您可以找到更多相关的代码。

问候。

+0

这是一个很好的解决方案,因为它允许捕获像cmd对应物这样的整个属性。 – user2858981 2017-08-18 13:07:08