2010-03-14 9 views
4

J'ai les partitions (sous Windows), par exemple:Façon de savoir si deux partitions sont dans un disque dur physique sans WMI?

Hard Disk 1 - Partition C, Partition D 
Hard Disk 2 - Partition E 

Est-il possible dans un langage de programme pour savoir si par exemple la partition C et la partition D sont un disque dur physique sans WMI?

Je ne veux pas utiliser WMI parce que c'est lent - pour cet exemple, il m'a fallu 0,5 secondes. J'ai besoin d'être rapide.

Merci.

+0

Offert une prime. –

+1

0,5 seconde? Est-ce quelque chose que vous allez faire énormément? Parce que, sinon, une demi-seconde n'est pas si mauvaise. Si vous êtes vraiment inquiet, vous pourriez peut-être le faire pour tous les lecteurs au démarrage de votre application et mettre en cache les informations. Cela suppose que vous ne repartitionnerez pas régulièrement tout au long de la journée, bien sûr :-) – paxdiablo

+1

Cette (vitesse) * est une contrainte plutôt étrange pour quelque chose qui ne changera probablement pas souvent deux - à quelle vitesse pouvez-vous échanger des disques durs à chaud? Je parie que ça prendra un peu plus de 0.5s - ne vous méprenez pas, je suis juste curieux de savoir pourquoi vous avez besoin d'être rapide. –

Répondre

10

Je ne connais aucun autre moyen géré pour obtenir des informations de partition de disque. Vous pouvez utiliser l'API Win32 en utilisant P/Invoke de C#. Cependant, vous ne devriez pas, sauf si c'est absolument nécessaire.

La fonction Win32 dont vous aurez besoin s'appelle DeviceIoControl(). La documentation API peut être trouvée au http://msdn.microsoft.com/en-us/library/aa363216(VS.85).aspx. Appelez DeviceIoControl() avec le code de contrôle IOCTL_STORAGE_GET_DEVICE_NUMBER et vous obtiendrez le lecteur de disque physique pour le handle de périphérique de partition donné. Le handle de périphérique de la partition peut être récupéré à l'aide de l'API CreateFile(). Cependant, en utilisant DeviceIoControl() est lourd et vous devrez probablement faire des versions différentes pour les versions 32 bits et 64 bits de Windows.

Pour récupérer toutes les partitions que vous pouvez utiliser le code managé System.IO.DriveInfo comme ceci:

var x = from di in DriveInfo.GetDrives() 
     where (di.DriveType == DriveType.Fixed) 
     select di; 

foreach (DriveInfo di in x) 
{ 
    // Call DeviceIoControl() using the partition name from di.Name and the IOCTL_STORAGE_GET_DEVICE_NUMBER control code to retrieve the physical disk 
} 

Il semble pinvoke.net a quelques signatures for C#.

2

Ce morceau de code Delphi doit être facilement transformé à C# en utilisant les appels P/Invoke et fait exactement ce que vous voulez. (et un peu plus) L'appel importand est à DeviceIOControl.

type 
    STORAGE_QUERY_TYPE = DWORD; 

const 
    PropertyStandardQuery = 0;   // Retrieves the descriptor 
    PropertyExistsQuery = 1;   // Used to test whether the descriptor is supported 
    PropertyMaskQuery  = 2;   // Used to retrieve a mask of writeable fields in the descriptor 

type 
    STORAGE_PROPERTY_ID = DWORD; 

const 
    StorageDeviceProperty = 0; 

// Query structure - additional parameters for specific queries can follow the header 
type 
    STORAGE_PROPERTY_QUERY = packed record 
     PropertyId:     STORAGE_PROPERTY_ID; 
     QueryType:     STORAGE_QUERY_TYPE; 
     AdditionalParameters:  Longword; 
    end; 

const 
    FILE_DEVICE_MASS_STORAGE  = $0000002d; 
    IOCTL_STORAGE_BASE   = FILE_DEVICE_MASS_STORAGE; 
    FILE_ANY_ACCESS    = 0; 
    METHOD_BUFFERED    = 0; 
    IOCTL_STORAGE_QUERY_PROPERTY = (IOCTL_STORAGE_BASE shl 16) or ($500 shl 2) or METHOD_BUFFERED or (FILE_ANY_ACCESS shl 14); 

type 
    STORAGE_BUS_TYPE = DWORD; 

const 
    BusTypeUnknown   = $00; 
    BusTypeScsi    = $01; 
    BusTypeAtapi    = $02; 
    BusTypeAta    = $03; 
    BusType1394    = $04; 
    BusTypeSsa    = $05; 
    BusTypeFibre    = $06; 
    BusTypeUsb    = $07; 
    BusTypeRAID    = $08; 
    BusTypeiScsi    = $09; 
    BusTypeSas    = $0A; 
    BusTypeSata    = $0B; 
    BusTypeSd    = $0C; 
    BusTypeMmc    = $0D; 
    BusTypeVirtual   = $0E; 
    BusTypeFileBackedVirtual = $0F; 
    BusTypeMax    = $10; 
    BusTypeMaxReserved  = $7F; 

