spencer@cgrg.ohio-state.edu (Stephen N. Spencer) (02/26/91)
I've been experimenting with writing CDEF (Control Definitions) for a few
custom-type controls (an ICON button, a floating-point slider), and have
run into a problem.
First, the setup: Since I want debugging messages from the CDEF, I'm not
compiling it as a CODE resource and linking it into my tester application.
(I have two windows in my tester app: a drawing window, and a TextEdit window.
I have an 'AddMessage()' function which takes a character string and appends
it to the TextEdit window -- instant scrolling debugging message window!)
I'm doing the following to associate my control with the control handle:
First, I make the window, set the port, and call my setup function:
gDrawWindow = NewWindow(....blah blah....);
SetPort(gDrawWindow);
SetupCDEF(gDrawWindow);
Here's the setup function:
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.
I could, of course, compile this CDEF into a stand-alone CODE resource and
use ResEdit to paste it into the test application, making the requisite changes
to the 'NewControl' statement, (I know how to do that).
I don't want to, however. I want to see debugging messages. Is there SOME
way to link a CDEF function to a control handle so that it can be compiled in
together with the testing application?
Thanks in advance for any help anyone out there can provide. When these CDEFs
are finished, I will post them to the 'net: I'm sure they would be useful.
Stephen N. Spencer |"Now you must dance the dance that you imply!
ACCAD (614) 292-3416 | Your actions will follow you full circle round,
The Ohio State Univ. | The higher the leap, I said,
1224 Kinnear Road | The harder the ground!"
Columbus, OH 43212-1163| - Amy Ray, "Center Stage"
spencer@cgrg.ohio-state.edu||71160.3141@compuserve.com||stephen_spencer@osu.edu
hawley@adobe.COM (Steve Hawley) (02/27/91)
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
jwinterm@jarthur.Claremont.EDU (Jim Wintermyre) (02/27/91)
In article <1435@gertie.osc.edu> spencer@cgrg.ohio-state.edu (Stephen N. Spencer) writes: >I've been experimenting with writing CDEF (Control Definitions) for a few >custom-type controls (an ICON button, a floating-point slider), and have >run into a problem. > >First, the setup: Since I want debugging messages from the CDEF, I'm not >compiling it as a CODE resource and linking it into my tester application. >(I have two windows in my tester app: a drawing window, and a TextEdit window. >I have an 'AddMessage()' function which takes a character string and appends >it to the TextEdit window -- instant scrolling debugging message window!) > >I'm doing the following to associate my control with the control handle: >First, I make the window, set the port, and call my setup function: > > gDrawWindow = NewWindow(....blah blah....); > SetPort(gDrawWindow); > SetupCDEF(gDrawWindow); > >Here's the setup function: > >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. > >I could, of course, compile this CDEF into a stand-alone CODE resource and >use ResEdit to paste it into the test application, making the requisite changes >to the 'NewControl' statement, (I know how to do that). > >I don't want to, however. I want to see debugging messages. Is there SOME >way to link a CDEF function to a control handle so that it can be compiled in >together with the testing application? > >Thanks in advance for any help anyone out there can provide. When these CDEFs >are finished, I will post them to the 'net: I'm sure they would be useful. > >Stephen N. Spencer |"Now you must dance the dance that you imply! >ACCAD (614) 292-3416 | Your actions will follow you full circle round, >The Ohio State Univ. | The higher the leap, I said, >1224 Kinnear Road | The harder the ground!" >Columbus, OH 43212-1163| - Amy Ray, "Center Stage" >spencer@cgrg.ohio-state.edu||71160.3141@compuserve.com||stephen_spencer@osu.edu Hope you don't mind a Pascal example! This example is straight out of one of the example programs in the THINK Pascal package. It's a little different from what you were doing - you still have to put a little stub CDEF resource in the resource file, but this procedure modifies it so that it will jump to the code which is actually in your program. I've used this for doing the exact thing that you want to do, so I know it works. One thing - if you are having problems with the CDEF doing what it's supposed to do, make sure that you turn the debugging options off for the actual CDEF code type JmpRecord = record jmpInstr: Integer; jmpAddr: Ptr; end; JmpPtr = ^JmpRecord; JmpHandle = ^JmpPtr; Here is the documentation in the sample program (written by Rich Siegel of Symantec Corp.): InstallDefProc works by looking for a resource of the desired resource type and ID in the resource file specified by "dpPath"; if you're installing definition routines at program startup, you can pass CurResFile() as the first argument. The defproc resource is then patched to point to the procedure address given in "dpAddr". If the resource is not found in the resource file, a debugger trap is executed. To avoid this, you should create a 6-byte resource (it can be all zeros) of the defproc's type and ID, and place it in your program's resource file. Caveats: You probably don't want to install stub defprocs in the system resource file. Also remember that there are reserved resource ID's from 0 to 127; you should use resource ID's 128 or higher for your defprocs. Procedures that are installed should be in the main segment, and InstallDefProc only need be called once. HOW IT WORKS: In the normal case, the system loads a procedure resource (a CDEF, for example), and jumps to its beginning. InstallDefProc provides this functionality for procedures in your program by providing a 6-byte stub resource, which contains $4EF9, followed by a long word. The $4EF9 is a 68000 long jump instruction, and the long word is the address to which to jump. So the system calls this dummy defproc,which in turn jumps to the address passed. procedure InstallDefProc (dpPath: Integer; dpType: ResType; dpID: Integer; dpAddr: Ptr); var jH: JmpHandle; savePath: Integer; begin savePath := CurResFile; UseResFile(dpPath); jH := JmpHandle(GetResource(dpType, dpID)); UseResFile(savePath); if jH = nil then {Is there no defproc resource?} DebugStr('Stub Defproc Not Found!'); with jH^^ do begin jmpAddr := dpAddr; jmpInstr := $4EF9; end; HUnlock(Handle(jH)); MoveHHi(Handle(jH)); HNoPurge(Handle(jH)); {make this resource nonpurgeable} end; So you would call it like: InstallDefProc(CurResFile,'CDEF',200,@MyCDEFMain); Then, you would simply create a new control in the normal fashion (using NewControl), making sure that its procID field is set equal to 16 * resource ID of the CDEF stub in the resource file. So if the CDEF stub had resID 200, you'd pass 3200 in the procID parameter to NewControl. I hope this helps some. - Jim Wintermyre