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

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