2010-09-15 52 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