jln@accuvax.nwu.edu (John Norstad) (04/13/89)
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
bob@accuvax.nwu.edu (Bob Hablutzel) (04/13/89)
As a quick note to John's list of gotchas, let me just note that the gotcha #2, refering to releasing resources, also applies to disposing handles. If the handle is disposed in the wrong zone, the heap will be corrupted. Bob Hablutzel Wildwood Software BOB@NUACC.ACNS.NWU.EDU Disclaimer: Deep in the heart of ROM, it's every man for himself...
ra_robert@gsbacd.uchicago.edu (04/13/89)
Um...is there any chance that we might see Inside Mac, 2nd edition, from Apple one of these days? Shouldn't be too hard between one of the printings to go back and correct some of the misstatements. Just a suggestion... Robert ------ ra_robert@gsbacd.uchicago.edu ------ generic disclaimer: all my opinions are mine
ech@pegasus.ATT.COM (Edward C Horvath) (04/14/89)
From article <10050096@accuvax.nwu.edu>, by jln@accuvax.nwu.edu (John Norstad): > 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 #10. Always set the ioNamePtr field in file manager param blocks. Allow me to add my favorite foulup: Gotcha #11: Always set the ioMisc field in calls to PBOpen. Easiest thing here is to clear it. Failure to do so uses random locations in memory as the IO buffer for the file. This one is especially fun because the bomb is (a) guaranteed to hit sometime, and (b) always takes awhile, so you go nuts trying to find a stray pointer which your code never even referenced. Any more true confessions out there? This could be fun... =Ned Horvath=
alexis@ccnysci.UUCP (Alexis Rosen) (04/16/89)
John discusses the complete lack of sanity checking when you call OpenResFile. The Apple Answer is, rewrite the appropriate parts of RezDet and put them in your code. This is pretty terrible. As John says, damaged resource forks are not so rare. Crashing when you open a bad resource file is very very unfriendly. But having to write all that code to protect yourself is an awful lot of work. I can't think of many people outside of Apple, except maybe Steve Brecher, who could do this without extensive sessions with Inside Mac. This already makes that solution problematic. Of course the real problem is much worse. Apple specifically warns us, many times, that the format of the Resource Map will change in the future. It is my guess that such a change will come with System 7.0, and certainly no later than System 8.0. Guaranteeing that your application will crash on a future system just to protect yourself now is a miserable tradeoff. I suggest that Apple provide us with the tools to do this properly. There are two decent ways to do this that I can think of. First, the simplest way is to provide a trap that checks the file. The second, slightly more involved way is a little micer because it's more automatic. When you call OpenResFile, the first thing it does is write a dirty bit in the header somewhere. It then reads in the resource map. If everything is OK, the last thing is does before it returns is clear the dirty bit. When it opens a res file, before it writes the bit it reads it, and if it's set it knows that the last time it opened this resource fork it crashed. So it's a good bet that the res fork is damaged, and needs to be checked carefully. It would then call the sanity checker, and if it failed it would return an error. Anyone have any idea what the new resource manager is going to be like? --- Alexis Rosen alexis@ccnysci.{uucp,bitnet} alexis@rascal.ics.utexas.edu (last resort)
darin@Apple.COM (Darin Adler) (04/20/89)
In article <10050096@accuvax.nwu.edu> jln@accuvax.nwu.edu (John Norstad) writes: > 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. This is incorrect. Since the publication of Inside Macintosh Volume II, HandleZone has been *slightly* enhanced (along with the rest of the Memory Manager) to handle the system heap as a special case. The sentence could be correctly reworded: "If handle h is empty (points to a NIL master pointer), HandleZone returns a pointer to the current heap zone, unless the handle points somewhere within the system heap zone and the current heap zone is the application heap zone, in which case it returns a pointer to the system heap zone." This could return suprising results, since you can create a heap zone in the middle of the system heap (in a nonrelocatable or locked relocatable block), and HandleZone will return misleading values. This hack in the ROM (and in system patches) lets you be somewhat haphazard about what the current zone is as long as you stick mostly with the system heap and the application heap. -- Darin Adler, System Software Scapegoat, Apple Computer Internet: darin@Apple.com UUCP: {nsc,sun}!apple!darin
lsr@Apple.COM (Larry Rosenstein) (05/10/89)
Here are some others off the top of my head: * Be sure to set ioVersNum to 0 in the approrpiate paramter blocks, even if you call PBHOpen, etc. The description of PBHOpen doesn't mention this as a parameter, but a call to PBHOpen will be treated as a call to PBOpen on an MFS disk, and then the field must be 0. * Be aware of the Poor Man's Search Path. For example, when saving a file, don't call PBOpen or PBDelete to get rid of the existing file first. If the filename is the same as one in the system folder, you will get that file instead. You can disable the effect of the PMSP by setting an explicit dirID in the call. Also, note that Create doesn't use the PMSP, OpenResFile doesn't give you the option to set the dirID, and CreateResFile calls OpenResFile first. If you are creating a resource file, it is best to call Create first, to create the file, and then CreateResFile (see Tech Note 101; or use the MPW glue routine described in Tech Note 214.) * When creating a picture, always set the clip region to the minimum bounding box. When a picture is replayed and offset from its original position. All the coordinates in the picture are offset. If the clip region is the largest possible rectangle, it will be offset as well, and the coordinates will overflow resulting in an empty clipping. Larry Rosenstein, Apple Computer, Inc. Object Specialist Internet: lsr@Apple.com UUCP: {nsc, sun}!apple!lsr AppleLink: Rosenstein1