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

匿名 (未验证) 提交于 2019-12-03 02:22:01

问题:

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.

What I am doing

I am trying to get the size of a flash card from the first block to the last, boot record partition space and all. While I don't need it to dump the information from a card, I do want dynamic writing abilities. I would like to be able to say, put a marker so far from the end of a card, despite what size it might be. It was suggested that I pass IOCTL_DISK_GET_LENGTH_INFO to DeviceIoControl and at first I had no results but now I am finally getting an error from windows 50.

Source to the project

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.

Media Type & Interface

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.

The Problem

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

A similar application

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.

回答1:

I see what might be happening here.

Try this:

For every "FILE_SHARE_READ", change it to "FILE_SHARE_WRITE Or FILE_SHARE_READ"

From msdn:

"If you want to open a volume using \.\X:, you must use FILE_SHARE_WRITE | FILE_SHARE_READ, not just FILE_SHARE_WRITE. If you omit FILE_SHARE_READ, you'll get ERROR_NOT_SUPPORTED on most volumes"

http://msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx

EDIT:

Your going to hate this, but the real reason it's failing is because your define for IOCTL_DISK_GET_LENGTH_INFO is wrong. Replace it with this:

((IOCTL_DISK_BASE shl 16) or (FILE_READ_ACCESS shl 14) or ($0017 shl 2) or METHOD_BUFFERED); 

Which turns out to be 0x7405C not 0x80070017



回答2:

The question is already answered. As an alternative you can use IOCTL_DISK_GET_DRIVE_GEOMETRY to obtain disk size. Here a working code that I used in my project:

const   IOCTL_DISK_GET_DRIVE_GEOMETRY  = $00070000;  type {$MINENUMSIZE 4}   TMediaType = (     Unknown,                // Format is unknown     F5_1Pt2_512,            // 5.25", 1.2MB,  512 bytes/sector     F3_1Pt44_512,           // 3.5",  1.44MB, 512 bytes/sector     F3_2Pt88_512,           // 3.5",  2.88MB, 512 bytes/sector     F3_20Pt8_512,           // 3.5",  20.8MB, 512 bytes/sector     F3_720_512,             // 3.5",  720KB,  512 bytes/sector     F5_360_512,             // 5.25", 360KB,  512 bytes/sector     F5_320_512,             // 5.25", 320KB,  512 bytes/sector     F5_320_1024,            // 5.25", 320KB,  1024 bytes/sector     F5_180_512,             // 5.25", 180KB,  512 bytes/sector     F5_160_512,             // 5.25", 160KB,  512 bytes/sector     RemovableMedia,         // Removable media other than floppy     FixedMedia,             // Fixed hard disk media     F3_120M_512             // 3.5", 120M Floppy   ); {$MINENUMSIZE 1}   PDiskGeometry = ^TDiskGeometry;   TDiskGeometry = packed record     Cylinders: int64;     MediaType: TMediaType;     TracksPerCylinder: DWORD;     SectorsPerTrack: DWORD;     BytesPerSector: DWORD;   end;  var   H: THandle;   BytesReturned: DWORD;   DG: TDiskGeometry;   DSize: int64;  begin   H:= CreateFile(PChar('\\.\G:'), GENERIC_READ,     FILE_SHARE_WRITE or FILE_SHARE_READ, nil,     OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);   if Handle = INVALID_HANDLE_VALUE then     raise Exception.Create('OOps!');   if not DeviceIOControl(H, IOCTL_DISK_GET_DRIVE_GEOMETRY, nil, 0,     @DG, SizeOf(TDiskGeometry), BytesReturned, nil) then       raise Exception.Create('OOps #2!');   DSize:= DG.Cylinders * DG.TracksPerCylinder;   DSize:= DSize * (DG.SectorsPerTrack * DG.BytesPerSector);   ShowMessage(IntToStr(DSize)); end; 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!