Delphi - 使用DeviceIoControl传递IOCTL_DISK_GET_LENGTH_INFO来获取闪存介质物理大小(非分区)

Sui*_*hX2 3 delphi pascal

好吧,这是其他几个问题的结果.看来我对这些建议做了一些错误,并且在使用建议的API获取媒体大小时,此时出现了错误.我的问题的新手我在物理磁盘级别工作,而不是在分区或文件系统的范围内.

我在做什么

我试图从第一个块到最后一个,启动记录分区空间和所有的闪存卡的大小.虽然我不需要它从卡片中转储信息,但我确实需要动态写作能力.我想能够说,尽管它的尺寸可能是多大,但是距离卡片的末端还有一个标记.有人建议我将IOCTL_DISK_GET_LENGTH_INFO传递给DeviceIoControl,起初我没有结果,但现在我终于从Windows 50收到错误了.

该项目的来源

这是主单元的pastebin代码(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.
Run Code Online (Sandbox Code Playgroud)

我在表单上放了第二个方法按钮,所以如果我愿意,我可以在应用程序中编写一组不同的代码.只有最小的错误处理和安全措施,没有必要通过源调试这一点.

媒体类型和界面

我在使用PSP作为阅读器的索尼记忆棒上尝试了这个,因为我找不到适配器在我的机器中使用二重奏.目标是一个MS,我的一半用户使用PSP为读者一半.但是这应该可以在SD卡上正常工作,这也是我工作的次要目标.我在usb存储卡读卡器和几张SD卡上试过这个.

问题

现在我已经修复了我的尝试,我得到了一个错误.50 ERROR_NOT_SUPPORTED不支持请求.

类似的应用程序

我找到了一个使用这个API的应用程序以及我正在尝试的很多相关函数.我正准备调查它的应用程序名为DriveImage,其源代码在这里 - http://sourceforge.net/projects/diskimage/

我从该应用程序中唯一注意到的是使用TFileStream并使用它来获取物理磁盘上的句柄.

Thi*_*Six 5

我看到这里可能会发生什么.

试试这个:

对于每个"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"

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

编辑:

你会讨厌这个,但它失败的真正原因是因为你对IOCTL_DISK_GET_LENGTH_INFO的定义是错误的.替换为:

((IOCTL_DISK_BASE shl 16) or (FILE_READ_ACCESS shl 14) or ($0017 shl 2) or METHOD_BUFFERED);
Run Code Online (Sandbox Code Playgroud)

原来是0x7405C而不是0x80070017