Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read binary file in Delphi

Tags:

delphi

I want to read a binary file and display the result in a memo but not how to do with this error: "Incompatible types: 'string' and 'Array'", the code is this

unit yo;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  F: TFileStream;
  Buffer: array [0 .. 1023] of byte;
begin

  F := TFileStream.Create(ExtractFilePath(Application.ExeName)
      + 'yo.exe', fmOpenRead);

  while F.Position < F.Size do
  begin

    F.Read(Buffer, 1024);
    Memo1.Lines.Add(Buffer);

  end;

  F.Free;

end;

managed to avoid this error and to run the program without errors ?

anyone can help me?

like image 443
14K Avatar asked Nov 20 '25 18:11

14K


1 Answers

You can't directly bypass this error, because a string and an array[] of Byte are not directly assignable.

Since binary content (the #0 character in particular, or the 0x00 hex value (C/C++)) won't be displayable in a TMemo anyway (the text would be terminated at the first #0 value), you'd need to replace it with something.

The easiest way to get past the compiler error is to change your array from array[] of Byte to array[] of AnsiChar, which can be directly assigned to a string (or typecast to one):

var
  Buffer: array[0..1023] of AnsiChar;
  TempStr: string;
begin
  // Fill buffer from stream
  TempStr := Buffer;
  Memo1.Lines.Add(TempStr);
  // The next line eliminates the need for `TempStr`
  // Memo1.Lines.Add(String(Buffer));
end;

However, like I said, this won't solve the problem of displaying in the memo. For instance, when actually reading a Windows executable, the first buffer displays MZP, because the fourth byte is a #0 and the memo terminates the string.

To overcome this limitation, you need to replace all #0 characters with something else. The problem with that, of course, is that any value you replace it with can also actually occur in the executable (since they're bytes, there are only 256 possible values). Again, the simple solution is to replace all #0 characters with 0 (#216):

var
  Buffer: array[0..1023] of AnsiChar;
  i: Integer;
  TempStr: string;
begin
  // Fill buffer as before
  for i := Low(Buffer) to High(Buffer) do
    if Buffer[i] = #0 then
      Buffer[i] := `Ø`;        // Try #144 instead
  TempStr := Buffer;
  Memo1.Lines.Add(TempStr);
  // You can still eliminate the string variable by typecasting
  // Memo1.Lines.Add(String(TempStr));
end;

Here's code for a TForm.FormCreate event that actually reads a 1K buffer from a Delphi console application, does the above replacement, and displays the content in a TMemo. Drop the TMemo on a form, set its Alignment property to alClient, and ScrollBars to ssVertical. Add a FormCreate event handler to the form, and use the following code for that event:

procedure TForm1.FormCreate(Sender: TObject);
var
  Stream: TFileStream;
  Buffer: array[0..1023] of AnsiChar;
  TempStr: string;
  i: Integer;
begin
  Memo1.Clear;
  // Populate buffer elements
  Stream := TFileStream.Create('D:\Temp\Project2.exe', fmOpenRead);
  try
    Stream.Read(Buffer[0], SizeOf(Buffer));
  finally
    Stream.Free;
  end;
  // Replace null (#0) values with #216 (Ø)
  for i := Low(Buffer) to High(Buffer) do
    if Buffer[i] = #0 then
      Buffer[i] := 'Ø';
  TempStr := Buffer;
  Memo1.Lines.Add(TempStr);
end;

NOTE: If you're actually reading the entire binary file instead of just the first buffer, the last buffer might not be entirely full of the file content (you might not read a full buffer on the last pass). In that case, you want to mark the end of the buffer with a #0 so that the memo correctly displays that partial buffer. You can change the for loop to use something like this:

for i := Low(Buffer) to High(Buffer) do
begin
  if (i = BytesRead) then
  begin
    Buffer[i] := #0;   // Mark the end of the buffer and exit loop; 
    Break;
  end
  else if (Buffer[i] = #0) then
   Buffer[i] := 'Ø';
end;

Here's the output of reading a single buffer full:

Executable displayed in TMemo

like image 159
Ken White Avatar answered Nov 22 '25 15:11

Ken White



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!