home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 December / simtel1292_SIMTEL_1292_Walnut_Creek.iso / msdos / pcmag / extend_4.arc / EXTEND.DOC < prev    next >
Text File  |  1987-10-26  |  17KB  |  295 lines

  1. (*
  2.                                   EXTEND.PAS
  3.                                  Version 3.0
  4.                                October 26, 1987
  5.                               by Scott Bussinger
  6.                              Compuserve 72247,2671
  7.  
  8.  
  9.   MS-DOS and PC-DOS Turbo Pascal 3.0 and higher only allow up to 15 files to
  10. be open at the same time, due to limitations in DOS.  This file shows you how
  11. to have up to 96 files open simultaneously under DOS 2.0 or 2.1, or 252 files
  12. open simultaneously under DOS 3.0 or higher.  Below is a description of how to
  13. use this technique, followed by a technical explanation of the implementation,
  14. for those who are interested.  Note this documentation is liberally based on
  15. the original EXTEND.PAS documentation by Randy Forgaard, but has been renamed
  16. to EXTEND.DOC and modified by Scott Bussinger to reflect the changes to
  17. involved in versions 2 and 3.
  18.  
  19. TO USE THIS TECHNIQUE:
  20.  
  21.   You simply include the EXTEND unit in the USES statement in the source code
  22. of your main program.  You will have to either compile the EXTEND unit
  23. separately or use Turbo Pascal 4.0's project compiling features to Make or
  24. Build your program (which will compile the EXTEND unit automatically).  The
  25. EXTEND unit should be the first unit in your USES statement, following only
  26. the DOS unit (which is USEd by the EXTEND unit).  The unit automatically
  27. installs itself and will uninstall itself when your program ends or an error
  28. is detected.  See the demo program below for an example of using the EXTEND
  29. unit.
  30.  
  31.   The EXTEND.PAS file will need to be available available to be compiled if
  32. the Make or Build features are used to compile the program.  The EXTEND.ASM
  33. is the source code for the assembly language interrupt handler used when
  34. running under DOS 2.  The EXTEND.OBJ file is the assembled object code for
  35. EXTEND.ASM and was compiled using A86, a shareware compiler available on
  36. Compuserve and other bulletin boards.
  37.  
  38.   You must edit your CONFIG.SYS file (see the DOS manual for details), so
  39. that it includes a line that says "FILES=XXX".  XXX should be a number that is
  40. at least 3 greater than the maximum number of files that will be in use
  41. (opened by RESET, REWRITE or APPEND) at the same time during the execution of
  42. your program (larger values will provide no additional benefit, with respect
  43. to your individual program), and should not exceed 99 (for DOS 2.0/2.1) or 255
  44. (for DOS 3.0 and higher).  Under any version of DOS, the minimum allowable
  45. value for XXX is 8.  Then, reboot your computer so that the FILES=XXX
  46. parameter takes hold.  This same change to the CONFIG.SYS file will have to be
  47. made on any machine in which your program is to run.
  48.  
  49.   Running the sample program at the bottom of this file will tell you the
  50. maximum number of files you can have open at once (usually 3 less than the
  51. number of files specified in the CONFIG.SYS file as discussed above, unless a
  52. resident program has opened some files and hasn't closed them yet).
  53.  
  54.  
  55. THE TECHNICAL DETAILS:
  56.  
  57.   Much of the following information is not documented in the DOS Technical
  58. Reference manual.
  59.  
  60.   Under DOS 1.0 and 1.1, all files were accessed via File Control Blocks
  61. (FCB's).  There was no limit to the number of FCB's that a program could use,
  62. so there was no limit to the number of files open simultaneously.
  63.  
  64.   Under DOS 2.0 and higher, an alternate (and preferable) method of accessing
  65. files was introduced, using a 2-byte integer called a "handle" to refer to a
  66. file.  A "handle" file is described using a data structure called a Device
  67. Control Block (DCB).  However, DOS provides the storage space for all DCB's,
  68. rather than having the application program store the DCB's, so the number of
  69. available DCB's is limited to the amount of space that DOS has set aside for
  70. them.  The maximum number of files/devices that can be open simultaneously, on
  71. the whole computer, is the number of slots available in the DCB Table created
  72. by DOS.  The DCB's in the DCB Table are consecutively numbered, starting with
  73. 0.  DCB's 0, 1, and 2 are predefined by DOS to correspond to the AUX, CON, and
  74. PRN devices, respectively.  All remaining DCB's in the DCB Table are available
  75. for files or devices used by application programs.
  76.  
  77.   So that I/O redirection can be supported, the DCB numbers are not used
  78. directly when accessing files.  Instead, a file "handle" is used.  A "handle"
  79. is an index into a 20-byte array, called the Handle Table, located at offset
  80. 18H of the Program Segment Prefix (PSP) for a program (for a general
  81. discussion of the PSP, see the DOS Technical Reference manual).  Each element
  82. of the Handle Table is the DCB number of a file or device.  The value at index
  83. "handle" in the Handle Table is the DCB number of the file or device that
  84. implements that file handle.  Thus, if the value 8 is in the 6th byte of the
  85. Handle Table, the handle "6" refers to the file (or device) described by the
  86. DCB in slot 8 of the DCB Table.  If a handle is not currently being used, its
  87. entry in the Handle Table is FFH.  DOS predefines the first 5 handles to be
  88. primary input, primary output, error, auxiliary, and printer, so the first 5
  89. entries in the Handle Table are 1, 1, 1, 0, and 2, corresponding to the DCB
  90. numbers for the CON (1), AUX (0), and PRN (2) devices.  This leaves only 15
  91. available handles for opening files (or new devices).
  92.  
  93.   Every time a new handle file is opened, a new handle gets used.  Since there
  94. are only 20 slots available in the Handle Table for a program, DOS only allows
  95. a "process" to have a maximum of 20 file handles in use simultaneously (and the
  96. first 5 entries are predefined, as just noted, unless those handles get closed
  97. and reused).  Every new handle file requires a unique handle, so only 20
  98. files/devices can be open at the same time by a single process (unless FCB's
  99. are used).  (A "process" is any program spawned using the DOS EXEC function
  100. call.  A process can be invoked by COMMAND.COM, or by another program.)  There
  101. can be many more than 20 DCB's in the DCB Table, so the real limitation is in
  102. the size of the Handle Table in the PSP.
  103.  
  104.   The size of the DCB Table (i.e., the maximum number of files/devices that
  105. can be open simultaneously in the whole computer) is controlled by the
  106. FILES=XXX entry in the CONFIG.SYS file.  The minimum number of slots is 8.
  107. Under DOS 2.0/2.1, the maximum number is 99, and under DOS 3.0 and higher,
  108. the maximum is 255.  As previously mentioned, the first three of these DCB
  109. slots are occupied by the AUX, CON, and PRN devices.
  110.  
  111.   A single program can use all of the DCB's in the DCB Table (except for the 3
  112. reserved by DOS) all on its own, by effectively bypassing the Handle Table in
  113. the PSP, except on a temporary basis.  The program can accomplish this feat by
  114. using, say, only one entry in the Handle Table for all of its files.  Instead
  115. of allowing DOS to store the DCB numbers in the Handle Table, the program can
  116. store these numbers elsewhere.  Then, to manipulate a file using DOS, the
  117. program can temporarily put the DCB number of that file into a designated slot
  118. in the Handle Table, pass the index of that table slot (i.e., that "handle")
  119. to DOS, and DOS will operate on that handle/DCB number.  After the DOS call,
  120. the program can remove that DCB number from the designated Handle Table slot,
  121. freeing up that handle for use in another DOS call for another file.  In this
  122. way, DOS can be fooled into accessing up to 96 (or 252) different files/devices
  123. using a single handle entry in the Handle Table.
  124.  
  125.   To obtain the address of the Handle Table, which is at offset 18H in the
  126. PSP, the program needs to find the address of its PSP.  Under Turbo Pascal
  127. 4.0, the segment address of the PSP is stored in a pre-defined variable called
  128. PrefixSeg setup by Turbo's standard initialization code.  All of the
  129. manipulation of the DCB's and handles is dealt with by ExtendHandler which is
  130. an interrupt handler which intercepts all DOS functions calls (INT 21) and
  131. checks to see if the function involves a handle number.  If the function
  132. returns a handle (e.g. an Open File) it then returns the DCB to the calling
  133. program in place of a handle.  It adds an constant offset of 5 to the DCB so
  134. that ExHandler can distinguish between DCB's and calls refering to the 5
  135. automatically assigned standard handles.  If the function invoked passes a
  136. handle to DOS (e.g. Read File) it checks to see if it refers to a standard
  137. handle and if so it passes control normally.  If not, it subtracts the
  138. constant offset of 5 and temporarily usurps the use of handle 19.  It places
  139. the DCB into the the slot in the handle table for handle 19 and changes the
  140. handle in the registers to 19.  The previous DCB in handle 19 is saved and
  141. restored afterwards (this probably isn't necessary, but made me feel better).
  142.  
  143.   Note that these routines support all DOS functions up through DOS 3.3
  144. including those not normally used by Turbo.  The exception is function
  145. $46 (FORCEDUP). This call is rarely used, but incompatible with the methods
  146. used by ExHandler.  The only use that I know of for this function is to
  147. redirect the standard handles and since the standard handles are effectively
  148. skipped by these routines, there is no problem in using handles 0-4 as the
  149. second handle in a FORCEDUP call.  No other handles are allowed and will
  150. automatically result in an error being returned.  In other words, go ahead and
  151. redirect the standard handles, but don't try it with anything else.
  152.  
  153.   As of version 2.4, the installation routine now checks the DOS version that
  154. the program is being run under.  Under 2.x, the unit functions as described
  155. above.  Under DOS 3.x or higher, the EXTEND unit now uses a slightly
  156. documented capability to install a new, larger handle table in the later
  157. versions of the operating system.  Essentially, under DOS 3.x Microsoft
  158. started to permit a standard way of creating a new handle table.  This
  159. information is has been seen in at least one Microsoft document and that is
  160. probably a sign that it will continue to work in future versions of DOS.
  161. There are now two fields in the PSP which contain a pointer to the beginning
  162. of the handle table and a byte specifying the number of available handles.
  163. This method is much less kludgey and solves some problems with running
  164. programs under debuggers.  The space for the new handle table is allocated off
  165. of the heap to save space in the data segment.  When Exit routine is called,
  166. the handle table is DISPOSEd.  According to the Turbo manual DISPOSE is not
  167. compatible with RELEASE, so be aware that if you are using RELEASE in you
  168. program that you will need to modify the UnExtendHandles routine to be
  169. compatible.  Possibly the easiest thing to do is simply remove the DISPOSE
  170. statement, but it will depend on your program.
  171.  
  172.   As of version 2.3, the interrupt handler checks to make sure that the
  173. program calling the DOS function has the same code segment as the interrupt
  174. handler.  This solves a problem with EXEC'ing out to a DOS shell and trying
  175. to execute batch files.  For some reason, the methods used by EXTEND
  176. completely screw up the DOS batch file processor, so it just ignores calls
  177. from outside programs.  This may also prevent some problems from already
  178. loaded in-memory TSR programs like SideKick that also use DOS calls.  Note
  179. that because the internal file handle table is largely bypassed, any child
  180. process which is invoked using EXEC will not be able to inherit open handles
  181. other than the standard handles.  Version 3.0 modifies this logic slightly to
  182. account for Turbo 4.0's new memory map.  It checks that the program calling
  183. INT 21 has a code segment between the PSP and the DS of the installing
  184. program.  Because of how Turbo 4.0 initializes its registers, this guarantees
  185. the the calling program is the same as installed the interrupt handler.
  186.  
  187.   There is one incompatibility that I know of with EXTEND.  That is
  188. sub-functions 4 and 5 of DOS function $44 (IOCTL) when running under DOS 2.
  189. These calls send and receive control strings from block devices.  These two
  190. sub-functions use registers differently than all other sub-functions of IOCTL.
  191. I can't imagine a use for these two functions in a Pascal program, so I
  192. haven't bothered to modify the interrupt handler to identify this case.  If it
  193. is a problem, let me know and I may fix it.  The only other call that could
  194. potentially collide with EXTEND is the new function $67 (Set Handle Count)
  195. that was added in DOS 3.3.  This function serves the same purpose as the
  196. EXTEND unit, so I can't imagine why you would use both in one program anyway.
  197. Though I haven't tested it, I suspect the only effect would be to waste some
  198. memory (and not much at that).
  199.  
  200.   Many thanks to Randy Forgaard (Compuserve 70307,521) for writing the
  201. version 1 of this program, Bela Lubkin (CompuServe 76703,3015) for
  202. masterminding this idea, and to Kim Kokkonen (CompuServe 72457,2131) for
  203. helping me debug it.  For more discussion of Handle Tables and the
  204. implementation of DOS redirection, please see Stan Mitchell, "Command
  205. Line Redirection," PC Tech Journal, January 1986, Page 44.
  206.  
  207. Change Log:
  208.  
  209.   Version 1.1: Now invokes a DOS function to get the PSP address, so that
  210.                programs using this technique can be run in memory under Turbo.
  211.  
  212.   Version 1.2: Replaced some outdated comments.
  213.  
  214.   Version 1.3: Changed example program so that it erases the files it creates.
  215.  
  216.   Version 2.0: Changed program from explicit calls to extend files into an
  217.                INT21 handler which intercepts all DOS calls.  This makes use
  218.                of extended files generally invisible to the source code and
  219.                simplifies its use.  First version by Scott Bussinger.
  220.  
  221.   Version 2.1: Changed it so that handle passed to DOS is reconverted back
  222.                into a DCB since some of the runtime library code assumes that
  223.                registers are useful after calling DOS.
  224.  
  225.   Version 2.2: Fixed some problems with EXEC and allowed freedom to use
  226.                FORCEDUP with the standard handles.
  227.  
  228.   Version 2.3: EXTEND now only affects file handling calls made from within
  229.                its own code segment (i.e. the installing Pascal program). This
  230.                solves problems with using EXEC with batch files and already
  231.                resident TSR (terminate and stay-resident) programs.
  232.  
  233.   Version 2.4: Fixed a problem with the DUP function under DOS 2.x.  EXTEND
  234.                now allocates new handle table under DOS 3.x on heap to be
  235.                compatible with TDebug Plus.
  236.  
  237.   Version 2.5: Work done by Kim Kokkonen and Brian Foley to work with Turbo
  238.                Extender and whittle off a few clock cycles
  239.  
  240.   Version 3.0: Reworked as a UNIT for use with Turbo Pascal 4
  241.                EXTEND.ASM reworked to be compatible with A86 assembler
  242.                Added support for DOS 3.3 (Commit File call)
  243. *)
  244.  
  245. {  Example program  --  This program opens as many Text files as it can, until
  246.  DOS runs out of room in its DCB Table.  It then reports how many files were
  247.  successfully opened, writes a line to each of them, then closes and erases
  248.  each of them.  Note: The value of the FILES=XXX parameter in the CONFIG.SYS
  249.  file must be set to an appropriate value (see above).  If you change the
  250.  FILES=XXX value, be sure to reboot before running this program.
  251.  
  252.  This program takes a while to run, due to the heavy disk I/O, so running it on
  253.  a hard disk (or, even better, a RAM disk) is recommended.  Make sure that you
  254.  are running the program in a subdirectory, so that you don't run up against
  255.  the DOS limit on the number of allowable files in the root directory of a
  256.  drive. }
  257.  
  258. program Test;
  259.  
  260. uses Extend;       { <--- This is the magic line that does everything }
  261.  
  262. const MaxCount = 255;
  263.  
  264. type FileArray = array[1..MaxCount] of text;
  265.  
  266. var Count: integer;
  267.     F: ^FileArray;
  268.     I: integer;
  269.     Num: string[6];
  270.  
  271. begin
  272. new(F);            { Use heap because of large size of this array }
  273. writeln('Opening files...');
  274. I := 0;
  275. repeat
  276.   inc(I);
  277.   str(I,Num);
  278.   assign(F^[I],'junk' + num + '.txt');
  279.   {$I-}
  280.   rewrite(F^[I])
  281.   {$I+}
  282. until ioresult <> 0;
  283. Count := I - 1;
  284. writeln('Successfully opened ', Count, ' files at the same time.  Writing to each file...');
  285. for I := 1 to Count do
  286.   writeln(F^[I], 'This is a test.');
  287. writeln('Closing and erasing each file...');
  288. for I := 1 to Count do
  289.   begin
  290.   close(F^[I]);
  291.   erase(F^[I])
  292.   end;
  293. writeln('Done.')
  294. end.
  295.