unit openfiles; (* OPENFILES - Print list of all open files Written by D.J. Murdoch for the public domain. This unit interfaces three routines, which look in the (undocumented) DOS list of open files for the filenames. One routine prints a list of open files, another returns the list in a collection of strings, and the third calls a user routine once for each open file. If compiled for DOS, it automatically installs an exit handler to call the print routine, so if your program bombs because it runs out of file handles, you'll see the list of what's open. I've tested this unit in MSDOS 3.2, 4.01, 5 and 6; it should work in the other versions from 2 to 6, but I'd like to hear from you if it doesn't. Fidonet: DJ Murdoch at 1:249/99.5 Internet: dmurdoch@mast.queensu.ca CIS: 71631,122 History: 1. 21 Oct 91 - First release to PDN echo. 2. 26 Oct 91 - Added check of PSP segment, and DOS 3.0 record format. Set Allfiles to true to get previous behaviour. 3. 24 Jun 93 - Added DOS 6 and DPMI support 4. 24 Aug 94 - Added BP 7 Windows support, a bit more flexibility in ways to call Thanks are due to Berend de Boer for a series of articles explaining how to make real mode interrupt calls from protected mode. His hints let me add the DPMI and Windows support. *) {#Z+ Don't add these comments to the help file } interface uses {$ifdef windows} {$ifdef ver15} wobjects,winprocs,win31,windos; { For TPW 1.5 } {$else} objects,winapi,windos; { For BP 7 Windows. } {$endif} {$else} {$ifdef dpmi} winapi, { For BP 7 pmode } {$endif} objects,dos; { For BP 7 DOS } {$endif} {#Z-} const version = 4; Allfiles : boolean = false; { Whether to print files belonging to other processes } procedure print_open_files(var where:text); { Print open file list to given file } function get_open_files:PCollection; { Returns a new collection containing pointers to strings holding the filenames. Note that you'll need to use DisposeStr on each element to release them. } procedure For_each_open_file(Action:pointer); { Calls the far local procedure Action once per open file. Action should be declared as procedure Action(filename:string;openmode:word); far; if it's a local procedure, or procedure Action(filename:string; openmode,dummy:word); far; if not. (Local procedures are procedures defined within other procedures.) The filename will be the name of the file (no path), the openmode will be the mode used to open the file. } implementation {$ifdef windows} {$define dpmi} { Everything else about Windows is the same as DPMI } {$endif} type ptrrec = record ofs, seg : word; end; var MyPrefixSeg : word; {$ifdef dpmi} { This type was given by Berend de Boer, who credited the DPMI unit from Borland's Open Architecture book } type TRealModeRegs = record case Integer of 0: ( EDI, ESI, EBP, EXX, EBX, EDX, ECX, EAX: Longint; Flags, ES, DS, FS, GS, IP, CS, SP, SS: Word); 1: ( DI,DIH, SI, SIH, BP, BPH, XX, XXH: Word; case Integer of 0: ( BX, BXH, DX, DXH, CX, CXH, AX, AXH: Word); 1: ( BL, BH, BLH, BHH, DL, DH, DLH, DHH, CL, CH, CLH, CHH, AL, AH, ALH, AHH: Byte)); end; function MakePointer(seg,ofs:word):pointer; var sel,junk : word; begin sel := AllocSelector(Dseg); { !!4 Copy Dseg attributes } sel := SetSelectorBase(sel, longint(16)*seg); if sel <> 0 then begin junk := SetSelectorLimit(sel, $ffff); MakePointer := Ptr(sel,ofs); end else MakePointer := nil; end; procedure ReleasePointer(p:pointer); var junk : word; begin junk := FreeSelector(ptrrec(p).seg); end; procedure RealModeInterrupt(int:byte;var regs:TRealModeRegs); label okay; begin asm mov ax,$0300 mov bl,int mov bh,0 mov cx,0 les di,regs int $31 jnc okay end; writeln('Real mode call failed!'); okay: end; function GetListOfLists:pointer; { Calls DOS service $52 to get pointer to list of lists, and translates pointer to a pmode pointer } var regs : TRealModeRegs; begin fillchar(regs,sizeof(regs),0); regs.ah := $52; RealModeInterrupt($21,regs); GetListOfLists := MakePointer(regs.es,regs.bx); end; procedure GetPrefixSeg; { Stores real mode segment of the PSP in MyPrefixSeg} begin MyPrefixSeg := GetSelectorBase(system.prefixseg) div 16; end; {$else} function MakePointer(seg,ofs:word):pointer; begin MakePointer := Ptr(seg,ofs); end; procedure ReleasePointer(p:pointer); begin end; function GetListOfLists:pointer; var regs : Registers; begin fillchar(regs,sizeof(regs),0); regs.ah := $52; msdos(regs); GetListOfLists := MakePointer(regs.es,regs.bx); end; procedure GetPrefixSeg; begin MyPrefixSeg := PrefixSeg; end; {$endif} type dos2openfilerec = record numhandles, openmode : byte; junk1 : array[2..3] of byte; filename : array[4..$e] of char; junk2 : array[$f..$27] of byte; end; dos30openfilerec = record {!!2} numhandles, openmode : word; junk1 : array[4..$20] of byte; {!!2} filename : array[$21..$2b] of char; {!!2} junk2 : array[$2c..$31] of byte; {!!2} pspseg : word; {!!2} junk3 : array[$34..$37] of byte; {!!2} end; dos3openfilerec = record numhandles, openmode : word; junk1 : array[4..$1f] of byte; filename : array[$20..$2a] of char; junk2 : array[$2b..$30] of byte; {!!2} pspseg : word; {!!2} junk3 : array[$33..$34] of byte; {!!2} end; dos4openfilerec = record numhandles, openmode : word; junk1 : array[4..$1f] of byte; filename : array[$20..$2a] of char; junk2 : array[$2b..$30] of byte; {!!2} pspseg : word; {!!2} junk3 : array[$33..$3a] of byte; {!!2} end; filelistptr = ^filelistrec; filelistrec = record next : filelistptr; numfiles : word; case byte of 2 : (dos2files : array[1..1] of dos2openfilerec); 30 : (dos30files: array[1..1] of dos30openfilerec); {!!2} 3 : (dos3files : array[1..1] of dos3openfilerec); 4 : (dos4files : array[1..1] of dos4openfilerec); end; Tfilename = String[12]; function NiceName(filename:TFilename):TFilename; var result : string; blankpos : byte; begin result := filename; insert('.',result,9); repeat blankpos := pos(' ',result); if blankpos > 0 then delete(result,blankpos,1); until blankpos = 0; NiceName := result; end; procedure WalkList(var where:text;C:PCollection;Action:pointer;frame:word); procedure Doit(filename:TFilename;openmode:word); var DoAction : procedure(f:string;openmode:word;dummy:word) absolute Action; begin filename := NiceName(filename); if C <> Nil then C^.Insert(NewStr(filename)) else if Action <> Nil then DoAction(filename,openmode,frame) else writeln(where,filename); end; var p : pointer; list : filelistptr; i : word; begin GetPrefixSeg; {!!3} p := GetListOfLists; {!!3} inc(longint(p),4); {!!3} if ptrrec(p^).ofs <> $ffff then list := MakePointer(ptrrec(p^).seg,ptrrec(p^).ofs) {!!3} else list := nil; releasePointer(p); {!!3} while list <> nil do begin with list^ do for i:=1 to numfiles do case lo(dosversion) of 2 : with dos2files[i] do if numhandles > 0 then doit(filename,openmode); {!!4} 3 : if hi(dosversion) = 0 then {!!2} begin {!!2} with dos30files[i] do {!!2} if (numhandles > 0) and (allfiles or {!!2} (pspseg = myprefixseg)) then{!!3} doit(filename,openmode) {!!4} end {!!2} else {!!2} with dos3files[i] do if (numhandles > 0) and (allfiles or (pspseg = myprefixseg)) then{!!3} doit(filename,openmode); {!!4} 4..6 : with dos4files[i] do if (numhandles > 0) and (allfiles or {!!2} (pspseg = myprefixseg)) then {!!3} doit(filename,openmode); {!!4} end; p := list; if ptrrec(list^.next).ofs <> $ffff then list := MakePointer(ptrrec(list^.next).seg,ptrrec(list^.next).ofs) {!!3} else list := nil; ReleasePointer(p); {!!3} end; ReleasePointer(list); {!!3} end; procedure print_open_files(var where:text); { Print open file list to given file } begin WalkList(where,nil,nil,0); end; function get_open_files:PCollection; { Returns a new collection containing pointers to strings holding the filenames } var result : PCollection; junk : text; begin result := New(PCollection,init(16,16)); if result <> nil then WalkList(junk,result,nil,0); get_open_files := result; end; function CallerFrame:word; Inline( $8B/$46/$00/ { MOV AX,[BP]} $24/$FE); { AND AL,$0FE} procedure For_each_open_file(Action:pointer); var junk : text; begin WalkList(junk,nil,Action,CallerFrame); end; {$ifndef windows} { We don't use an exitproc in Windows} var exit_save : pointer; procedure my_exit_proc; far; var junk : word; begin ExitProc := Exit_save; junk := ioresult; assign(output,''); rewrite(output); writeln('Files open as program terminates:'); print_open_files(output); end; {$endif} begin if not (lo(dosversion) in [2..6]) then writeln('OPENFILES only works with DOS 2 to 6') {$ifndef Windows} else begin exit_save := ExitProc; ExitProc := @my_exit_proc; end {$endif} end. { ------------------ DEMO PROGRAM ----------------------- } program test; { Test program for Openfiles unit. Should be compilable in TP/BP 6+, TPW 1.5+ } uses {$ifdef windows} {$ifdef ver15} wincrt,wobjects,openfiles; {$else} wincrt,objects,openfiles; {$endif} {$else} objects,openfiles; {$endif} { This routine uses the callback function "for_each_open_file". It's the only way to get the file open mode. } procedure doit(prefix:string); procedure printone(f:string;openmode:word); far; begin writeln(prefix,f:12,' mode ',openmode); end; begin for_each_open_file(@printone); end; { This routine builds the collection of strings and prints it } procedure doit2(prefix:string); var c:Pcollection; { Print each filename } procedure printone(f:PString); far; begin writeln(prefix,f^); end; { Release each string } procedure disposeone(f:PString); far; begin DisposeStr(f); end; begin c:=get_open_files; if c <> nil then begin c^.foreach(@printone); { This shows the proper way to dispose of the collection } c^.foreach(@disposeone); c^.deleteall; dispose(c,done); end; end; var f:file; i : longint; begin assign(f,'test.pas'); reset(f); allfiles := true; doit('Open by some process: '); allfiles := false; doit2('Open by us: '); { At the end, the exitproc will print one more list (in DOS). } end.