qseclrb@prism.gatech.EDU (BOB BAGGERMAN) (06/12/90)
Several people asked about Turbo Pascal code which will go in and modify
its own .EXE file to install options and things of that sort. Below is
an example program I got off of CompuServe. Note that this procedure will
probably only work for TP 4.0. I think in TP 5.0 and 5.5 they moved the
location of constants. If so, maybe someone could bone up on the later
versions and post an AutoInst which works for TP 5.0 and above.
Regards, Bob Baggerman rwb@csdvax.gatech.edu
p.s. I have some great toolboxes for Turbo Pascal available via anonymous
ftp from csdvax.gatech.edu. Help yourself!
-------------------------------------------------------------------------
program AutoInst3;
{ ==================================================================
Program: AutoInst v3.0
Author: David Dubois
Zelkop Software
Halifax, Nova Scotia
CompuServe User I.D.: 71401,747
Date last revised: 1988.05.05
==================================================================
I hereby dedicate this knowledge to the public domain. I would
appreciate, though, if you mentioned my name.
This program demonstrates how to write a program which will change
the value of a typed constant in its own .EXE file. When the
program is run again, the data will be initialized to the new
value. No external files are necessary.
Unlike version 1 of this program, this code will work in a unit.
Thanks to those who suggested using PrefixSeg.
Version 3 has corrected a bug in found by some freindly users of
Version 2. The "seek" computation (see below) was changed to
longint arithmetic. Thanks to those who notified me of the problem.
The code shown here is designed for Turbo Pascal version 4.0.
There is a simpler method available for Turbo 3.0.
For this example, I have written a program that writes a certain
string a certain number of times. It then prompts the user for a
new string and a new number. The .EXE file is updated to reflect
the values entered by the user. The next time the program is run,
the new values are used.
USES
This can be used to write a program that can install itself.
Examples of the usefulness of this technique would be:
A program that allows the user to change default display colors.
A program that keeps track of a password that the user can change.
HOW TO USE THIS TECHNIQUE
In your own program just copy the ReInstall procedure listed here.
Replace InstallBlock with the name of the typed constant you wish
to change, and replace the file name with the name of your
program. It's that easy.
HOW IT WORKS
You don't have to understand all the details in order to use this
technique, but here they are.
The data to be changed must be stored in a TurboPascal typed
constant. In all effect, a typed constant is actually a pre-
initialized variable. It is always stored in the program's Data
Segment. The data can be of any type.
First, the procedure finds the .EXE file. In this example, the
file must be in the current directory, and the user cannot have
changed the name of the file.
The untyped file is opened with a record size of 1. This allows us
to read or write a string of bytes using BlockRead and BlockWrite.
As documented in the DOS Technical Reference, the size of the .EXE
header, in paragraphs (a paragraph is 16 bytes), is stored as a
two-byte word at position 8 of the file. This is read into the
variable HeaderSize.
The next step is to find the position of the typed constant in the
.EXE file. This requires an understanding of the Turbo Pascal 4.0
memory map, documented on the first and second pages of the Inside
Turbo Pascal chapter. (That's chapter 26, pages 335 and 336 in my
manual.)
First, find the address in memory where the typed constant is
stored. This can be done in Turbo Pascal by using the Seg and Ofs
functions. Next find the segment of the PSP (program segment
prefix). This should always be the value returned by PrefixSeg.
That will mark the beginning of the program in memory. The
position of the typed constant in the .EXE image should be the
number of bytes between these two places in memory. But ...
But, two corrections must be made. First, the PSP is not stored in
the .EXE file. As mentioned on page 335, the PSP is always 256
bytes. We must subtract that out. Secondly, there is the .EXE file
header. The size of this has already been read in and must be
added in to our calculations.
You may be wondering about the purpose of longint. One of the values
had to be typecast into a longint so that the final result will be a
longint. This is in case your typed constant appears more than 65535
bytes into the file.
Once the position has been determined, the data stored in the
typed constant is written in one fell swoop using a BlockWrite.
This replaces the original data, so that the next time the program
is run, the new values will used.
ERROR CHECKING
For the sake of simplicity, error checking has been left out of
this example.
In a real-world application, I would imagine that you would want
to check that the file has actually been found. If the user ran
the program from a different directory, or if the program name had
been changed, the program will fail. In this case, you may want to
prompt the user for the path where the program can be found.
(Perhaps you might want to store this information as well.) A
clever program may be able to search for copies of itself on disk,
but that's a little dangerous.
Another check could be added. Before writing to disk, you could
read the value of the data currently stored on the file to make
sure it matches the original values. This would guarantee that the
file had not been altered, or that the wrong version of your
program hadn't been found.
LIMITATIONS
Somehow, the program must be able to find the .EXE file. The
program cannot be run from the integrated enviroment with the
Memory option, since no .EXE file is generated.
You cannot use MicroSoft's EXEPACK on the .EXE file, or any other
packing method I know of. This may change the position, or even
the size of the typed constant in the file image.
NOTES
Since typed constants are always stored in the data segment, the
function call to Seg ( InstallBlock ) can be replaced with DSeg. I
prefer using Seg since it is more descriptive.
One might think that Cseg can used as an alternative to using
PrefixSeg and subtracting 256. This will work only if the code
resides in the main program. If, on the other hand, the code is
used in a unit, PrefixSeg must be used as described here. You
might as well use PrefixSeg and save yourself some headaches.
If you have any comments or questions I would be glad to hear
them. If you're on CompuServe, you can EasyPlex a letter to
71401,747. Or leave a message on the Borland Programmer's A
Forum (GO BPROGA). Or, you can write to
Zelkop Software
P.O. Box 5177
Armdale, N.S.
Canada
B3L 4M7
==================================================================}
type
InstallBlockType = record
TheNumber : integer;
TheString : string;
end;
const
InstallBlock : InstallBlockType
= ( TheNumber : 3;
TheString : 'Greetings from Zelkop Software' );
procedure ReInstall;
const
FileName = 'AutoInst.Exe';
var
ExeFile : file;
HeaderSize : word;
begin
assign ( ExeFile, FileName );
reset ( ExeFile, 1 );
seek ( ExeFile, 8 );
blockread ( ExeFile, HeaderSize, sizeof ( HeaderSize ) );
seek ( ExeFile, 16 * ( longint ( seg ( InstallBlock ) )
- PrefixSeg
+ HeaderSize )
+ ofs ( InstallBlock )
- 256 );
blockwrite ( ExeFile, InstallBlock, sizeof ( InstallBlock ) );
close ( ExeFile );
end;
var
I : integer;
begin
with InstallBlock do
begin
{ write out old data }
for I := 1 to TheNumber do
writeln ( TheString );
{ read in new data }
write ( 'Enter TheNumber: ' );
readln ( TheNumber );
write ( 'Enter TheString: ' );
readln ( TheString );
end;
{ update .EXE file }
ReInstall;
writeln ( 'Now run this program again.' );
end.
--
Bob Baggerman ! rwb@csdvax.gatech.edu
Communications Systems Division ! qseclrb@hydra.gatech.edu
Georgia Tech Research Institute ! bbaggerm@gtri01.gatech.edu
Atlanta, GA 30332 ! 404-894-3525