A way to find out if two partitions are on the same physical hard drive without WMI?

I have these sections (on Windows), for example:

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

Is there any way in the programming language to know if, for example, partition C and partition D are on the same physical hard drive without WMI ?

I do not want to use WMI because it is slow - it took 0.5 seconds for this example. I need it to be fast.

Thanks.

+4
source share
2 answers

I don’t know any other controlled way to get disk partition information. You can use the Win32 API using P / Invoke from C #. However, you should not, if absolutely necessary.

The Win32 function you need is called DeviceIoControl (). API documentation can be found at http://msdn.microsoft.com/en-us/library/aa363216(VS.85).aspx . Call DeviceIoControl () with the control code IOCTL_STORAGE_GET_DEVICE_NUMBER, and you will get a physical disk for this partition descriptor. The device handle for the partition can be restored using the CreateFile () API.

However, using DeviceIoControl () is cumbersome, and you will most likely have to create different versions for 32-bit and 64-bit versions of Windows.

To get all the partitions, you can use the System.IO.DriveInfo managed code as follows:

 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 } 

Pinvoke.net seems to have signatures for C #.

+10
source

This Delphi code snippet should be easily converted to C # using P / Invoke calls and does exactly what you want. (and a little more). Import and call - 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 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 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 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 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 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; 
+2
source

Source: https://habr.com/ru/post/1304089/


All Articles