type 
    STORAGE_DEVICE_DESCRIPTOR = packed record 
     // sizeof(STORAGE_DEVICE_DESCRIPTOR) 
     Version:      DWORD; 
     // Total size of the descriptor, including the space for additional data and id strings 
     Size:       DWORD; 
     // The SCSI-2 device type 
     DeviceType:     BYTE; 
     // The SCSI-2 device type modifier (if any) - this may be zero 
     DeviceTypeModifier:   BYTE; 
     // Flag indicating whether the device's media (if any) is removable. This field should be ignored for media-less devices 
     RemovableMedia:    BOOLEAN; 
     // Flag indicating whether the device can support multiple outstanding commands. 
     // The actual synchronization in this case is the responsibility of the port driver. 
     CommandQueueing:    BOOLEAN; 
     // Byte offset to the zero-terminated ascii string containing the device's vendor id string. 
     // For devices with no such ID this will be zero 
     VendorIdOffset:    DWORD; 
     // Byte offset to the zero-terminated ascii string containing the device's product id string. 
     // For devices with no such ID this will be zero 
     ProductIdOffset:    DWORD; 
     // Byte offset to the zero-terminated ascii string containing the device's product revision string. 
     // For devices with no such string this will be zero 
     ProductRevisionOffset:   DWORD; 
     // Byte offset to the zero-terminated ascii string containing the device's serial number. 
     // For devices with no serial number this will be zero 
     SerialNumberOffset:   DWORD; 
     // Contains the bus type (as defined above) of the device. It should be used to interpret the raw device 
     // properties at the end of this structure (if any) 
     BusType:      STORAGE_BUS_TYPE; 
     // The number of bytes of bus-specific data which have been appended to this descriptor 
     RawPropertiesLength:   DWORD; 
     // Place holder for the first byte of the bus specific property data 
     RawDeviceProperties:   DWORD; 
    end; 

    PSTORAGE_DEVICE_DESCRIPTOR = ^STORAGE_DEVICE_DESCRIPTOR; 

    STORAGE_DEVICE_NUMBER = packed record 
      DeviceType:     LONGWORD; // DEVICE_TYPE 
      DeviceNumber:    ULONG; 
      PartitionNumber:    ULONG; 
    end; 

    PSTORAGE_DEVICE_NUMBER = ^STORAGE_DEVICE_NUMBER; 

const 
    IOCTL_STORAGE_GET_DEVICE_NUMBER = (IOCTL_STORAGE_BASE shl 16) or ($420 shl 2) or METHOD_BUFFERED or (FILE_ANY_ACCESS shl 14); 

type 
    TDriveBusType = (
     dbtUnknown, 
     dbtScsi, 
     dbtAtapi, 
     dbtAta, 
     dbt1394, 
     dbtSsa, 
     dbtFibre, 
     dbtUsb, 
     dbtRAID, 
     dbtiScsi, 
     dbtSas, 
     dbtSata, 
     dbtSd, 
     dbtMmc, 
     dbtVirtual, 
     dbtFileBackedVirtual); 

    TDeviceType = (
     dtUnknown); // todo: implement 

    TDriveInfoResult = record 
     // 
     Drive:  string; 
     VendorID:  string; 
     ProductID: string; 
     Revision:  string; 
     Serial:  string; 
     BusType:  TDriveBusType; 
     Removable: Boolean; 
     // 
     DeviceType: TDeviceType; 
     DeviceNumber: Integer; 
     Partition: Integer; 
    end; 

const 
    BusTypes: array [ TDriveBusType ] of AnsiString = (
     'Unknown', 
     'Scsi', 
     'Atapi', 
     'Ata', 
     '1394', 
     'Ssa', 
     'Fibre', 
     'Usb', 
     'RAID', 
     'iScsi', 
     'Sas', 
     'Sata', 
     'Sd', 
     'Mmc', 
     'Virtual', 
     'FileBackedVirtual'); 

function DriveInfo(const Drive: string): TDriveInfoResult; 
var 
    H:  THandle; 
    N:  Longword; 
    Query: STORAGE_PROPERTY_QUERY; 
    Buffer: array [ 0..1023 ] of Byte; 
    Desc: PSTORAGE_DEVICE_DESCRIPTOR; 
    S, X: AnsiString; 
    i:  Integer; 
    Info: PSTORAGE_DEVICE_NUMBER; 
