VCL Delphi

Title: loading an exe in a memo field
Question: How can I read a binary file?
How can I show a binary file in a memo field?
This article has been writtten in reply to an old artcile request by the same name by Ismael U.
Answer:
How to load an exec in a memo field
How to load an exec in a memo field
Why?
This article has been written in answer to an old request by ismael u, asking
how an executable can be loaded in a memo or rich memo field.
First a remark, executables should usually not be stored in a tmemo field,
but rather in some blob field. However, there are some occasions on which one
would like to view an executable. Studying (differences between) compiled
executables comes to mind.
I assume that Ismael means executable when he says exec, and the solution is
rather simple.
How?
Loading a an executable in a memo field basically comes down to 2 steps. The
first step is reading the file from disk and loading the file into memory, the
second step is showing the loaded contents in the tmemo field.
The first step, reading the file from disk and loading it into memory, is
rather easy. Perhaps TFileStream could be used, but I prefer the rather low
level FileOpen function because of its performance. Also, when working with
binary files, we must keep in mind that these files may contain #0 and many
pointer based operations regard this as an end/of/string character.
Basically, here is the code, mostly a copy of the delphi5 help after fixing
some minor bugs. Just create a form, add a button and a fileopendialog,


procedure TForm1.Button1Click(Sender : TObject);
var
iFileHandle : Integer;
iFileLength : Integer;
iBytesRead : Integer;
Buffer : PChar;
begin
opendialog1.filter := 'executables|*.exe';
if opendialog1.Execute then
begin
try
iFileHandle := FileOpen(OpenDialog1.FileName, fmOpenRead or fmShareDenyNone);
if iFileHandle 0 then
begin
iFileLength := FileSeek(iFileHandle, 0, 2);
FileSeek(iFileHandle, 0, 0);
Buffer := PChar(AllocMem(iFileLength + 2));
iBytesRead := FileRead(iFileHandle, Buffer^, iFileLength); // note that ^ is missing in D5 help.
FileClose(iFileHandle);
end;
finally
FreeMem(Buffer);
end;
end;
end;


The second step again poses us some questions. As the contents of the binary
file will contain #0, how will we show them?
The first way is to convert the entire Buffer read above into a string and
add this string to the memo. Doing this causes no technical problem, but the
memo shows just a few characters. That's probably now what we want. The cause
are the aforementioned #0 characters.
The second way is to go through the Buffer bit by bit, and switch to a new
line whenever we encounter a #0. Doing so is easy, and reveals that an ordinary
executable contains lots of #0 characters.
The third and probably best way is to show all characters in a hexagonal
notation.


procedure TForm1.Button1Click(Sender : TObject);
var
iFileHandle : Integer;
iFileLength : Integer;
iBytesRead : Integer;
Buffer : PChar;
i,linelength : integer;
s : string;
line : string;
c : char;
ordval, ordval1, ordval2 : integer;
begin
opendialog1.filter := 'executables|*.exe';
if opendialog1.Execute then
begin
try
iFileHandle := FileOpen(OpenDialog1.FileName, fmOpenRead or fmShareDenyNone);
if iFileHandle 0 then
begin
iFileLength := FileSeek(iFileHandle, 0, 2);
FileSeek(iFileHandle, 0, 0);
Buffer := PChar(AllocMem(iFileLength + 2));
iBytesRead := FileRead(iFileHandle, Buffer^, iFileLength); // note that ^ is missing in D5 help.

// 3 ways of conversion and show:
// way 1: exe will contain \0 so this code shows only part of exe
memo1.lines.add('way 1*********************************************');
s := string(Buffer);
memo1.lines.add(s);

// way 2: use \0 as newline for purpose of displaying in memo1.
memo1.lines.add('way 2*********************************************');
LineLength := 0;
Line:= '';
For i:= 0 To iFileLength-1 do
begin
If Buffer[i] = #0 then
begin
memo1.lines.add(Line);
LineLength := 0;
Line := '';
end
else
begin
inc(LineLength); // perhaps provision should be added for LineLength max delphi stringlength
Line := Line+Buffer[i]; // memo1 will handle normal new line chars
end;
end;

// way 3: display every char as ord
memo1.lines.add('way 3*********************************************');
Line:= '';
For i:= 0 To iFileLength-1 do
begin
c := Buffer[i];
ordval := ord(c);
ordval1 := ordval div 16;
ordval2 := ordval mod 16;
Line := Line + '0123456789ABCDEF'[ordval1+1]+'0123456789ABCDEF'[ordval2+1];
if Length(Line) = 80 then
begin
memo1.lines.add(line);
line := '';
end;
end;

FileClose(iFileHandle);
end;
finally
FreeMem(Buffer);
end;
end;
end;