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 -------