begin 
    // Clear out old data 
    Result.Drive  := Drive; 
    Result.VendorID := ''; 
    Result.ProductID := ''; 
    Result.Revision := ''; 
    Result.Serial := ''; 

    // Open drive for querying 
    H := CreateFile(PChar('\\.\' + Drive), 0, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0); 
    if H = INVALID_HANDLE_VALUE then Exit; 
    try 
     // Query device. 
     FillChar(Query, sizeof(Query), 0); 
     Query.PropertyId := StorageDeviceProperty; 
     Query.QueryType := PropertyStandardQuery; 
     FillChar(Buffer, sizeof(Buffer), 0); 
     if not DeviceIoControl (H, IOCTL_STORAGE_QUERY_PROPERTY, @Query, sizeof(Query), @Buffer, sizeof(Buffer), N, nil) then Exit; 

     // Sanity checks. 
     if N < sizeof(STORAGE_DEVICE_DESCRIPTOR) then Exit; 
     Desc := @Buffer; 
     if Desc^.Version < sizeof(STORAGE_DEVICE_DESCRIPTOR) then Exit; 

     // And obtain result. 
     if Desc^.VendorIdOffset  <> 0 then Result.VendorID := Trim(PAnsiChar(@Buffer[ Desc^.VendorIdOffset  ])); 
     if Desc^.ProductIdOffset  <> 0 then Result.ProductID := Trim(PAnsiChar(@Buffer[ Desc^.ProductIdOffset  ])); 
     if Desc^.ProductRevisionOffset <> 0 then Result.Revision := Trim(PAnsiChar(@Buffer[ Desc^.ProductRevisionOffset ])); 
     if Desc^.SerialNumberOffset <> 0 then begin 
      // The serial number is encoded in HEX and with each two characters encoded swapped. ER ABCD -> BADC -> '42414443' 
      S := PAnsiChar(@Buffer[ Desc^.SerialNumberOffset ]); 
      X := ''; 
      for i := 1 to Length(S) do if S[ i ] in [ '0'..'9', 'A'..'F', 'a'..'f' ] then X := X + S[ i ]; 
      S := ''; 
      SetLength(S, Length(X) div 2); 
      // i = 1,2,3,4,5,6 -> 3,1,7,5,11,9 
      for i := 1 to Length(S) do S[ i ] := AnsiChar(StrToInt('$' + Copy(X, 1 + (((i - 1) div 2) * 4) + 2 * (i and 1), 2))); 
      Result.Serial := Trim(S); 
     end; 
     if Desc^.BusType <= Longword(High(TDriveBusType)) then Result.BusType := TDriveBusType(Desc^.BusType); 
     Result.Removable := Desc^.RemovableMedia; 

     System.FillChar(Buffer, sizeof(Buffer), 0); 
     if DeviceIoControl (H, IOCTL_STORAGE_GET_DEVICE_NUMBER, nil, 0, @Buffer, sizeof(Buffer), N, nil) then begin 
      Info := @Buffer; 
      Result.DeviceType := dtUnknown; 
      Result.DeviceNumber := Integer(Info^.DeviceNumber); 
      Result.Partition := Integer(Info^.PartitionNumber); 
     end; 

    finally 
     CloseHandle(H); 
    end; 
end; 

function GetLogicalDrives(): TStringDynArray; 
var 
    Buffer: array [ 0..1023 ] of Char; 
    N, i: Integer; 
begin 
    SetLength(Result, 0); 
    N := GetLogicalDriveStrings(High(Buffer), @Buffer); 
    if N >= Length(Buffer) then raise Exception.Create('Oops'); 
    i := 0; 
    while (i <= N) and (Buffer[ i ] <> #0) do begin 
     SetLength(Result, Length(Result) + 1); 
     Result[ High(Result) ] := PChar(@(Buffer[ i ])); 
     Inc(i, Length(Result[ High(Result) ]) + 1); 
    end; 
end; 

function RemoveTrailingPathDelimiter(const Path: string): string; 
begin 
    if (Length(Path) = 0) or (Path[ Length(Path) ] <> PathDelim) then Result := Path else Result := Copy(Path, 1, Length(Path) - 1); 
end; 

procedure TForm7.Button1Click(Sender: TObject); 
var 
    Drives: TStringDynArray; 
    Drive: string; 
    Res: TDriveInfoResult; 
begin 
    Memo1.Lines.BeginUpdate(); 
    try 
     Memo1.Lines.Clear(); 
     Drives := GetLogicalDrives(); 
     for Drive in Drives do begin 
      Res := DriveInfo(RemoveTrailingPathDelimiter(Drive)); 
      Memo1.Lines.Add('DRIVE: ' + Drive); 
      Memo1.Lines.Add('VendorID = ' + Res.VendorID); 
      Memo1.Lines.Add('ProductID = ' + Res.ProductID); 
      Memo1.Lines.Add('Revision = ' + Res.Revision); 
      Memo1.Lines.Add('Serial = ' + Res.Serial); 
      Memo1.Lines.Add('BusType = ' + BusTypes[ Res.BusType ]); 
      Memo1.Lines.Add('Removable = ' + IntToStr(Ord(Res.Removable))); 
      // device type. 
      Memo1.Lines.Add('Device = ' + IntToStr(Res.DeviceNumber)); 
      Memo1.Lines.Add('Partition = ' + IntToStr(Res.Partition)); 

      Memo1.Lines.Add(''); 
     end; 
    finally 
     Memo1.Lines.EndUpdate(); 
    end; 
end; 
Questions connexes