home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / AUTOI3.ZIP / AUTOINST.PAS
Encoding:
Pascal/Delphi Source File  |  1988-05-05  |  8.4 KB  |  223 lines

  1. program AutoInst3;
  2.  
  3.   { ==================================================================
  4.  
  5.                             Program: AutoInst v3.0
  6.                              Author: David Dubois
  7.                                      Zelkop Software
  8.                                      Halifax, Nova Scotia
  9.                CompuServe User I.D.: 71401,747
  10.                   Date last revised: 1988.05.05
  11.  
  12.     ==================================================================
  13.  
  14.     I hereby dedicate this knowledge to the public domain. I would
  15.     appreciate, though, if you mentioned my name.
  16.  
  17.     This program demonstrates how to write a program which will change
  18.     the value of a typed constant in its own .EXE file. When the
  19.     program is run again, the data will be initialized to the new
  20.     value. No external files are necessary.
  21.  
  22.     Unlike version 1 of this program, this code will work in a unit.
  23.     Thanks to those who suggested using PrefixSeg.
  24.  
  25.     Version 3 has corrected a bug in found by some freindly users of
  26.     Version 2. The "seek" computation (see below) was changed to
  27.     longint arithmetic. Thanks to those who notified me of the problem.
  28.  
  29.     The code shown here is designed for Turbo Pascal version 4.0.
  30.     There is a simpler method available for Turbo 3.0.
  31.  
  32.     For this example, I have written a program that writes a certain
  33.     string a certain number of times. It then prompts the user for a
  34.     new string and a new number. The .EXE file is updated to reflect
  35.     the values entered by the user. The next time the program is run,
  36.     the new values are used.
  37.  
  38.     USES
  39.  
  40.     This can be used to write a program that can install itself.
  41.     Examples of the usefulness of this technique would be:
  42.  
  43.     A program that allows the user to change default display colors.
  44.  
  45.     A program that keeps track of a password that the user can change.
  46.  
  47.     HOW TO USE THIS TECHNIQUE
  48.  
  49.     In your own program just copy the ReInstall procedure listed here.
  50.     Replace InstallBlock with the name of the typed constant you wish
  51.     to change, and replace the file name with the name of your
  52.     program. It's that easy.
  53.  
  54.     HOW IT WORKS
  55.  
  56.     You don't have to understand all the details in order to use this
  57.     technique, but here they are.
  58.  
  59.     The data to be changed must be stored in a TurboPascal typed
  60.     constant. In all effect, a typed constant is actually a pre-
  61.     initialized variable. It is always stored in the program's Data
  62.     Segment. The data can be of any type.
  63.  
  64.     First, the procedure finds the .EXE file. In this example, the
  65.     file must be in the current directory, and the user cannot have
  66.     changed the name of the file.
  67.  
  68.     The untyped file is opened with a record size of 1. This allows us
  69.     to read or write a string of bytes using BlockRead and BlockWrite.
  70.  
  71.     As documented in the DOS Technical Reference, the size of the .EXE
  72.     header, in paragraphs (a paragraph is 16 bytes), is stored as a
  73.     two-byte word at position 8 of the file. This is read into the
  74.     variable HeaderSize.
  75.  
  76.     The next step is to find the position of the typed constant in the
  77.     .EXE file. This requires an understanding of the Turbo Pascal 4.0
  78.     memory map, documented on the first and second pages of the Inside
  79.     Turbo Pascal chapter. (That's chapter 26, pages 335 and 336 in my
  80.     manual.)
  81.  
  82.     First, find the address in memory where the typed constant is
  83.     stored. This can be done in Turbo Pascal by using the Seg and Ofs
  84.     functions. Next find the segment of the PSP (program segment
  85.     prefix). This should always be the value returned by PrefixSeg.
  86.     That will mark the beginning of the program in memory. The
  87.     position of the typed constant in the .EXE image should be the
  88.     number of bytes between these two places in memory. But ...
  89.  
  90.     But, two corrections must be made. First, the PSP is not stored in
  91.     the .EXE file. As mentioned on page 335, the PSP is always 256
  92.     bytes. We must subtract that out. Secondly, there is the .EXE file
  93.     header. The size of this has already been read in and must be
  94.     added in to our calculations.
  95.  
  96.     You may be wondering about the purpose of longint. One of the values
  97.     had to be typecast into a longint so that the final result will be a
  98.     longint. This is in case your typed constant appears more than 65535
  99.     bytes into the file.
  100.  
  101.     Once the position has been determined, the data stored in the
  102.     typed constant is written in one fell swoop using a BlockWrite.
  103.     This replaces the original data, so that the next time the program
  104.     is run, the new values will used.
  105.  
  106.     ERROR CHECKING
  107.  
  108.     For the sake of simplicity, error checking has been left out of
  109.     this example.
  110.  
  111.     In a real-world application, I would imagine that you would want
  112.     to check that the file has actually been found. If the user ran
  113.     the program from a different directory, or if the program name had
  114.     been changed, the program will fail. In this case, you may want to
  115.     prompt the user for the path where the program can be found.
  116.     (Perhaps you might want to store this information as well.) A
  117.     clever program may be able to search for copies of itself on disk,
  118.     but that's a little dangerous.
  119.  
  120.     Another check could be added. Before writing to disk, you could
  121.     read the value of the data currently stored on the file to make
  122.     sure it matches the original values. This would guarantee that the
  123.     file had not been altered, or that the wrong version of your
  124.     program hadn't been found.
  125.  
  126.     LIMITATIONS
  127.  
  128.     Somehow, the program must be able to find the .EXE file. The
  129.     program cannot be run from the integrated enviroment with the
  130.     Memory option, since no .EXE file is generated.
  131.  
  132.     You cannot use MicroSoft's EXEPACK on the .EXE file, or any other
  133.     packing method I know of. This may change the position, or even
  134.     the size of the typed constant in the file image.
  135.  
  136.     NOTES
  137.  
  138.     Since typed constants are always stored in the data segment, the
  139.     function call to Seg ( InstallBlock ) can be replaced with DSeg. I
  140.     prefer using Seg since it is more descriptive.
  141.  
  142.     One might think that Cseg can used as an alternative to using
  143.     PrefixSeg and subtracting 256. This will work only if the code
  144.     resides in the main program. If, on the other hand, the code is
  145.     used in a unit, PrefixSeg must be used as described here. You
  146.     might as well use PrefixSeg and save yourself some headaches.
  147.  
  148.     If you have any comments or questions I would be glad to hear
  149.     them. If you're on CompuServe, you can EasyPlex a letter to
  150.     71401,747. Or leave a message on the Borland Programmer's A
  151.     Forum (GO BPROGA). Or, you can write to
  152.  
  153.                          Zelkop Software
  154.                          P.O. Box 5177
  155.                          Armdale, N.S.
  156.                          Canada
  157.                          B3L 4M7
  158.  
  159.     ==================================================================}
  160.  
  161. type
  162.   InstallBlockType = record
  163.                        TheNumber : integer;
  164.                        TheString : string;
  165.                      end;
  166.  
  167. const
  168.   InstallBlock : InstallBlockType
  169.  
  170.                = ( TheNumber : 3;
  171.                    TheString : 'Greetings from Zelkop Software' );
  172.  
  173.   procedure ReInstall;
  174.   const
  175.     FileName = 'AutoInst.Exe';
  176.   var
  177.     ExeFile    : file;
  178.     HeaderSize : word;
  179.   begin
  180.     assign    ( ExeFile, FileName );
  181.     reset     ( ExeFile, 1 );
  182.  
  183.     seek      ( ExeFile, 8 );
  184.     blockread ( ExeFile, HeaderSize, sizeof ( HeaderSize ) );
  185.  
  186.     seek      ( ExeFile,   16 * (   longint ( seg ( InstallBlock ) )
  187.                                   - PrefixSeg
  188.                                   + HeaderSize          )
  189.                          + ofs ( InstallBlock )
  190.                          - 256                           );
  191.  
  192.     blockwrite ( ExeFile, InstallBlock, sizeof ( InstallBlock ) );
  193.  
  194.     close      ( ExeFile );
  195.   end;
  196.  
  197. var
  198.   I : integer;
  199. begin
  200.   with InstallBlock do
  201.     begin
  202.  
  203.       { write out old data }
  204.  
  205.       for I := 1 to TheNumber do
  206.         writeln ( TheString );
  207.  
  208.       { read in new data }
  209.  
  210.       write  ( 'Enter TheNumber: ' );
  211.       readln ( TheNumber );
  212.       write  ( 'Enter TheString: ' );
  213.       readln ( TheString );
  214.     end;
  215.  
  216.   { update .EXE file }
  217.  
  218.   ReInstall;
  219.  
  220.   writeln ( 'Now run this program again.' );
  221. end.
  222.  
  223.