[fa.info-mac] Writting Desk Accessories

info-mac@uw-beaver (info-mac) (11/07/84)

From: Mike Schuster <MIKES@CIT-20.ARPA>
Since a desk accessory running within an application is much like the
Stranger in a Strange Land, here are a few things to consider when
writting desk accessories:

* Open routine

  The Open routine may be called more than once (e.g. if the
accessories' name in the Apple menu is pulled down when the accessory
is already open).  Hence, be sure to avoid creating new windows, etc,
on subsequent calls.

* Close routine

  The Close routine may never be called (e.g. if the application does
an ExitToShell without explicitly closing desk accessories).  Hence,
if you have some cleanup work to do (like closing files, etc) be sure
to set the dNeedGoodbye bit 12 in the drvrFlags word at the start of
the desk accessory.  Setting this bit will force a call to Close just
before the zone containing the accessory is disposed/initialized.

  When Close is called (do to a CloseDeskAcc call or a goodbye kiss),
the accessories' window may have been disposed.  Some applications
blindly close all windows when quitting (e.g. MacWrite and Multiplan).
So before disposing your window, check to see if it still exists.
Here is the fragment I use:

   w = FrontWindow();
   while (w && w != dce->dCtlWindow)
      w = w->nextWindow;
   if (w)
      DisposWindow(w);

The idea is to search down the chain of windows looking for the
accessories window (a pointer to which was stored in the accessories'
device control entry by the Open routine).

* Control routine

  The Control routine may be called reentrantly.  This is a suprise,
since normally the system sets the drvrActive flag in the device
control entry before calling a device control routine just to avoid
problems with reentrant calls.  Apparently, however, a desk accessory
is a special case, since this bit is not set.

  To see this, have your control routine open a modal dialog box.
When the dialog appears, the system generates a deactivate event for
the accessories' window and then calls the control routine passing
this event even though control is currently executing (e.g. waiting
for ModalDialog to return).

  Even if you write reentrant code, this senario can cause havoc.
For example, suppose your control routine locks down some private storage
just after entry and unlocks it just before exiting.  Then the original
control call in the above example returns from ModalDialog with private
storage unlocked!

  Furthermore, unless you set the dNeedLock bit in the drvrFlags word,
the system will unlock the accessories' code whenever one of the accessories'
routine exits.  Hence, if the heap got compacted while ModalDialog was
running (e.g. do to a SystemTask call to some other desk accessory which
allocated some memory), then the original control call in the above example
may return only to find that its code has slipped away, with all the
return addresses in the stack invalid!

  With these facts in mind, the control routine probably should maintain
a queue of all reentrant calls and process them itself in some controlled
manner.

  By the way, if you find all of this hard to believe, try this out:
Run MacWrite with MacsBugs installed and pull down the alarm clock.
Break to the debugger and starting with the low memory pointer to the
global device unit table, find the code for the alarm clock and insert
a trap to the debugger as the very first instruction in the alarm
clock's control routine.  (This isn't to hard).  Then do a go, and
wait for the trap (you won't wait long).  Now manually set the
drvrActive bit in the clocks' device control entry, and go.  You will
see that the clock now will not update its time.  Apparently, the
drvrActive flag is working.  Now, click on the clock's key.  The clock
will open up!  Hence, even setting drvrActive yourself is not good
enough.

* Resources

  Sometimes desk accessories need their own resources.  This can cause
problems when desk accessories are moved between system and archive files,
unless you are careful to copy all resources.  Apple's scheme for solving
this problem uses a special encoding of the resource ids to uniquely
match resources to desk accessories.  Each resource id owned by a desk
accessory must be of the following form:

   bits 14 and 15 must be 1
   bits 11, 12, and 13 must be 0
   bits 6, 7, 8, 9, and 10 contain the resource id of the desk
      accessories DRVR resource
   bits 0, 1, 2, 3, 4, and 5 contain any arbitrary value

Since the resource id of the DRVR resource may change when the accessory
is installed into the system file, the accessory must determine its id
at runtime and construct the ids of all required resources on the fly.
The id can be found as follows:

   DrvrId = (-dce->dCtlRefNum) - 1;

That is, if the DRVR resource id is 17, its dCtlRefNum is -18.

Mike Schuster
mikes@cit-20

-------