I'm using the Windows API I/O Control to identify specifically which sectors on any disk are occupied by pieces of any particular given file. I have found what I need to use, and am partially done implementing it, but the rest of what I need help with is a bit out of the scope to as a question on StackOverflow.com. Therefore, can someone help me make sense of what I'm doing?
The end goal is to provide a visual display of how a single file is broken up into different sectors of a disk. The visual part is still to come, and I don't want any help with that part. I need to get this data first, before I even think about how to display it. Just for reference, here's a link to my related question on StackOverflow.com:
It has come down to using the "DeviceIoControl()" function with the "FSCTL_GET_RETRIEVAL_POINTERS" control code. Using "CreateFile()", I create a handle to the appropriate file. Then, I use that handle in "DeviceIoControl()" to fetch a structure "RETRIEVAL_POINTERS_BUFFER". This structure is supposed to contain the data I need in the record array of "Extents".
What I'm not understanding is why I'm not getting any data back. The extents array is coming back empty. Not only that, but I need to understand once I do have this data, how am I supposed to use it to identify what I'm trying to do? That is, how to use this data to identify the exact sectors being occupied by pieces of any given file.
In the end, I would like to have a list of clusters (array of a record) where each of these listed items represents a piece of that file, and info about the clusters where it's found on the disk. I'm really not educated in the NTFS file system or anything along those lines, all I'm trying to do is give the user a visual demonstration of when and how a file is fragmented on a disk.
Here's what I've put together so far:
And in my main form, I just have one "TMemo" control called "Log" and an "OnCreate" event handler on the form...
Every time I run this, the function "DeviceIoControl()" returns false, and I get a random number in the returned record structure. Can anyone see what's going wrong here? And help me understand what too look for in this data?
JD Solutions
The end goal is to provide a visual display of how a single file is broken up into different sectors of a disk. The visual part is still to come, and I don't want any help with that part. I need to get this data first, before I even think about how to display it. Just for reference, here's a link to my related question on StackOverflow.com:
It has come down to using the "DeviceIoControl()" function with the "FSCTL_GET_RETRIEVAL_POINTERS" control code. Using "CreateFile()", I create a handle to the appropriate file. Then, I use that handle in "DeviceIoControl()" to fetch a structure "RETRIEVAL_POINTERS_BUFFER". This structure is supposed to contain the data I need in the record array of "Extents".
What I'm not understanding is why I'm not getting any data back. The extents array is coming back empty. Not only that, but I need to understand once I do have this data, how am I supposed to use it to identify what I'm trying to do? That is, how to use this data to identify the exact sectors being occupied by pieces of any given file.
In the end, I would like to have a list of clusters (array of a record) where each of these listed items represents a piece of that file, and info about the clusters where it's found on the disk. I'm really not educated in the NTFS file system or anything along those lines, all I'm trying to do is give the user a visual demonstration of when and how a file is fragmented on a disk.
Here's what I've put together so far:
Code:
unit JD.DiskPosition;
interface
uses
Windows, Classes, SysUtils;
type
PStartingVcnInputBuffer = ^TStartingVcnInputBuffer;
TStartingVcnInputBuffer = record
StartingVcn: Int64;
end;
PRetrievalPointersBuffer = ^TRetrievalPointersBuffer;
TRetrievalPointersBuffer = record
ExtentCount: DWORD;
StartingVcn: Int64;
Extents: array [0..0] of record
NextVcn: Int64;
Lcn: Int64;
end;
end;
TLogEvent = procedure(Sender: TObject; const Msg: String) of object;
TDiskControl = class(TObject)
private
FOnLog: TLogEvent;
procedure DoLog(const Msg: String);
public
constructor Create;
destructor Destroy; override;
procedure DoTest(const Filename: String);
property OnLog: TLogEvent read FOnLog write FOnLog;
end;
implementation
{ TDiskControl }
constructor TDiskControl.Create;
begin
inherited Create;
end;
destructor TDiskControl.Destroy;
begin
inherited;
end;
procedure TDiskControl.DoLog(const Msg: String);
begin
if assigned(FOnLog) then
FOnLog(Self, Msg);
end;
procedure TDiskControl.DoTest(const Filename: String);
var
hDevice: THandle;
Input: PStartingVcnInputBuffer;
Output: PRetrievalPointersBuffer;
Return: DWORD;
X: Integer;
begin
DoLog('CreateFile...');
hDevice:= CreateFile(PChar(Filename), GENERIC_READ, FILE_SHARE_READ,
nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if hDevice > 0 then begin
DoLog('CreateFile Success');
try
DoLog('DeviceIoControl...');
if DeviceIoControl(hDevice, FSCTL_GET_RETRIEVAL_POINTERS, Input,
SizeOf(Input), Output, SizeOf(Output), Return, nil) then
begin
DoLog('DeviceIoControl Success');
end else begin
DoLog('DeviceIoControl Failure');
end;
DoLog('> '+IntToStr(Output.StartingVcn));
for X := 0 to Length(Output.Extents) - 1 do
DoLog('> '+IntToStr(X)+': '+IntToStr(Output.Extents[X].Lcn));
finally
CloseHandle(hDevice);
end;
end else begin
DoLog('CreateFile Failure');
end;
end;
end.
And in my main form, I just have one "TMemo" control called "Log" and an "OnCreate" event handler on the form...
Code:
unit uMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
JD.DiskPosition;
type
TForm1 = class(TForm)
Log: TMemo;
procedure FormCreate(Sender: TObject);
private
FDisk: TDiskControl;
procedure GotLog(Sender: TObject; const Msg: String);
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
Log.Align:= alClient;
Show;
BringToFront;
Application.ProcessMessages;
FDisk:= TDiskControl.Create;
try
FDisk.OnLog:= GotLog;
FDisk.DoTest('C:\SomeFile.abc');
finally
FDisk.Free;
end;
end;
procedure TForm1.GotLog(Sender: TObject; const Msg: String);
begin
Log.Lines.Append(Msg);
end;
end.
Every time I run this, the function "DeviceIoControl()" returns false, and I get a random number in the returned record structure. Can anyone see what's going wrong here? And help me understand what too look for in this data?
JD Solutions