home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / bor_tips / ti174.asc < prev    next >
Text File  |  1988-04-18  |  16KB  |  336 lines

  1. PRODUCT : TURBO PASCAL     NUMBER : 174
  2. VERSION : 3.01A
  3.      OS : PC-DOS, MS-DOS
  4.    DATE : August 1, 1986
  5.  
  6.   TITLE : FILES OPEN EXTEND
  7.  
  8. The following is public domain information that has been uploaded 
  9. to  our Forum on CompuServe.  As a courtesy to our users that  do 
  10. not  have  immediate  access  to  CompuServe,  Technical  Support 
  11. distributes these routines free of charge.
  12.  
  13. However,  because these routines are public domain programs,  not 
  14. developed by Borland International,  we are unable to provide any 
  15. technical support or assistance using these routines. If you need 
  16. assistance   using   these   routines,    or   are   experiencing 
  17. difficulties,  we  recommend  that you log  onto  CompuServe  and 
  18. request  assistance  from the Forum members that developed  these 
  19. routines.
  20.  
  21. Written by    
  22.   Randy Forgaard,  CompuServe 70307,521
  23. Many   thanks   to  Bela  Lubkin, (CompuServe   76703,3015)   for 
  24. masterminding   this   idea,   and  Kim   Kokkonen,   (CompuServe 
  25. 72457,2131)  for  helping me debug it.   For more  discussion  of 
  26. Handle Tables and the implementation of DOS  redirection,  please 
  27. see  Stan Mitchell,  "Command Line Redirection," PC Tech Journal, 
  28. January 1986, Page 44.
  29.  
  30. Due to a limitation of DOS, Turbo Pascal, version 3.0 only allows  up to 15 files at a time to be open.  The following method allows  you  to have up to 96 files open simultaneously under DOS 2.0  or  2.1, or 252 files open simultaneously under DOS 3.0 or greater.
  31.  
  32. TO USE THIS TECHNIQUE:
  33.  
  34. You need the routines and global declarations below.   Everywhere  in  your  program that you Reset or Rewrite a file  "f"  for  the  first  time,  insert  an "OpenExtend(f);" invocation  immediately  after the Reset or Rewrite.   Each time your program calls one of  Turbo's built-in routines (other than Assign) for handling  files  (e.g.,  Read,  Write,  Close, Seek, Reset, Rewrite, etc.), put an  "UnExtend(f);"  invocation immediately prior to the call,  and  a  "ReExtend(f);"  invocation  immediately after the call.   Do  not  insert UnExtend and ReExtend calls around the very first Reset or  Rewrite that you use to initially open a file.
  35.  
  36.  
  37. no additional benefit.   Each larger value for the {$F} directive  uses 2 additional bytes in the program's global data space.)  The  value you specify for the {$F} directive is the maximum number of  files  you  will  be able to have open at the same time  in  your  program.
  38.  
  39. Edit  your CONFIG.SYS file (see the DOS manual for  details),  so  that it includes a line that says "FILES=XXX".   XXX should be  a  version  that  is 3 greater than the value you specified for  the  {$F} directive (larger values will provide no additional benefit,  with respect to your individual program),  and should not  exceed  99 (for DOS 2.0/2.1) or 255 (for DOS 3.0 and higher).   Under any  version of DOS,  the minimum allowable value for XXX is 8.  Then,  reboot  your computer so that the FILES=XXX parameter takes hold.   
  40. Running  the sample program at the bottom of this file will  tell  you the maximum number of files you can open at once. (Usually 96  or  252,  unless  a resident program has opened  some  files  and  hasn't closed them yet.)
  41.  
  42. THE TECHNICAL DETAILS:
  43.  
  44. Much  of the following information is not documented in  the  DOS 
  45. Technical Reference manual.
  46.  
  47. Under  DOS 1.0 and 1.1,  all files were accessed via File Control 
  48. Blocks (FCB's).  There was no limit to the number of FCB's that a 
  49. program could use,  so there was no limit to the number of  files 
  50. open simultaneously.
  51.  
  52. Under  DOS 2.0 and greater,  an alternate (and preferable) method 
  53. of  accessing  files was introduced which uses a  2-byte  integer 
  54. called  a  "handle"  to refer to a  file.   A  "handle"  file  is 
  55. described  using  a data structure called a Device Control  Block 
  56. (DCB).   However,  DOS provides the storage space for all  DCB's, 
  57. rather  than having the application program store the  DCB's,  so 
  58. the  number of available DCB's is limited to the amount of  space 
  59. that  DOS  has  set  aside  for  them.   The  maximum  number  of 
  60. files/devices  that can be open simultaneously is the  number  of 
  61. slots  available in the DCB Table created by DOS.   The DCB's  in 
  62. the DCB Table are consecutively numbered, starting with 0.  DCB's 
  63. 0,  1, and 2 are predefined by DOS to correspond to the AUX, CON, 
  64. and  PRN devices,  respectively.   All remaining DCB's in the DCB 
  65. Table  are  available for files or devices  used  by  application 
  66. programs.
  67.  
  68. So that I/O redirection can be supported, the DCB numbers are not 
  69. used directly when accessing files.   Instead, a file "handle" is 
  70.  
  71. Handle  Table,  which  is  located at offset 18H of  the  Program 
  72. Segment  Prefix (PSP) for a program (for a general discussion  of 
  73. the PSP,  see the DOS Technical Reference manual).   Each element 
  74. of the Handle Table is the DCB number of a file or  device.   The 
  75. value  at index "handle" in the Handle Table is the DCB number of 
  76. the file or device that implements that file  handle.   Thus,  if 
  77. the  value 8 is in the 6th byte of the Handle Table,  the  handle 
  78. "6" refers to the file (or device) described by the DCB in slot 8 
  79. of the DCB Table.   If a handle is not currently being used,  its 
  80. entry  in  the Handle Table is FFH.   DOS predefines the first  5 
  81. handles to be primary input,  primary output,  error,  auxiliary, 
  82.  
  83.  
  84. and printer, so the first 5 entries in the Handle Table are 1, 1, 
  85. 1,  0,  and 2,  corresponding to the DCB numbers for the CON (1), 
  86. AUX  (0),  and PRN (2) devices.   This leaves only  15  available 
  87. handles for opening files (or new devices).
  88.  
  89. Every time a new handle file is opened,  a new handle gets  used.  
  90. Since there are only 20 slots available in the Handle Table for a 
  91. program, DOS only allows a "process" to have a maximum of 20 file 
  92. handles  in  use  simultaneously  (and the first  5  entries  are 
  93. predefined,  as just noted,  unless those handles get closed  and 
  94. reused).  Every new handle file requires a unique handle, so only 
  95. 20 files/devices can be open at the same time by a single process 
  96. (unless  FCB's  are used).   (A "process" is any program  spawned 
  97. using  the DOS EXEC function call.   A process can be invoked  by 
  98. COMMAND.COM, or by another program.)  There can be many more than 
  99. 20 DCB's in the DCB Table,  so the real limitation is in the size 
  100. of the Handle Table in the PSP.
  101.  
  102. The  size  of  the  DCB  Table  (i.e.,   the  maximum  number  of 
  103. files/devices  that  can  be open  simultaneously  in  the  whole 
  104. computer)  is controlled by the FILES=XXX entry in the CONFIG.SYS 
  105. file.   The minimum number of slots is 8.  Under DOS 2.0/2.1, the 
  106. maximum number is 99,  and under DOS 3.0 and higher,  the maximum 
  107. is 255.   As previously mentioned,  the first three of these  DCB 
  108. slots are occupied by the AUX, CON, and PRN devices.
  109.  
  110. A  single  program  can  use all of the DCB's in  the  DCB  Table 
  111. (except for the 3 reserved by DOS) all on its own, by effectively 
  112. bypassing  the  Handle Table in the PSP,  except on  a  temporary 
  113. basis.   The program can accomplish this feat by using, say, only 
  114. one entry in the Handle Table for all of its files.   Instead  of 
  115. allowing  DOS to store the DCB numbers in the Handle  Table,  the 
  116. program can store these numbers elsewhere.  Then, to manipulate a 
  117. file using DOS, the program can temporarily put the DCB number of 
  118. that  file into a designated slot in the Handle Table,  pass  the 
  119.  
  120. will operate on that handle/DCB number.   After the DOS call, the 
  121. program  can  remove that DCB number from the  designated  Handle 
  122. Table  slot,  freeing up that handle for use in another DOS  call 
  123. for another file.   In this way, DOS can be fooled into accessing 
  124. up  to 96 (or 252) different files/devices using a single  handle 
  125. entry in the Handle Table.
  126.  
  127. The  OpenExtend,  UnExtend,  and ReExtend routines below use this  
  128. technique.  OpenExtend(f)  is used on a  previously-opened  file, 
  129. "f."  It removes f's DCB number from the Handle Table, and stores 
  130. that  DCB  number  in place of the file handle  in  Turbo's  data 
  131. structure for "f."  UnExtend(f) copies the current DCB number (if 
  132. any) in the last slot of the Handle Table to a safe place, copies 
  133. the  DCB number of "f" to that slot,  and then puts the handle of 
  134. that  slot into the "handle" position of the data  structure  for 
  135. "f,"  in  preparation  for its  use  by  Turbo/DOS.   ReExtend(f) 
  136. replaces  the  file handle of "f" with the DCB number for "f"  in 
  137. the  data structure for "f," and restores the previous value  (if 
  138. any)  of the last slot in the Handle Table.   Note that the  last 
  139. slot  of the Handle Table was chosen  totally  arbitrarily.   Any 
  140. H  Table  entry can be used for fooling DOS,  and (since  we  are 
  141. saving  the  previous  DCB  number in that  entry)  it  does  not 
  142. preclude also using that designated slot as a legitimate handle.
  143.  
  144. To obtain the address of the Handle Table, which is at offset 18H 
  145. in  the PSP,  the program needs to find the address of  its  PSP.  
  146. Normally,  this  is very easy:  when DOS loads a .COM  file,  the 
  147. address of the PSP is just CS:0000.  Using CS:0000 in this manner 
  148. would  be viable as long as we compile the program to a .COM file 
  149. and  execute the .COM file.   However,  if we run the program  in 
  150. DOS  still thinks of Turbo's PSP as being the "official" PSP  for 
  151. the  running program,  since DOS did not "see" Turbo  invoke  the 
  152. program.   Hence,  CS:0000  is not the valid PSP address when the 
  153. program  is running in memory,  since that is the address of  the 
  154. program's "fake" PSP rather than Turbo's PSP.
  155.  
  156. To  allow  the  program to work correctly both  when  running  in 
  157. memory and when run as a .COM file,  we use the DOS function call 
  158. 62H,  "Get Program Segment Prefix  Address (PSP)."  This function 
  159. call is available in DOS 3.0 and higher.   There is an  identical 
  160. function call in DOS 2.0/2.1, but its function number is 51H, and 
  161. it  is  not documented.   Function 51H is also available  in  DOS 
  162. 3.0/3.1.  However,  for  upward-compatibility reasons with future 
  163. versions of DOS,  we will use the undocumented 51H function  with 
  164. DOS  2.0/2.1 (since we know 51H is available in those versions of 
  165. DOS),  and  use  62H  for  DOS 3.0 and higher  (since  62H  is  a 
  166. documented  function).   There  is no such function call  in  DOS 
  167. 1.0/1.1,  but the technique below will not work with those  early 
  168.  
  169.  
  170. To  decide  whether  to  use function 51H or  62H,  we  call  DOS 
  171. function  30H,  "Get  DOS  Version Number,"  to  determine  which 
  172. version  of  DOS is running.   This strategy  for  obtaining  the 
  173. Handle  Table  address is implemented in  the  GetHandleTableAddr 
  174. function, below, which gets called only once (the first time that 
  175. OpenExtend is called).
  176.  
  177. Note:  This  technique  will not interfere with overlays in  your 
  178. program  (since it only uses the Handle Table slot  temporarily), 
  179. provided that your program leaves at least one DCB available  for 
  180. use by the Turbo run-time library to read in overlay files.
  181.  
  182. {$F252}
  183.  
  184. const
  185.   LastHandle = 19;    {Highest-numbered handle}
  186.   UnusedHandle = $FF; {DcbTable entry that denotes an unused handle}
  187. type
  188.   HandleTable = array[0..LastHandle] of Byte;
  189.   HandleTablePtr = ^HandleTable;
  190. const
  191.   TablePtrOk: Boolean = false; {"True" iff TablePtr is initialized}
  192. var
  193.   TablePtr: HandleTablePtr; {Points to Handle Table for this process}
  194.   SaveDcb:  Byte;  {Temporary variable for a DCB number during  a 
  195.                    function call} 
  196.  
  197. {Internal  routine.   Returns  the address of the  Handle  Table, 
  198. which is at offset 18H in the PSP.}
  199.  
  200. function GetHandleTableAddr: HandleTablePtr;
  201. var
  202.   regs: record
  203.           case Integer of
  204.             1: (AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags: Integer);
  205.             2: (AL, AH, BL, BH, CL, CH, DL, DH: Byte)
  206.           end;
  207.  
  208.  
  209. begin
  210.   regs.AH := $30;
  211.   MsDos(regs);         {Get DOS version number}
  212.   case regs.AL of
  213.     0: begin
  214.           writeln('This program only works with DOS 2.0 and higher');
  215.           Halt
  216.        end;
  217.     2: regs.AH := $51; {Undocumented, but works with DOS 2.0/2.1 (and 3.X)}
  218.   else regs.AH := $62  {Works with DOS 3.0 and higher}
  219.   end;
  220.   MsDos(regs);         {Get PSP address}
  221.   GetHandleTableAddr := Ptr(regs.BX, $18)
  222. end {GetHandleTableAddr};
  223.  
  224.  
  225. {Causes  "f" to become an "extended" file;  i.e.,  to remain open 
  226. without using up any file handles.  The parameter "f" must be any 
  227. Turbo file;  e.g.,  a File,  a File of Byte, a File of Foo, Text, 
  228. etc.   This routine should be called immediately after the  Reset 
  229. or  Rewrite  that  is initially used to open "f." After  "f"  has 
  230. become  extended,  it  cannot be used by  Turbo's  built-in  file 
  231. routines until it has been unextended using UnExtend.}
  232.  
  233. procedure OpenExtend (var f);
  234. var
  235.   handle: Integer absolute f;
  236. begin
  237.   if not TablePtrOk then
  238.     begin
  239.       TablePtr := GetHandleTableAddr;
  240.       TablePtrOk := true
  241.     end;
  242.   SaveDcb := TablePtr^[handle];
  243.   TablePtr^[handle] := UnusedHandle;
  244.   handle := SaveDcb
  245. end {OpenExtend};
  246.  
  247.  
  248. {Unextends  the extended file "f," so that it can be used by  any 
  249. of Turbo's built-in file routines.   Note that "f" must have been 
  250. converted  to  an  extended file by  OpenExtend  before  invoking 
  251. UnExtend(f).  After calling UnExtend, and then invoking the Turbo 
  252. file  function on "f," ReExtend(f) should be invoked  immediately 
  253. to re-extend "f" and restore the DOS file state information.}
  254.  
  255.  
  256. procedure UnExtend (var f);
  257. var
  258.   handle: Integer absolute f;
  259. begin
  260.   SaveDcb := TablePtr^[LastHandle];
  261.   TablePtr^[LastHandle] := Lo(handle);
  262.   handle := LastHandle
  263. end {UnExtend};
  264.  
  265.  
  266. {Re-extends  "f" into an extended file.   Note that "f" must have 
  267. been  converted  to  an extended file  by  OpenExtend,  and  then 
  268. unextended  using  UnExtend,  before "f"  can  re-extended  using 
  269. ReExtend.   ReExtend(f)  should be invoked immediately after  any 
  270. normal Turbo file function call using "f."}
  271.  
  272. procedure ReExtend (var f);
  273. var
  274.   handle: Integer absolute f;
  275. begin
  276.   handle := TablePtr^[LastHandle];
  277.   TablePtr^[LastHandle] := SaveDcb
  278. end {ReExtend};
  279.  
  280.  
  281.  
  282. {Example  program -- This program opens as many Text files as  it 
  283. can,  until  DOS  runs out of room in its  DCB  Table.   It  then 
  284. reports how many files were successfully opened, writes a line to 
  285. each  of them,  then closes and erases each of them.   Note:  The 
  286. value  of the FILES=XXX parameter in the CONFIG.SYS file must  be 
  287. set  to  an appropriate value (see above).   If  you  change  the 
  288. FILES=XXX value, be sure to reboot before running this program.
  289.  
  290. This program takes a while to run,  due to the heavy disk I/O, so 
  291. running  it  on  a hard disk (or,  even better,  a RAM  disk)  is 
  292. recommended.   Make  sure that you are running the program  in  a 
  293. subdirectory,  so  that you don't run up against the DOS limit on 
  294. the number of allowable files in the root directory of a drive.}
  295.  
  296.  
  297. const
  298.   MaxCount = 255;
  299. var
  300.   num: string[6];
  301.   f: array[1..MaxCount] of Text;
  302.   i, count: Integer;
  303.   result: Byte;
  304. begin
  305.   writeln('Opening files...');
  306.   i := 0;
  307.   repeat
  308.     i := i + 1;
  309.     Str(i, num);
  310.     Assign(f[i], 'junk' + num + '.txt');
  311.     {$I-} Rewrite(f[i]); {$I+}
  312.     result := IOResult;
  313.     if result = 0 then OpenExtend(f[i])
  314.   until result <> 0;
  315.   count := i - 1;
  316.   writeln('Successfully opened ', count, ' files at the same time.  ',
  317.           'Writing to each file...');
  318.   for i := 1 to count do
  319.     begin
  320.       UnExtend(f[i]);
  321.       writeln(f[i], 'This is a test');
  322.       ReExtend(f[i])
  323.     end;
  324.   writeln('Closing and erasing each file...');
  325.   for i := 1 to count do
  326.     begin
  327.       UnExtend(f[i]);
  328.       Close(f[i]);
  329.       Erase(f[i]);
  330.       ReExtend(f[i])
  331.     end;
  332.   writeln('Done.')
  333. end.
  334. (**)
  335.  
  336.