I am trying to read a file into a byte array in Delphi XE2.
This is my current code:
function FileToBytes(const AName: string; var Bytes: TBytes): Boolean;
var
Ms: TMemoryStream;
begin
Result := False;
if not FileExists(AName) then
Exit;
Ms := TMemoryStream.Create;
try
Ms.LoadFromFile(AName);
if Ms.Size > 0 then
begin
Ms.Position := 0;
MS.ReadBuffer(Bytes[0], Ms.Size);
Result := True;
end;
finally
Ms.Free;
end;
end;
procedure runFile();
var
Bytes: TBytes;
OpFile: String;
begin
OpFile := 'C:\Users\Kenny\Documents\calc.exe';
Bytes := nil;
if FileToBytes(OpFile, Bytes) then
begin
//do someting with Bytes(array of Byte)
end;
end;
I am getting an error at this line:
MS.ReadBuffer(Bytes[0], Ms.Size);
The error is:
access violation at 0x00404727: write of address 0x00000008
Any help in solving this would be greatly appreciated.
You did not allocate the array, which explains the error. You can fix your code like this:
Ms.LoadFromFile(AName);
SetLength(Bytes, Ms.Size);
Ms.Position := 0;
MS.ReadBuffer(Pointer(Bytes)^, Ms.Size);
Result := True;
Note that I have avoided the need to check for a zero length file, and used Pointer(Bytes)
so that the code will work when range checking is active.
I would also note that your code falls foul of what I refer to as the Delphi memory stream anti-pattern. You read the file into a memory stream, which is essentially a byte array. And then you copy from that byte array to another byte array. You write the entire file to two separate byte arrays. That's one more than necessary. Better to write it like this:
function FileToBytes(const AName: string; var Bytes: TBytes): Boolean;
var
Stream: TFileStream;
begin
if not FileExists(AName) then
begin
Result := False;
Exit;
end;
Stream := TFileStream.Create(AName, fmOpenRead);
try
SetLength(Bytes, Stream.Size);
Stream.ReadBuffer(Pointer(Bytes)^, Stream.Size);
finally
Stream.Free;
end;
Result := True;
end;
This way you read directly from the file into the target byte array.
I'm not terribly fond of having this as a function which returns a boolean to indicate success. There are plenty of ways in which the code could fail other than the file not existing. And these will lead to exceptions with your design. I'd prefer to see pure exception based error handling, or pure error code based error handling. But not the blended approach of your design.
As Andreas points out in comments, you can also use the RTL library function System.IOUtils.TFile.ReadAllBytes
to perform this task. Although personally I tend to avoid the entire System.IOUtils
unit because of numerous design issues. Whilst some of these have been sorted out in more recent releases, I believe that the XE2 version is liable to be somewhat riddled with issues.
User contributions licensed under CC BY-SA 3.0