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

后端 未结 2 1937
南笙
南笙 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;
    
    0 讨论(0)
  • 2021-01-13 18:23

    I don't know of any other managed way to get disk partition information. You may use the Win32 API using P/Invoke from C#. However, you shouldn't unless it's absolutely necessary.

    The Win32 function you'll need is called DeviceIoControl(). The 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'll get the physical disk drive for the given partition device handle. The device handle for the partition can be retrieved using CreateFile() API.

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

    To retrieve all partitions you may use the managed code System.IO.DriveInfo like this:

    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
    }
    

    It seems pinvoke.net has some signatures for C#.

    0 讨论(0)
提交回复
热议问题