[comp.sys.mac.programmer] Calling Think C objects from MyUserItem

jason@ux1.cso.uiuc.edu (Jason Watts) (09/30/90)

	I have written an XFCN using Think C 4.0.  The routine utilizes an 
object (in the oop sense) to bind the data being manipulated to its graphic 
representation (a dialog box).  The main() function creates an instance of my 
object, calls its initialization routine, calls its "go" routine (which handles 
the dialog actions), then disposes of the object before exitting.  The object's 
initialization routine creates a dialog by calling GetNewDialog and passing it 
a pointer to the dialog record that is itself an instance variable of the
object.  A reference to the object (i.e., this) is saved in the dialog's window 
refCon.  The initialization routine proceeds with other initializations, 
including installing userItem procedures to be called by ModalDialog and 
storing, in the object's filterProc instance variable, a pointer to the filter 
function to be used by ModalDialog.  The three dialog userItem procs and the 
ModalDialog filter proc all call certain methods of the object; these routines 
access the object by retrieving the object reference that has been stored in 
the window refCon of the dialog.
	When the XFCN is executed, the object is successfully created and 
initialized.  The MyObject->Go() routine is then called.  It succeeds in 
showing the dialog window.  ModalDialog is called next, which in turn calls the 
first userItem proc.  The userItem proc executes smoothly until it attempts to 
call my object with something like theObject->AnyMethod().  The routine bombs 
with either a bus error or an illegal instruction, indicating that the object 
reference is bad (execution never branches to AnyMethod(), so the problem isn't 
in the method being called).  What baffles me is that when I bypass the 
userItem procs (by either having them return immediately or by never installing 
them), ModalDialog() calls only the filter proc I gave it.  The filter proc 
runs beautifully, even when it calls my object, which it accesses in exactly 
the same way as the userItem procs!  This proves that "this" (the object 
reference) *is* being stored correctly in the dialog window refCon, and that it 
is valid when my ModalDialog filter gets it.
	My userItem procs all look something like:

pascal   void   UserItemProc( DialogPeek theDialog, int itemNumber )
{
	CMyClass	*theObjectRef;
	
	
	/* get the object reference--it's in theDialog's window refCon */
	theObjectRef = ( CMyClass * ) GetWRefCon( theDialog );
	
	/* The following instruction dies with system error ID=1 or 3 */
	theObjectRef->DoSomeDrawing();
}


and the ModalDialog filter:

pascal   Boolean   DialogFilter( DialogPeek theDialog, EventRecord *theEvent,
                                 int *itemHit )
{
	CMyClass     *theObjectRef;
	.
	.
	.
		
	theObjectRef = ( CMyClass * ) GetWRefCon( theDialog );

	/* the follow call works fine */
	theObjectRef->WhateverMethod();
	.
	.
	.
	/* In fact, all such calls from this filter proc work as expected */
	theObjectRef->AnotherMethod();
	.
	.
	.
}


I have checked that the functions are all declared correctly.  I have tried to 
verify that everything that shouldn't matter doesn't matter. I have tested both
direct and indirect versions of my object (using new() and delete() for the 
indirect, and NewPtr(), blessD(), and DisposPtr() for the direct). I *am* using
the oopsA4 library.  Can anyone figure out what might be going wrong?
-- 
                       |  |    |====================|
                       |  |    | Jason Watts        |
                     \ |\ |\ \ | (jazzin@uiuc.edu)  |
                      \| \| \|\|====================|

phils@chaos.cs.brandeis.edu (Phil Shapiro) (10/02/90)

If you have a callback (in a code resource or application) that uses
objects, you must make sure that your jump table pointer is correct,
since object use the jump table to dispatch their methods.  In code
resources, A4 points to the jump table.  What you should do is
something like:

void LineBox::DoAbout(DialogPtr aboutBox)
{
    int itemHit;

    RememberA4();
    ShowWindow((WindowPtr) aboutBox);
    SetWRefCon((WindowPtr) aboutBox, (long) this);
    do {
        ModalDialog(filter, &itemHit);
    } while (itemHit != OKItem);
}

static pascal
Boolean filter(DialogPtr theDialog, EventRecord *theEvent, short
*itemHit)
{
    Boolean result = FALSE;

    switch(theEvent->what) {
    /* ... other cases ... */
    case nullEvent:
        SetUpA4();
        ((LineBox *)GetWRefCon((WindowPtr) theDialog))->Idle();
        RestoreA4();
        break;
    default:
        break;
    }
    return result;
}

This method works the same for user item draw procedures, as well.

	-phil shapiro, symantec tech support
--
Phil Shapiro
phils@chaos.cs.brandeis.edu