Delphi - Использование DeviceIoControl для передачи IOCTL_DISK_GET_LENGTH_INFO для получения физического размера флэш-носителя (не раздела) - PullRequest
3 голосов
/ 03 января 2011

Хорошо, это результат пары других вопросов. Похоже, что я делал что-то не так с предложениями, и в этот момент возникла ошибка при использовании предложенного API для получения размера носителя. Те, кто впервые в моей проблеме, я работаю на уровне физического диска, а не в пределах раздела или файловой системы.

Что я делаю

Я пытаюсь получить размер флэш-карты от первого блока до последнего, раздел загрузочной записи и все. В то время как мне не нужно, чтобы сбросить информацию с карты, я хочу динамические способности записи. Я хотел бы сказать: «Поместите маркер так далеко от конца карты, несмотря на его размер. Было предложено передать IOCTL_DISK_GET_LENGTH_INFO в DeviceIoControl, и сначала у меня не было результатов, но теперь я наконец получаю сообщение об ошибке из windows 50.

Источник проекта

Вот код вставки для основного блока (Delphi 2009) - http://clutchx2.pastebin.com/iMnq8kSx

Вот исходный код приложения и исполняемый файл с формой, созданной для вывода статуса происходящего - http://www.mediafire.com/?js8e6ci8zrjq0de

Вероятно, проще использовать загрузку, если только вы не ищете проблемы в коде. Я также вставлю код здесь.

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.

Я поместил в форму кнопку второго метода, чтобы я мог написать другой набор кода в приложение, если мне так хочется. Только минимальная обработка ошибок и защита - нет ничего, что не было бы необходимо для отладки этого через источник.

Тип носителя и интерфейс

Я попробовал это на Sony Memory Stick, используя PSP в качестве считывателя, потому что я не могу найти адаптер для использования дуэта в моей машине. Цель - MS, и половина моих пользователей использует PSP для читателя, наполовину нет. Однако это должно работать на SD-картах, и это также является второстепенной целью для моей работы. Я пробовал это на устройстве чтения карт памяти USB и нескольких SD-картах.

Проблема

Теперь, когда я исправил попытку, мне возвращается ошибка. 50 ERROR_NOT_SUPPORTED Запрос не поддерживается.

Аналогичное приложение

Я нашел приложение, которое использует этот API, а также множество связанных функций для того, что я пытаюсь сделать. Я собираюсь разобраться в этом, приложение называется DriveImage и его источник здесь - http://sourceforge.net/projects/diskimage/

Единственное, что я действительно заметил в этом приложении - это использование TFileStream и использование его для получения дескриптора на физическом диске.

Ответы [ 2 ]

5 голосов
/ 03 января 2011

Я вижу, что здесь может происходить.

Попробуйте это:

Для каждого «FILE_SHARE_READ» измените его на «FILE_SHARE_WRITE или FILE_SHARE_READ»

Из MSDN:

"Если вы хотите открыть том с помощью \. \ X :, вы должны использовать FILE_SHARE_WRITE | FILE_SHARE_READ, а не только FILE_SHARE_WRITE. Если вы опустите FILE_SHARE_READ, вы получите ERROR_NOT_SUPPORTED на большинстве томов" * 1009

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

EDIT:

Вы будете ненавидеть это, но настоящая причина его неудачи в том, что вы неверно определяете IOCTL_DISK_GET_LENGTH_INFO. Замените его следующим:

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

Который оказывается 0x7405C, а не 0x80070017

3 голосов
/ 03 января 2011

На вопрос уже дан ответ. В качестве альтернативы вы можете использовать IOCTL_DISK_GET_DRIVE_GEOMETRY для получения размера диска. Вот рабочий код, который я использовал в своем проекте:

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;
...