2010-09-15 36 views
12

En mi aplicación (Delphi), necesito enumerar todos los dispositivos de almacenamiento USB. Pueden ser memorias flash o unidades de almacenamiento externo.Delphi - ¿Cómo obtener la lista de unidades de memoria extraíbles y memorias USB extraíbles?

hay un componente JvclJvDriveCombo, y tiene la propiedad DriveType - el problema es si selecciono DriveType := Fixed entonces, además de la unidad externa, que también enumera las unidades internas (C:\, D:\ etc). Sin embargo, solo quiero enumerar las unidades externas.

Creo que hay una función DeviceIoControl (la vi en MSDN) pero no tengo idea de cómo usarla.

Me pregunto si alguien me puede ayudar con la forma/código adecuado para enumerar los dispositivos de almacenamiento USB.

Gracias.

EDIT:

me acaba de encontrar algunos ejemplos de código y estoy publicando aquí:

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; 

Ahora me gustaría simplemente saber cómo enumerar todas las letras de unidad. ¿Cuál es la función más eficiente?

Respuesta

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

¡Funciona! gracias por tu problema! –

+1

perfecto, pero es "{$ MINENUMSIZE 4}" realmente necesario? – Peter

+1

@ Peter Bueno, el campo 'BusType' debe ocupar 4 bytes. Normalmente, Delphi asignaría solo tantos bytes como sea necesario para almacenar cualquier valor de la enumeración (en este caso, 1 byte), a menos que especifique el tamaño de enum mínimo con la directiva '$ MINENUMSIZE'. También podría declarar 'BusType' como' DWORD' y encasillarlo en 'STORAGE_BUS_TYPE'. –

3

No estoy seguro si solo está buscando enumerar letras de unidad? El for-loop a continuación lo hace, pasando por todas las letras, independientemente de si hay una unidad para esa letra.

O, si está buscando una forma diferente de encontrar unidades extraíbles, también hay una función a continuación. (El tuyo puede ser mejor ...) Sorprendentemente, en mi prueba, Windows.GetDriveType NO considera unidades de CD como extraíbles. Las unidades USB se marcan como extraíbles, como era de esperar.

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

No funciona si se trata de un disco duro USB externo, que según Windows es una unidad fija. Así que ahora ves la razón por la que publiqué mi pregunta original :) –

4

Se puede acceder a esta información mediante WMI. Si usa este SQL, puede acceder a la información sobre los discos instalados.

select * from Win32_diskdrive where size<>NULL 

Este código recupera información sobre las unidades.

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; 

Debe añadir ActiveX y WbemScripting_TLB (esto debe ser importado) en sus usos. Con esto puede acceder a toda la información de los discos.

retrive la carta de todos los discos se pueden combinar (recuperar puede hacer con el mismo código) el acceso a las clases Win32_LogicalDiskToPartition y Win32_DiskDrive.

select * from Win32_LogicalDiskToPartition 
select * from Win32_DiskDrive 

Si busca WMI, puede encontrar más códigos relacionados.

Atentamente.

+0

Esta es una gran solución porque permite la captura de propiedades completas como la contraparte de cmd. – user2858981

Cuestiones relacionadas