Reputation: 629
I need to read files directly from drive without any system buffering. I tried this code here How to unload a file from cache? unfortunately I get error message saying "Out of memory while expanding memory stream". It happens with any file I tried. Later I've noticed that SrcStream.Size returns always value -1. So obviously problem is here and the question is why is this happening?
uses
MMSystem;
function GetTimeForRead(ABuffered: boolean): single;
const
FileToRead = // name of file with maybe 500 MByte size
var
FlagsAndAttributes: DWORD;
FileHandle: THandle;
SrcStream, DestStream: TStream;
Ticks: DWord;
begin
if ABuffered then FlagsAndAttributes := FILE_ATTRIBUTE_NORMAL
else FlagsAndAttributes := FILE_FLAG_NO_BUFFERING;
FileHandle := CreateFile(FileToRead, GENERIC_READ, FILE_SHARE_READ, nil,OPEN_EXISTING, FlagsAndAttributes, 0);
if FileHandle = INVALID_HANDLE_VALUE then
begin
Result := 0.0;
exit;
end;
SrcStream := THandleStream.Create(FileHandle);
try
DestStream := TMemoryStream.Create;
try
DestStream.Size := SrcStream.Size;
Ticks := timeGetTime;
DestStream.CopyFrom(SrcStream, SrcStream.Size);
Result := 0.001 * (timeGetTime - Ticks);
finally
DestStream.Free;
end;
finally
SrcStream.Free;
end;
end;
Upvotes: 0
Views: 6519
Reputation: 612884
FILE_FLAG_NO_BUFFERING
places special requirements on use of the file handle, that are not compatible with all functionality of Delphi's THandleStream
class. The principal such requirement is that all access is aligned. By which it is meant that the file pointer is always placced on sector boundaries, and all reads and writes are of multiples of the sector size. The specific failure point here is the Size
property.
You are reading a file whose size is not an exact multiple of the sector size. The answer you refer to fails with the error you report when presented a file whose size is not an exact multiple of the sector size. Presumably the author of that code was not aware of the issue and by pure chance used a file whose size was an exact multiple of the sector size.
You can confirm all of this by executing the code with an input file whose size is an exact multiple of 4096.
You likely can use THandleStream
with such a file handle but you will need to be careful. Obviously you must avoid Size
. You have to respect the alignment requirements. You need to be careful when reading at the end of the file because you will need to read an entire sector even if you know that the logical file ends before the end of the sector. That means using Read
rather than ReadBuffer
.
Frankly, in my opinion, if you must use unbuffered file access then I don't believe that the stream abstraction is a good fit. I would work directly with the Windows API.
Upvotes: 3