Way to know if two partitions are in one physical hard disk without WMI?

后端 未结 2 1962
南笙
南笙 2021-01-13 18:02

I have those partitions (in Windows) for example:

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

Is there any way in a pr

2条回答
  •  灰色年华
    2021-01-13 18:19

    This piece of Delphi code should be easily transformed to C# using P/Invoke calls and does exactly what you want. (and a bit more) The importand call is to 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;
    

提交回复
热议问题