[comp.sys.mac] The final word on sublaunching

jww@sdcsvax.UCSD.EDU (Joel West) (05/21/87)

[ We had so much discussion of launching and sublaunching that, now that
  this is available, I thought I'd pass it along -- jww]

________________________________________________________________________________
Macintosh Technical Notes

#126: Sublaunching: playing the Shell game

See also:	Segment Loader

Written by:	Rick Blair 	May 4, 1987
________________________________________________________________________________

Note: Macintosh Technical Support takes the view that this is a feature
which is best left out for compatibility (and other) reasons, but we want to
make sure that when it is absolutely necessary to implement it,  it is done in
the safest way.

Herein is a means to launch an application from your program and have it
return to you as though you were a Rshell,S like the Finder. There are
unresolved issues, though (and some downright problems), so please read
the cautionary notes which follow.
________________________________________________________________________________


Warning

The interface to the Launch trap will change in the not-too-distant future. When that
happens, programs which launch other applications will break. You should really only
consider doing this if you are implementing an integrated development system.


The Finder does a lot of hidden cleanups and other tasks of which the user isn't aware.
Therefore it is best if you don't try to replace the Finder with a "mini," or try to launch
other programs and have them return to your application. In the future the Finder may
provide better integration for applications and you would circumvent this if you tried to
take over its role.



Nevertheless, consider a text editor that wants to allow the user to run, say, ResEdit,
and then return to program editing. If it isn't worried about the transition to new
environments, then it would want to do this in a way that would fit into the current system
well. System file version 4.1 (or higher) includes a mechanism for allowing a call to
another application which we term a sublaunch. This is accomplished with a set of
simple extensions to the parameter block which is passed to the Launch trap.





A Sublaunch from Pascal 

	{It is assumed that the Signals are caught elsewhere; see Technical
	 Note #88 for more information on the Signal mechanism}

	{the extended parameter block to _Launch}
	TYPE
   	  pLaunchStruct = ^LaunchStruct;
  	  LaunchStruct = record
            pfName: ^Str255;
            param: INTEGER;
	       LC:PACKED ARRAY[0..1] OF CHAR; {start of extended parameter block}
	       ExtBlockLen: LONGINT; {number of bytes in extension = 6}
	       fFlags: INTEGER; {Finder file info flags (see below)}
	       LaunchFlags: LONGINT; {bit 31=1 for sublaunch, other bits reserved}
 	   End;  {LaunchStruct}
	VAR
  	       pMyLaunch: pLaunchStruct;
  	       myLaunch:  LaunchStruct;
  	       fName: Str255;

	PROCEDURE LaunchIt(pLnch: pLaunchStruct);INLINE $205F, $A9F2; 
	 { pops pointer into A0 and calls Launch }

	PROCEDURE DoLaunch;
	VAR
	  wher: Point;          { where to display dialog }
	  reply: SFReply;         { reply record }
	  myFileTypes: SFTypeList;      { we won't actually use this }
	  NumFileTypes: integer;
	  myPB: CInfoPBRec;
	  DirNameStr: str255;
	BEGIN
	   wher.h := 20;
	   wher.v := 20;
	   NumFileTypes:= -1;  {Display all files}

	{ Let the user choose the file to Launch }
	   SFGetFile (wher, '', Nil, NumFileTypes, MyFileTypes, NIL, reply);

	   IF reply.good THEN begin
	    DirNameStr:= reply.fName;  {initialize to file selected}
	    with MyPB do Begin
	          ioCompletion:= NIL;
	          ioNamePtr:= @DirNameStr;
	          ioVRefNum:= reply.vRefNum;
	          ioFDirIndex:= 0;
	          ioDrDirID:=0
	    End;  {with MyPB}
	    
	{ Get the Finder flags }
	   Signal(PBGetCatInfo(@MyPB,FALSE));
	
	{ Set the current volume to where the target application is }
	   Signal(SetVol(NIL, reply.vRefNum));
	 
	   pMyLaunch:= @myLaunch;
	   fName:= reply.fName; 
	   With pMyLaunch^ do Begin
	         pfName:= @fName; {pointer to our fileName}
	         param:= 0; {we don't want alternate screen or sound buffers}
	         LC := 'LC'; {here to tell Launch that there is non-junk next}
	         ExtBlockLen := 6; {length of param. block past this long word}
	      {copy flags; set bit 6 of low byte to 1 for RO access:}
	         fFlags := MyPB.ioFlFndrInfo.fdFlags; {from GetCatInfo}
	         LaunchFlags := $80000000; {set hi bit to indicate a sublaunch}
	   End;  {With}
	   Launchit(pMyLaunch);	{do the actual launch}
	 end; {IF reply.good}
	End; {DoLaunch}

Working directories

Putting aside the compatibility issue for the moment, the only problem this creates
under the current system is one of Working Directory Control Blocks, or WDCBs. 
Unless the application you are launching is at the root or on an MFS volume, a new
WDCB must be created so that it may be set as the current directory when the program
is run.

In the example procedure above, the new working directory is opened (allocated) by
Standard File and its WDRefNum is returned in reply.vRefNum. If you weren't using
Standard File and couldn't assume, for instance, that the application was in the blessed
folder or root then you would have to open a new working directory explicitly via
OpenWD. The new WDCB should have a WDProcID of 'ERIK' so that the Finder or
another shell that saw that the WDCB had been allocated by a RsublauncheeS would
know to de-allocate it.

The sublaunching process is recursive; you may sublaunch a program which then
sublaunches another, and so on, and when each application exits it will return to the
one that called it. The problem is that there is a limit to the number of WDCBs that can
be created; currently (and probably forever) the limit is 40. You can see how quickly
these might be used up if many programs were playing the shell game or neglecting to
de-allocate WDCBs they had created.

The ideal thing for a truly friendly shell to do would be to make a list of all 'ERIK' WDCBs
that were in existence when it first started running. It could do this by indexed calls to
GetWDInfo, and the only information it would have to save would be the WDRefNum of
each 'ERIK' block. When a program it had launched returned to it, it could once again
sequence through all the WDCBs and throw away any that weren't on the original list.
WDCBs with WDProcIDs other than 'ERIK' should be left alone.
-- 
	Joel West
	{ucbvax,ihnp4}!sdcsvax!jww	(ihnp4!gould9!joel if I ever fix news)
	jww@sdcsvax.ucsd.edu	if you must