domino@sunybcs (Michael Domino) (01/03/88)
In volume 2 of The Complete MacTutor, page 287 there is an article about creating WDEF routines. I would like to be able to do that, so I came up with the following code that doesn't work, using the article as a guide. If I fill in the ProcID field in ResEdit with one of the standard WDEFs, and comment out everything but the call to GetNewWindow,I get a window just fine. Can someone tell me what's wrong, please? This is all in Lightspeed C. main() { WindowPtr w; ...usual init stuff... /* The window is created invisible. */ w = GetNewWindow(windowID, NULL, (WindowPtr)(-1L)); (WindowPeek)w->windowDefProc = NewHandle(0L); *((WindowPeek)w->windowDefProc) = (Ptr)WindowDef; ShowWindow(w); ...event loop stuff... } /* I just want a simple window def right now, one that just draws a frame. */ pascal long WindowDef(varCode, theWindow, message, param) int varCode, message; WindowPtr theWindow; long param; { Rect r; switch(message) { case wDraw: r = (**((WindowPeek)theWindow->strucRgn)).rgnBBox; PenNormal(); FrameRect(&r); InsetRect(&r, 1, 1); EraseRect(&r); break; case wHit: break; case wCalcRgns: break; case wNew: break; case wDispose: break; case wGrow: break; case wDrawGIcon: break; } return 0L; } And while we're on the subject, it says in Inside Mac that the window definition id passed as a parameter to NewWindow (or set with ResEdit for GetNewWindow) has the resource id of the WDEF in the upper 12 bits and a variation code in the lower 4 bits. This does not seem to jive with the ids of the standard windows given on page 273 of IM, vol 1, which are all very small numbers. What's going on here? Michael Domino @ SUNY/Buffalo internet: domino@cs.buffalo.edu
lsr@apple.UUCP (Larry Rosenstein) (01/05/88)
In article <7595@sunybcs.UUCP> domino@sunybcs.UUCP (Michael Domino) writes: > >Can someone tell me what's wrong, please? This is all in Lightspeed C. > >main() >{ > WindowPtr w; > > ...usual init stuff... > > /* The window is created invisible. */ > w = GetNewWindow(windowID, NULL, (WindowPtr)(-1L)); > (WindowPeek)w->windowDefProc = NewHandle(0L); > *((WindowPeek)w->windowDefProc) = (Ptr)WindowDef; > ShowWindow(w); One problem is that this code trashes the heap. It is creating a handle and replacing the master pointer. The block that was created contains a back pointer to the master pointer, which the Memory Manager uses when the heap needs to be compacted. I mentioned this to the author of the article and the editor of MacTutor. Also, I believe there was a letter to the editor regarding this. Unfortunately, this had no effect because in the article on tear-off menus (a couple of months ago) the same technique was used. (I think this was the same author.) The correct thing to do is allocate a 6-byte handle and install a 68000 JMP instruction in the handle, which goes to the defproc. The first 2 bytes of such an instruction are $4EF9; the last 4 bytes should be the address of the defproc procedure. Then you simply install the handle in the window record. As you can see, doing the right thing doesn't take much extra code. In my opinion, it is purely a matter of laziness on the part of the author. His only response is to include a disclaimer saying that this technique shouldn't be used in a real program. The end effect is that people type in the program and it doesn't work. -- Larry Rosenstein Object Specialist Apple Computer AppleLink: Rosenstein1 UUCP: {sun, voder, nsc, mtxinu, dual}!apple!lsr CSNET: lsr@Apple.com
olson@endor.harvard.edu (Eric K. Olson) (01/05/88)
In a recent article Larry Rosenstein writes: >In article <7595@sunybcs.UUCP> domino@sunybcs.UUCP (Michael Domino) writes: >> ...usual init stuff... >> >> /* The window is created invisible. */ >> w = GetNewWindow(windowID, NULL, (WindowPtr)(-1L)); >> (WindowPeek)w->windowDefProc = NewHandle(0L); >> *((WindowPeek)w->windowDefProc) = (Ptr)WindowDef; >> ShowWindow(w); > >One problem is that this code trashes the heap. It is creating a handle and >replacing the master pointer. The block that was created contains a back >pointer to the master pointer, which the Memory Manager uses when the heap >needs to be compacted. > >[About how MacTutor has published this technique more than once] > >The correct thing to do is allocate a 6-byte handle and install a 68000 JMP >instruction in the handle, which goes to the defproc. The first 2 bytes of >such an instruction are $4EF9; the last 4 bytes should be the address of the >defproc procedure. Then you simply install the handle in the window record. While writing a WDEF I got sick of repeatedly compiling it into a resource and came up with another method for testing code fragments that need to be referenced as handles. I believe it will not trash the heap, and is correct (if somewhat unorthadox): If you have a compiler that can specify code segments (LSC, for example), move the WDEF code into a segment of its own and do a GetResource('CODE',id) and a DetachResource of the returned handle. This gives you a private copy of the code, referenced as a handle. Detaching it ensures that it will not be mucked with by the segment loader or resource manager. You may also need to HNoPurge the handle, depending on how it is flagged in the resource file. This works well for me, and I can't see a reason why it would screw anything up. On the other hand, I would never do such a thing after the WDEF is done being developed, since the WDEF resource/ProcId Window Field combo works so very well. -Eric (defun maybe (x) (maybe (not x))) Eric K. Olson olson@endor.harvard.edu harvard!endor!olson D0760 (Name) (ArpaNet) (UseNet) (AppleLink)
lsr@apple.UUCP (Larry Rosenstein) (01/06/88)
In article <3709@husc6.harvard.edu> olson@endor.UUCP (Eric K. Olson) writes: > >If you have a compiler that can specify code segments (LSC, for example), >move the WDEF code into a segment of its own and do a GetResource('CODE',id) >and a DetachResource of the returned handle. This gives you a private >copy of the code, referenced as a handle. Detaching it ensures that it will >not be mucked with by the segment loader or resource manager. You may also >need to HNoPurge the handle, depending on how it is flagged in the resource >file. There is supposed to be a 4 byte segment header in each CODE segment (indicating the location and number of jump table entries). Do you do something special to get around this? It is possible that for your programs, these 4 bytes ended up being 68000 instructions that don't do anything harmful. (For example, small 2-byte numbers are OR.B instructions.) As long as you don't have too many jump table entries, it should be benign. Ignoring this problem, you should not have to detach the segment. The Window Manager calls LoadResource to ensure that the WDEF is loaded into memory before calling it, so even if the segment is purged everything will work. If you DO detach it, then you will have to call HNOPurge, because it won't be a resource and LoadResource won't do anything with it. In MPW, it would be better to read the resource in by name, since then you don't have to depend on the ID remaining constant. The MPW linker, by default, names the resource the same as the segment. -- Larry Rosenstein Object Specialist Apple Computer AppleLink: Rosenstein1 UUCP: {sun, voder, nsc, mtxinu, dual}!apple!lsr CSNET: lsr@Apple.com
olson@endor.harvard.edu (Eric K. Olson) (01/06/88)
Folks, I must apoligize: In a recent article Larry Rosenstein writes: >In article <3709@husc6.harvard.edu> olson@endor.UUCP (Eric K. Olson) writes: >> >>If you have a compiler that can specify code segments (LSC, for example), >>move the WDEF code into a segment of its own and do a GetResource('CODE',id) >>and a DetachResource of the returned handle. This gives you a private >>copy of the code, referenced as a handle. Detaching it ensures that it will >>not be mucked with by the segment loader or resource manager. You may also >>need to HNoPurge the handle, depending on how it is flagged in the resource >>file. I'm not sure what drugs I was on when I wrote this. I may have tried it, but it certainly didn't work. The reason below is good enough for me: >There is supposed to be a 4 byte segment header in each CODE segment >(indicating the location and number of jump table entries). Do you do >something special to get around this? No, I probably gave up on that idea when it crashed.... In fact, I wasn't even testing a WDEF when I tried to incorporate a definition procedure's code into the test application. I was writing a CDEF!!! (I wrote a WDEF just before the CDEF, so you can understand my confusion with myself). [BTW, CDEFs should be tested the same way as WDEFs-- the way Larry originally suggested, with a 6-byte jump-to-the- correct-spot handle]. I have erred. In correcting the correct, I have erred twice. In not recognizing my error, I have erred three times. I must apply my prime directive. Sterilize. Sterilize. Ste...ri...lizzzzz..... [Star Trek] In fact, what I did is this [incorrect technique-- don't use this]: GetDItem(theDlg,1,&dType,&dHandle,&dRect); control=dHandle; cdefptr=&cdef; (*control)->contrlDefProc=&cdefptr; cdefptr is a global Ptr. This worked [really!], but not for any good reason. In fact, any memory manager calls applied to this "handle" (it's not a handle) will fail miserably. I just got lucky. *Sigh*. Sorry for the confusion, everybody. -Eric (defun maybe (x) (maybe (not x))) Eric K. Olson olson@endor.harvard.edu harvard!endor!olson D0760 (Name) (ArpaNet) (UseNet) (AppleLink)
earleh@eleazar.Dartmouth.EDU (Earle R. Horton) (01/09/88)
In article <3709@husc6.harvard.edu>, olson@endor.harvard.edu (Eric K. Olson) writes: > In a recent article Larry Rosenstein writes: > >The correct thing to do is allocate a 6-byte handle and install a 68000 JMP > >instruction in the handle, which goes to the defproc. The first 2 bytes of > >such an instruction are $4EF9; the last 4 bytes should be the address of the > >defproc procedure. Then you simply install the handle in the window record. I may be missing something here, but it seems to me the best way to test a resource containing code is to compile it into a resource right off the bat. Make a resource with 68000 code in it, with entry point at the top, flags, and whatever is needed in the final resource. At run time, you load the resource using GetResource(), and there is your handle, right in the function return value. I have to admit I haven't done 'WDEF's yet, but I load many types of code this way, and some even work right the very first time! I have even been known to extract a problem function from out of a large application, place it in a little code resource of its own for easier debugging and faster recompiling, and LEAVE it there once I finally got it running. Let me know if I'm off base here, but doing it right the first time seems to me to be the least amount of work. -- ********************************************************************* *Earle R. Horton, H.B. 8000, Dartmouth College, Hanover, NH 03755 * *********************************************************************