Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How to do Disk and File Access? 2

Status
Not open for further replies.

topcat01

Programmer
Jul 10, 2003
83
0
0
GB
Hi All,

I have been pulling my hair out trying to figure out where I going wrong with this... I want to read a physical disk, goto offset 'xxx' and read x number of bytes into a buffer. I then want to save the buffer as a file. My program keeps returning a 4 byte file filled with random values even when I set a file size of 1000, source below, would be grateful for help on this!

Code:
var
  buf2:array of byte;  // Dynamic array to hold file data
  hDiskfile : THandle; // Disk
  hSavefile : THandle; // File to Save
  CPConverted: LARGE_INTEGER;
  BytesWritten , intReadedAmount: Cardinal;


begin
  CPConverted.QuadPart := Offset; 	// This is the location of the data on the disk
  SetLength(buf2, FileSize);  		// Size of the file to save

  if WIN32PLATFORM = VER_PLATFORM_WIN32_NT then
  begin
    // open disk
    hDiskFile := CreateFile(pChar('\\.\PhysicalDrive0'), GENERIC_READ, 	FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 		FILE_ATTRIBUTE_NORMAL, 0);
	
    // goto location on disk and read data into buffer
    SetFilePointer(hDiskFile, Longint(CPConverted.LowPart), @CPConverted.HighPart, FILE_BEGIN);
    ReadFile(hDiskFile, buf2, sizeof(buf2), intreadedamount, nil);

    // create new file and save contents of buffer into new file
    hsavefile := CreateFile(pChar('C:\Test.txt'), GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
    WriteFile(hsavefile,buf2,sizeof(buf2),BytesWritten,nil);
    CloseHandle(hcarvefile);

    CloseHandle(hDiskFile);
  end;


end;
 
After I made a few changes (mainly relating to compiler errors), I got it to the point of where it seems to work right. The main thing involved the file size and buffer definitions at the beginning. My guess would be to look at what filesize is set to when it enters this code. Then if that doesn't work, then there is a possibility that the read file routine is not receiving the proper buffer and your data are going elsewhere. Here's what I did, it produces a 16K file. Hopefully it can be a start for you.

Code:
procedure TForm1.Button1Click(Sender: TObject);
type
  LARGE_INTEGER = record
    LowPart: DWord;
    HighPart: DWord;
  end;
var
  buf2: array[1..16834] of byte;  // Dynamic array to hold file data
  hDiskfile : THandle; // Disk
  hSavefile : THandle; // File to Save
  CPConverted: LARGE_INTEGER;
  BytesWritten , intReadedAmount: DWord;
  OffSet: DWord;

begin
  Offset := 256;
  CPConverted.LowPart := Offset;     // This is the location of the data on the disk
  {SetLength(buf2, FileSize);}          // Size of the file to save

  if WIN32PLATFORM = VER_PLATFORM_WIN32_NT then
  begin
    // open disk
    hDiskFile := CreateFile(pChar('\\.\PhysicalDrive0'), GENERIC_READ,     FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING,         FILE_ATTRIBUTE_NORMAL, 0);

    // goto location on disk and read data into buffer
    SetFilePointer(hDiskFile, Longint(CPConverted.LowPart), @CPConverted.HighPart, FILE_BEGIN);
    ReadFile(hDiskFile, buf2, sizeof(buf2), intreadedamount, nil);
    // create new file and save contents of buffer into new file
    hsavefile := CreateFile(pChar('C:\Test.txt'), GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
    WriteFile(hsavefile,buf2,sizeof(buf2),BytesWritten,nil);
    CloseHandle(hsavefile);
    CloseHandle(hDiskFile);
  end;
 
if that doesn't work, then there is a possibility that the read file routine is not receiving the proper buffer and your data are going elsewhere.

I just had a thought, this is probably the possibility - buf2 might be being treated as a 4 byte pointer instead of the value itself as you are expecting. Anyhow try what was said above and see if you get any better results than what you have now.
 
File streams are a good way to prevent unnecessary baldness! The coding is so much simpler. Here is a suitable procedure that takes 4 parameters:
Source file name
Destination file name
Offset that you want copying to start
Length of data that you want copied.
Code:
procedure TForm1.CopyPartFile(src: string; offset, length: cardinal;
  dst: string);
var
  instream: TFileStream;
  outstream: TFileStream;
begin
  instream := TFileStream.Create( src, fmOpenRead );
  outstream := TFileStream.Create( dst, fmCreate );
  try
    instream.Position := offset;
    outstream.CopyFrom( instream, length);
  finally
    outstream.Free;
    instream.Free;
  end;
end;
Streams are an important part of the Delphi programmers toolkit so if you are not familiar with them then take a look at the help for File Streams and Memory Streams. Several VCL components use streams to make the use of the components easier.

Andrew
Hampshire, UK
 
My mistake! I misread the question. My apologies.

Andrew
Hampshire, UK
 
Code:
var
  buf2:array of byte;  // Dynamic array to hold file data

...

ReadFile(hDiskFile, [b]buf2[/b], sizeof(buf2), intreadedamount, nil);

The dyn arrays and ansi strings are pointers to the control structures, not pointers to the data space.

If you use buf2 : array[1..n] of byte as Glen said, then buf2 is the address of the buffer space.

But if you use a dynamic array (as in your code) buf2 is the pointer pointing to the control structure, not the buffer space.

Using dyn arrays or you need to use buf2[0] or @buf2[0] to point to the buffer space.

SizeOf(buf2) is wrong either, as SizeOf applied to a dyn array does not returns the space size but the pointer size (4). For a byte dyn array use Length instead.

buho (A).



 
All, Thanks for your help, Glenn I think you were spot on with the 4 byte pointer issue, I've modified it as buho mentioned. Here is the code so far:

Code:
procedure TForm1.Button2Click(Sender: TObject);
var
  CPConverted: LARGE_INTEGER;
  hDiskFile, hSaveFile : thandle;
  intreadedamount,BytesWritten : cardinal;
  Sector : int64;
  buf2 : array of byte;
  GoodRead : boolean;
begin
  // Specify location
  Sector := 0;
  SetLength(buf2,512);
  CPConverted.QuadPart := (Sector * 512);  {assumes sector is 512 bytes}
  // Open and read disk
  hDiskFile := CreateFile(pChar('\\.\PhysicalDrive1'), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  SetFilePointer(hDiskFile, Longint(CPConverted.LowPart), @CPConverted.HighPart, FILE_BEGIN);
  ReadFile(hDiskFile,buf2[0], length(buf2), intreadedamount, nil);
  CloseHandle(hDiskFile);
  // save buffer to text file
  hsavefile := CreateFile(pChar('C:\Test2.txt'), GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  WriteFile(hsavefile,buf2[0],length(buf2),BytesWritten,nil);
  CloseHandle(hsavefile);
end;

Setting the sector to 0 will save the first sector of the disk to a text file.

I have been going around in circles with this... it seems to read in sectors, I have a vague recollection of having this working before (prior to posting here). I am trying to get the bytes I require into the buffer and save, for example:

Goto offset 300 (decimal), read 14 bytes, save the 14 bytes to file.

I have tried changing the length of buffer to 14 and setting CPConverted.QuadPart to 300 but this doesn't seem to work, any suggestions? It seems obvious but cannot see what is wrong. Is readfile only capable of reading sectors?
 
ReadFile is byte oriented.

You are reading in 512 bytes chunks due to the fact you are telling ReadFile to read 512 bytes :).

Try this:

Code:
SetLength(buf2, 512);
hDiskFile := CreateFile(...);
// Error check here, please.
SetFilePointer(hDiskFile, 300, Nil, FILE_BEGIN);
// Error check here, please.
ReadFile(hDiskFile, buf2[0], 8, intReadedAmount, Nil);
// Error check here, please.

The code will read 8 bytes starting at offset 300... or fail somewhere.

buho (A).


 
buho,

Thanks for the help again, I've tested this on two machines and get the same result...

Here is my new code:

Code:
procedure TForm1.Button3Click(Sender: TObject);
var
  hDiskFile, hSaveFile : thandle;
  intreadedamount,BytesWritten : cardinal;
  buf2 : array of byte;
begin
  SetLength(buf2,8);
  hDiskFile := CreateFile(pChar('\\.\PhysicalDrive1'), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  SetFilePointer(hDiskFile, 0, nil, FILE_BEGIN);
  ReadFile(hDiskFile,buf2[0], length(buf2), intreadedamount, nil);
  CloseHandle(hDiskFile);

  hsavefile := CreateFile(pChar('C:\Test2.txt'), GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
 WriteFile(hsavefile,buf2[0],length(buf2),BytesWritten,nil);
  CloseHandle(hsavefile);
end;

Using the above code I changed the following:

Offset was set to 0

SetLength(buf2,8); - result (failed, 8 null values in txt file)
SetLength(buf2,128); - result (failed, 128 null values in txt file)
SetLength(buf2,500); - result (failed, 500 null values in txt file)
SetLength(buf2,512); - result (success, reads data)
SetLength(buf2,1024); - result (success, reads data)

I then set offset to 30

SetLength(buf2,512); - result (reads data successfully but NOT beginning at offset 30, instead at offset 0)???

I am only changing two values so I know it's not typing error...

Do these functions work correctly in Delphi?
 
512 and 1024 are potential block sizes, and it may be wanting to read the entire block on disk, and is limiting you to reading specific blocks? It seems possible.

ReadFile API definition

However, you might want to call GetLastError along with possibly FormatMessage on your failed calls and tell us what the error actually is on these calls.
 
What '\\.\PhysicalDrive1' actually is? What kind of device?

Yes, CreateFile/ReadFile works well in Delphi (at least till D6).

buho (A).
 
I had the time, so I just put the code you posted into TD2006. As it turns out, I was right, as the error code that is returned out of your read is "Parameter is incorrect". Your buffer will need to be a multiple of the block size, given how you are using the CreateFile function. This is not coincidental as it will read the data in a raw fashion (block by block).

It does pay to error check. Posted to help out those that might come along
Code:
const
  INVALID_SET_FILE_POINTER: Integer = -1;

  SetLength(buf2,511);
  hDiskFile := Windows.CreateFile(pChar('\\.\PhysicalDrive0'), GENERIC_READ,
  FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING,
  FILE_ATTRIBUTE_NORMAL, 0); {I have no physical drive 0 in my computer }
  try
  if hDiskFile = INVALID_HANDLE_VALUE then
    begin
      MessageDlg('CreateFile: ' + SystemErrorMessage, mtInformation, [mbOK], 0);
      halt(1);
    end;
  DwPtr := SetFilePointer(hDiskFile, 0, nil, FILE_BEGIN);
  if DwPtr = INVALID_SET_FILE_POINTER then
    begin
      MessageDlg('SetFilePointer: ' + SystemErrorMessage, mtInformation, [mbOK], 0);
      halt(1);
    end;
  if not ReadFile(hDiskFile,buf2[0], length(buf2), intreadedamount, nil) then
    begin
      MessageDlg('ReadFile: ' + SystemErrorMessage, mtInformation, [mbOK], 0);
      halt(1);
    end;
  finally
    CloseHandle(hDiskFile);
  end;

SystemErrorMessage is a neat little function I just picked up off of Torry's Delphi Pages, which does the calls for you I mentioned earlier.

Code:
function SystemErrorMessage: string;
var
  P: PChar;
begin
  if FormatMessage(Format_Message_Allocate_Buffer + Format_Message_From_System,
                   nil,
                   GetLastError,
                   0,
                   @P,
                   0,
                   nil) <> 0 then
  begin
    Result := P;
    LocalFree(Integer(P))
  end
  else
    Result := '';
end;
 
Ok... not my most brilliant day today :(.

PhysicalDriveN es a physical drive or a volume. Drives are sector oriented devices.

buho (A).
 
This has been an interesting exercise.

I thought it would be a good idea not to include error checking to save time, in the long run it has created more work.

Once again thank you for taking the time out to help, I am very grateful.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top