Path: utzoo!attcan!uunet!world!decwrl!adobe!hawley From: hawley@adobe.COM (Steve Hawley) Newsgroups: comp.sys.mac.programmer Subject: Re: CDEF Woes... Message-ID: <11612@adobe.UUCP> Date: 26 Feb 91 17:51:41 GMT References: <1435@gertie.osc.edu> Reply-To: hawley@adobe.UUCP (Steve Hawley) Distribution: na Organization: Adobe Systems Incorporated, Mountain View Lines: 143 In article <1435@gertie.osc.edu> spencer@cgrg.ohio-state.edu (Stephen N. Spencer) writes: >SetupCDEF(win) > WindowPtr win; >{ > Rect r; > Handle h; > > h = NewHandle(h); > *h = (Ptr)MyControl; /* MyControl() is my CDEF 'main' function. */ > SetRect(&r, 160, 30, 400, 60); > myCH = NewControl(win, &r, "\p", TRUE, 0, 50, 100, scrollBarProc, 0L); > > HLock(myCH); > (*myCH)->contrlDefProc = myH; > HUnlock(myCH); >} > >This works, to a point. 'MyControl' intercepts all 'drawCntl' and 'testCntl' >messages passed through the control handle 'myCH', I'm glad to report, but >where it messes up is that I don't get any 'initCntl' message(s) at all. OK - the reason you aren't seeing the initCntl messages is because they get called at NewControl() time --before you have installed your procPtr into the contrlDefProc. This isn't surprising. BUT Beware! h = NewHandle(h); is dangerous. You are taking an uninitialized local variable and using its contents as the size argument to NewHandle. That's the same as doing this: myFunc() { int k; char *p; p = NewPtr(k); /* k is not initialized */ } Thta's not the end of your trouble. Suppose you do this correctly: h = NewHandle(SOMESIZE); *h = aFunctionPointer; This is also bad. You see, h is a handle to moveable memory. This means that it is a pointer to a master pointer which then points to memory gifted you you by the memory manager. By assigning a value to *h, you are wiping over the master pointer value and effectively losing the memory. What's worse is that aFunctionPointer is now going to be treated as a master pointer to relocatable memory by subsequent memory manager calls. Most likely, you are safe, as the worst that will happen is an HLock() or HUnlock(), but don't expect this to work. Another problem: Think C pulls some acrobatics to allow you to declare global variables and static arrays in CDEFs (and other stand-alone code segments). In order for them to work (correct me if I'm wrong, Rich), you'll need a call to SetUpA5() and RestoreA5() in your main to allow you to access your variables. This means that your code has to be different for the final version than it does for the debugging version -- different enough to be cause for worry. I've written a bunch of CDEFs (mostly buttons -- one of them used to be at sumex in source form as an example) and what I have done to debug them was first write a shell application that creates a type of control that is closest to what I'm creating (ie, button, scroll, etc) and exercises every aspect of it. When that's done, I add some code to add a new file to the resource search path of the application (say add in blah.CDEF) and change the NewControl to grab a CDEF of the an ID and type that will match what I will create. I then build an application and leave it. Next, I build the CDEF and have think C write the output into blah.CDEF, and I can then use the application to test it. I find this works best since I will be spending most of the time in Think C on the CDEF not the test application. The bad news is that debugging is difficult. You can be clever though, but using the refCon field of the control (which is defined for program use, so it's safe for you to dick around with for debugging) and do something like makeing the refCon be a pointer to your debugging output. Then you can make your CDEF look like this #ifdef DEBUG typedef void (*PFV)(); /* pointer to function returning void */ #define DebugMessage(ctrl, s) \ (*((PFV)((**ctrl).refCon)))(s) /* call the debugging output */ #endif DEBUG pascal short main(ctrl, message) /* I forget the args -- help me out here */ ControlHandle ctrl; long message; { SetUpA5(); #ifdef DEBUG DebugMessage("Entered. Message is: "); /* code to do a NumToString, etc etc etc */ #endif DEBUG RestoreA5(); } Now maybe you intend to use the refCon for your own insidious needs in the control. That's okay. You can always play the dereference game and pretend that the refCon is really a pointer to the following data structure: typdef struct { long realRefCon; PFV debugRoutine; } InsidiousRefcon; Then in your shell program you can do this: InsidiousRefcon ir; main() { ControlHandle ch; ir.realRefCon = WHAT_I_REALLY_INTENDED_AS_THE_REFCON; ir.debigRoutine = myDebugOutputFunction; ch = NewControl(...... , (long)(&ir)); } Then you might have your control do something like: #ifdef DEBUG #define RefCon(ch) \ (((InsidiousRefcon *)(**ch).refCon)->realRefCon) #define DebugMessage(ctrl, s) \ *(((InsidiousRefcon *)(**ctrl).refCon)->debugRoutine)(s) #else DEBUG #define RefCon(ch) \ ((**ch).refCon) #endif DEBUG I may have botched all the parenthesis twiddling, but you get the idea. Good luck. Stevge Hawley hawley@adobe.com -- "Did you know that a cow was *MURDERED* to make that jacket?" "Yes. I didn't think there were any witnesses, so I guess I'll have to kill you too." -Jake Johansen