Author: Teun Spaans
How can I read a binary file?
How can I show a binary file in a memo field?
Answer:
Solve 1:
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,
1
2 procedure TForm1.Button1Click(Sender: TObject);
3 var
4 iFileHandle: Integer;
5 iFileLength: Integer;
6 iBytesRead: Integer;
7 Buffer: PChar;
8 begin
9 opendialog1.filter := 'executables|*.exe';
10 if opendialog1.Execute then
11 begin
12 try
13 iFileHandle := FileOpen(OpenDialog1.FileName, fmOpenRead or fmShareDenyNone);
14 if iFileHandle > 0 then
15 begin
16 iFileLength := FileSeek(iFileHandle, 0, 2);
17 FileSeek(iFileHandle, 0, 0);
18 Buffer := PChar(AllocMem(iFileLength + 2));
19 iBytesRead := FileRead(iFileHandle, Buffer^, iFileLength);
20 // note that ^ is missing in D5 help.
21 FileClose(iFileHandle);
22 end;
23 finally
24 FreeMem(Buffer);
25 end;
26 end;
27 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.
28
29 procedure TForm1.Button1Click(Sender: TObject);
30 var
31 iFileHandle: Integer;
32 iFileLength: Integer;
33 iBytesRead: Integer;
34 Buffer: PChar;
35 i, linelength: integer;
36 s: string;
37 line: string;
38 c: char;
39 ordval, ordval1, ordval2: integer;
40 begin
41 opendialog1.filter := 'executables|*.exe';
42 if opendialog1.Execute then
43 begin
44 try
45 iFileHandle := FileOpen(OpenDialog1.FileName, fmOpenRead or fmShareDenyNone);
46 if iFileHandle > 0 then
47 begin
48 iFileLength := FileSeek(iFileHandle, 0, 2);
49 FileSeek(iFileHandle, 0, 0);
50 Buffer := PChar(AllocMem(iFileLength + 2));
51 iBytesRead := FileRead(iFileHandle, Buffer^, iFileLength);
52 // note that ^ is missing in D5 help.
53 // 3 ways of conversion and show:
54 // way 1: exe will contain \0 so this code shows only part of exe
55 memo1.lines.add('way 1*********************************************');
56 s := string(Buffer);
57 memo1.lines.add(s);
58 // way 2: use \0 as newline for purpose of displaying in memo1.
59 memo1.lines.add('way 2*********************************************');
60 LineLength := 0;
61 Line := '';
62 for i := 0 to iFileLength - 1 do
63 begin
64 if Buffer[i] = #0 then
65 begin
66 memo1.lines.add(Line);
67 LineLength := 0;
68 Line := '';
69 end
70 else
71 begin
72 inc(LineLength);
73 // perhaps provision should be added for LineLength > max
74 delphi stringlength
75 Line := Line + Buffer[i]; // memo1 will handle normal new line chars
76 end;
77 end;
78 // way 3: display every char as ord
79 memo1.lines.add('way 3*********************************************');
80 Line := '';
81 for i := 0 to iFileLength - 1 do
82 begin
83 c := Buffer[i];
84 ordval := ord(c);
85 ordval1 := ordval div 16;
86 ordval2 := ordval mod 16;
87 Line := Line + '0123456789ABCDEF'[ordval1 + 1] +
88 '0123456789ABCDEF'[ordval2 + 1];
89 if Length(Line) = 80 then
90 begin
91 memo1.lines.add(line);
92 line := '';
93 end;
94 end;
95 FileClose(iFileHandle);
96 end;
97 finally
98 FreeMem(Buffer);
99 end;
100 end;
101 end;
Solve 2:
There is an inbuild delphi function (which I think appears in pre delphi 5)
BinToHex(Buffer, Text: PChar; BufSize: Integer);
which would create an output buffer in hex format.
|