Delphi - Using DeviceIoControl passing IOCTL_DISK_GET_LENGTH_INFO to get flash media physical size (Not Partition)

Posted by SuicideClutchX2 on Stack Overflow See other posts from Stack Overflow or by SuicideClutchX2
Published on 2011-01-03T00:52:16Z Indexed on 2011/01/03 0:53 UTC
Read the original article Hit count: 359

Filed under:
|

Alright this is the result of a couple of other questions. It appears I was doing something wrong with the suggestions and at this point have come up with an error when using the suggested API to get the media size. Those new to my problem I am working at the physical disk level, not within the confines of a partition or file system.

Here is the pastebin code for the main unit (Delphi 2009) - http://clutchx2.pastebin.com/iMnq8kSx

Here is the application source and executable with a form built to output the status of whats going on - http://www.mediafire.com/?js8e6ci8zrjq0de

Its probably easier to use the download, unless your just looking for problems within the code. I will also paste the code here.

unit Main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TfrmMain = class(TForm)
    edtDrive: TEdit;
    lblDrive: TLabel;
    btnMethod1: TButton;
    btnMethod2: TButton;
    lblSpace: TLabel;
    edtSpace: TEdit;
    lblFail: TLabel;
    edtFail: TEdit;
    lblError: TLabel;
    edtError: TEdit;
    procedure btnMethod1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TDiskExtent = record
    DiskNumber: Cardinal;
    StartingOffset: Int64;
    ExtentLength: Int64;
  end;
  DISK_EXTENT = TDiskExtent;
  PDiskExtent = ^TDiskExtent;
  TVolumeDiskExtents = record
    NumberOfDiskExtents: Cardinal;
    Extents: array[0..0] of TDiskExtent;
  end;
  VOLUME_DISK_EXTENTS = TVolumeDiskExtents;
  PVolumeDiskExtents = ^TVolumeDiskExtents;

var
  frmMain: TfrmMain;

const
  FILE_DEVICE_DISK                     = $00000007;
  METHOD_BUFFERED                      = 0;
  FILE_ANY_ACCESS                      = 0;
  IOCTL_DISK_BASE                      = FILE_DEVICE_DISK;
  IOCTL_VOLUME_BASE                    = DWORD('V');
  IOCTL_DISK_GET_LENGTH_INFO = $80070017;
  IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS = ((IOCTL_VOLUME_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or (0 shl 2) or METHOD_BUFFERED);

implementation

{$R *.dfm}

function GetLD(Drive: Char): Cardinal;
var
  Buffer : String;
begin
  Buffer := Format('\\.\%s:',[Drive]);
  Result := CreateFile(PChar(Buffer),GENERIC_READ Or GENERIC_WRITE,FILE_SHARE_READ,nil,OPEN_EXISTING,0,0);
  If Result = INVALID_HANDLE_VALUE Then
    begin
    Result := CreateFile(PChar(Buffer),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,0,0);
  end;
end;

function GetPD(Drive: Byte): Cardinal;
var
  Buffer : String;
begin
  If Drive = 0 Then
    begin
    Result := INVALID_HANDLE_VALUE;
    Exit;
  end;
  Buffer := Format('\\.\PHYSICALDRIVE%d',[Drive]);
  Result := CreateFile(PChar(Buffer),GENERIC_READ Or GENERIC_WRITE,FILE_SHARE_READ,nil,OPEN_EXISTING,0,0);
  If Result = INVALID_HANDLE_VALUE Then
    begin
    Result := CreateFile(PChar(Buffer),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,0,0);
  end;
end;

function GetPhysicalDiskNumber(Drive: Char): Byte;
var
  LD : DWORD;
  DiskExtents : PVolumeDiskExtents;
  DiskExtent : TDiskExtent;
  BytesReturned : Cardinal;
begin
  Result := 0;
  LD := GetLD(Drive);
  If LD = INVALID_HANDLE_VALUE Then Exit;
  Try
    DiskExtents := AllocMem(Max_Path);
    DeviceIOControl(LD,IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,nil,0,DiskExtents,Max_Path,BytesReturned,nil);
    If DiskExtents^.NumberOfDiskExtents > 0 Then
      begin
      DiskExtent := DiskExtents^.Extents[0];
      Result := DiskExtent.DiskNumber;
    end;
  Finally
    CloseHandle(LD);
  end;
end;

procedure TfrmMain.btnMethod1Click(Sender: TObject);
var
  PD : DWORD;
  CardSize: Int64;
  BytesReturned: DWORD;
  CallSuccess: Boolean;
begin
  PD := GetPD(GetPhysicalDiskNumber(edtDrive.Text[1]));
  If PD = INVALID_HANDLE_VALUE Then
    Begin
      ShowMessage('Invalid Physical Disk Handle');
      Exit;
    End;
  CallSuccess := DeviceIoControl(PD, IOCTL_DISK_GET_LENGTH_INFO, nil, 0, @CardSize, SizeOf(CardSize), BytesReturned, nil);
  if not CallSuccess then
    begin
      edtError.Text := IntToStr(GetLastError());
      edtFail.Text := 'True';
    end
    else edtFail.Text := 'False';
  CloseHandle(PD);
end;

end.

I placed a second method button on the form so I can write a different set of code into the app if I feel like it. Only minimal error handling and safeguards are there is nothing that wasn't necessary for debugging this via source.

I tried this on a Sony Memory Stick using a PSP as the reader because I cant find the adapter for using a duo in my machine. The target is an MS and half of my users use a PSP for a reader half dont. However this should work fine on SD cards and that is a secondary target for my work as well. I tried this on a usb memory card reader and several SD cards.

Now that I have fixed my attempt I get an error returned. 50 ERROR_NOT_SUPPORTED The request is not supported.

I have found an application that uses this API as well as alot of related functions for what I am trying todo. I am getting ready to look into it the application is called DriveImage and its source is here - http://sourceforge.net/projects/diskimage/

The only thing I have really noticed from that application is there use of TFileStream and using that to get a handle on the physical disk.

© Stack Overflow or respective owner

Related posts about delphi

Related posts about pascal