Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!shadooby!accuvax.nwu.edu!elvin!nucsrl!accuvax.nwu.edu!jln From: jln@accuvax.nwu.edu (John Norstad) Newsgroups: comp.sys.mac.programmer Subject: Toolbox Gotchas (LONG) Message-ID: <10050096@accuvax.nwu.edu> Date: 12 Apr 89 17:14:27 GMT Organization: Northwestern U, Evanston IL, USA Lines: 136 While devoping Disinfectant I ran into a number of "gotchas" that caused me great grief. I thought it would be nice to tell the rest of you about these problems, in the hope that you'll be able to avoid them in your own programs. I've told DTS about most of this stuff. Gotcha #1. Watch out for PBGetCatInfo calls with TOPS. The file manager routine PBGetCatInfo uses a parameter block of type CInfoPBRec. Make certain that you pass a pointer to the full parameter block when using MPW C, even if you know in advance that the object is a directory. Don't just allocate and pass a pointer to the DirInfo variant. The DirInfo variant is four bytes shorter than the full union type, and with TOPS the PBGetCatInfo call sets those four bytes at the end. If your parameter block is not big enough you'll trash the stack. Gotcha #2. Make certain you're in the proper heap zone before calling ReleaseResource. At the bottom of IM II-26, in the Memory Mangler chapter, is the warning "Be sure, when calling routines that access blocks, that the zone in which the block is located is the current zone." Heed this warning, especially when releasing resources. Bob Hablutzel and I discovered (after hours in Macsbug) that on the 128K ROMs, if you try to release an empty (unloaded) resource in the system heap, and if you neglect to set the current zone to the system zone, then the system will trash the free master pointer list. This is not good, and will almost undoubtably lead to subsequent bizarre behaviour. Here's the code I use to release a resource: curZone = GetZone(); SetZone(HandleZone(theRez)); ReleaseResource(theRez); SetZone(curZone); Gotcha #3. Don't believe Inside Macintosh. On page IM II-34 we read the following warning in the description of the HandleZone routine: "If handle h is empty (points to a NIL master pointer), HandleZone returns a pointer to the current heap zone." This is false - HandleZone properly returns a pointer to the heap zone that contains the master pointer. See Gotcha #2 above. Gotcha #4. Don't expect OpenResFile to do sanity checking. Neither OpenResFile nor OpenRFPerm does any sanity checking of any sort when opening a resource file. If the file is damaged or contains trash it is very possible for the Resource Mangler to bomb or hang inside the OpenResFile or OpenRFPerm call. Often what happens is that it makes a Memory Mangler request for some ridiculously huge block of memory. If you have a GrowZone proc this can cause problems. To prevent this problem you must write a sanity checker of your own that opens the resource file as a binary file and checks at least the most important structural characteristics of the file. If your sanity check fails you must avoid calling OpenResFile or OpenRFPerm on the file. In Disinfectant I check that the resource map and resource data are within the logical EOF of the file and don't overlap, I check that the resource type list immediately follows the resource map, and I check that the resource name list starts within the logical eof. DTS tells me that the only way to be completely safe is to do a complete sanity check of the entire resource fork - e.g., rewrite the RezDet MPW tool. Damaged and trashed resource forks are much more common than you might think. Gotcha #5. Don't believe Inside Macintosh. In the description of the OpenResFile routine, IM I-115 states "If the resource file is already open, it doesn't make it the current resource file; it simply returns the reference number." This is false. If the resource file is already open, OpenResFile in fact DOES make it the current resource file. OpenRFPerm also has the same behaviour, in those cases where OpenRFPerm returns the reference number of the previously opened copy of the file, rather than opening a new access path (see IM IV-17 and TN 185). Gotcha #6. Watch out for Standard File if you unmount volumes. The standard file package keeps track of the last volume it used in the low core global SFSaveDisk, which contains the negative of the vol ref num of the last volume used. If your program unmounts this volume and then later calls the standard file package again, it will post an alert saying that "A system error has occurred. Please try again." A simple fix for this problem is to check the vRefNum stored in SFSaveDisk immediately before any calls to standard file. Call PBGetVInfo to see if the volume still exists. If it doesn't, make an indexed call to PBGetVInfo to get the vRefNum of the first volume in the VCB queue, and set SFSaveDisk to the negative of this vRefNum. Also set CurDirStore to fsRtDirID. Gotcha #7. Don't believe Inside Macintosh. IM I-116 states that "When calling the CurResFile and HomeResFile routines, described below, be aware that for the system resource file the actual reference number is returned." This is false. CurResFile does indeed return the actual reference number of the system file (2), but HomeResFile in fact returns 0 for system file resources. Gotcha #8. Don't believe Inside Macintosh. IM I-126 states "Like the attributes of individual resources, resource file attributes are specified by bits in the low-order byte of a word." This is false. In fact, the resource file attributes are stored in the high-order byte of the word. Gotcha #9. Directory IDs are longs, not shorts, stupid. Directory IDs, unlike volume reference numbers and working directory ids, are longs, not shorts. Watch out for this one. It's really easy to declare a dirID to be a short by mistake, and unless you're using Modula-2 you probably won't catch the bug even with extensive beta testing. Don't feel too stupid if you do this - I have it on good authority that ResEdit once had this bug! Gotcha #10. Always set the ioNamePtr field in file manager param blocks. See TN 179. Read it. Believe it. Always set ioNamePtr. Set it to nil if you don't care about the name. I made this mistake three times while developing Disinfectant, and all three times it took FOREVER to find the bug. The problem is those silly little arrows in the file manager chapter of IM IV. They all point to the left for ioNamePtr, which usually means that you don't have to set the field before calling the routine. I hope my experiences help somebody. John Norstad Academic Computing and Network Services Northwestern University Bitnet: jln@nuacc Internet: jln@acns.nwu.edu AppleLink: a0173 CompuServe: 76666,573