[comp.sys.mac.programmer] What am I doing wrong?--modal dialog use crashes

labc-1ic@e260-1c.berkeley.edu (Willy S. Liao) (05/08/91)

OK dokee, I have a problem that's driving me nuts.  I'm sure I'm doing
something wrong, but I don't know what it is...I'm using THINK C 4.04 with
System 7.0f4 and the Think Class Library.  The following function is invoked
by my document initialization method (it gets an integer for the size of the
game board).  Whenever this function gets run with NO other windows on the
screen, it works fine.  However, if there is another window open in the
application layer (i.e. another instance of a document is in memory), this
function chokes with a syserr 28 (stack has moved into heap) error on the
call to ModalDialog().  Can anyone suggest anything?  Boosting the memory
partition up to larger sizes doesn't help (although the crash sometimes
turns into an error 25, out of memory).

My function:

static short
GetBoardsize(void)
{
	Str255 req, answer;
	DialogPtr dptr;
	short temp;
	long size;
	Rect rect;
	Handle ihandle;
	DialogRecord dRec;
	
	GetIndString(req, GAME_MSGS, GET_SIZE_STR);
	ParamText(req, "\p","\p","\p");
	dptr = GetNewDialog(GENERAL_REQ_DIALOG, &dRec, NULL);
	GetDItem(dptr, GENERAL_ETEXT_NUM, &temp, &ihandle, &rect);
	do {
		NumToString((long) DEFAULT_BSIZE, answer);
		SetIText(ihandle, answer);
		SelIText(dptr, GENERAL_ETEXT_NUM, 0, (short) answer[0]);
		ShowWindow(dptr);
		ModalDialog(NULL, &temp);
		GetDItem(dptr, GENERAL_ETEXT_NUM, &temp, &ihandle, &rect);
		GetIText(ihandle, answer);
		StringToNum(answer, &size);
	} while (size < MIN_BOARDSIZE || size > MAX_BOARDSIZE);
	CloseDialog(dptr);
	DisposHandle(dRec.items);
	return (short) size;
}

All of the all-caps identifiers are #define's.

Willy Liao
labc-1ic@web.berkeley.edu

REEKES@applelink.apple.com (Jim Reekes) (05/09/91)

In article <1991May7.234354.26431@agate.berkeley.edu>, labc-1ic@e260-1c.berkeley.edu (Willy S. Liao) writes:
> 
> OK dokee, I have a problem that's driving me nuts.  I'm sure I'm doing
> something wrong, but I don't know what it is...I'm using THINK C 4.04 with
> System 7.0f4 and the Think Class Library.  The following function is invoked
> by my document initialization method (it gets an integer for the size of the
> game board).  Whenever this function gets run with NO other windows on the
> screen, it works fine.  However, if there is another window open in the
> application layer (i.e. another instance of a document is in memory), this
> function chokes with a syserr 28 (stack has moved into heap) error on the
> call to ModalDialog().  Can anyone suggest anything?  Boosting the memory
> partition up to larger sizes doesn't help (although the crash sometimes
> turns into an error 25, out of memory).
> 
> My function:
> 
> static short
> GetBoardsize(void)
> {
> 	Str255 req, answer;
> 	DialogPtr dptr;
> 	short temp;
> 	long size;
> 	Rect rect;
> 	Handle ihandle;
> 	DialogRecord dRec;
> 	
> 	GetIndString(req, GAME_MSGS, GET_SIZE_STR);
> 	ParamText(req, "\p","\p","\p");
> 	dptr = GetNewDialog(GENERAL_REQ_DIALOG, &dRec, NULL);
> 	GetDItem(dptr, GENERAL_ETEXT_NUM, &temp, &ihandle, &rect);
> 	do {
> 		NumToString((long) DEFAULT_BSIZE, answer);
> 		SetIText(ihandle, answer);
> 		SelIText(dptr, GENERAL_ETEXT_NUM, 0, (short) answer[0]);
> 		ShowWindow(dptr);
> 		ModalDialog(NULL, &temp);
> 		GetDItem(dptr, GENERAL_ETEXT_NUM, &temp, &ihandle, &rect);
> 		GetIText(ihandle, answer);
> 		StringToNum(answer, &size);
> 	} while (size < MIN_BOARDSIZE || size > MAX_BOARDSIZE);
> 	CloseDialog(dptr);
> 	DisposHandle(dRec.items);
> 	return (short) size;
> }

This line is bad:

dptr = GetNewDialog(GENERAL_REQ_DIALOG, &dRec, NULL);

You never allocated any memory for dRec.  You have an uninitialized pointer, and passed
it to the Dialog Manager for storage.  This is going to cause random results.  Either
allocate some storage with NewPtr(sizeof(DialogRecord)) or pass nil.

Refer to Inside Mac "The Dialog Manager" chapter pages 413-414.

Also, you don't need to call ShowWindow every time through the loop.  Are you resetting
GENERAL_ETEXT_NUM to DEFAULT_BSIZE everytime through the loop?  Finally, you should
do some reality checking on the text of GENERAL_ETEXT_NUM before calling StringToNum.
If some non-numeric data was entered, random results will be returned by StringToNum


Jim Reekes E.O., Macintosh Toolbox Engineering

Lawson.English@p88.f15.n300.z1.fidonet.org (Lawson English) (05/09/91)

Willy S. Liao writes in a message to All

WSL>  OK dokee, I have a problem that's driving me nuts. I'm sure 
WSL> I'm doing something wrong, but I don't know what it is...I'm 
WSL> using THINK C 4.04 with System 7.0f4 and the Think Class Library

The Think Class Libraries don't support dialogs directly (gDeskTop gets confused
and tries to send messages to the dialog window after it is gone).

The following class is based on the information supplied by Symantec Tech Support.
The key procedures are: 

DeactivateTCLDeskAndShowDialog;

and

ActivateTCLDeskAfterHideDialog;

which will prevent gDeskTop from getting an update while the dialog is around.

One does "myDialog.IResModalDialog (aDLOGid, aFilter, aDefaultItem);" followed
by  "myDialog.Go" (for generics) or "Run" (for more complicated dialogs) and
overrides the "DoModalDialogStuff" method to do the inner workings.

Caveat: only one ModalDialog should be "GO"-ing at a time...otherwise gDeskTop
might get confused. Never tried it with multiple dialogs, maybe it will work...

Hope it helps, source code released for public use by Lawson D. English.



******************************************************************************

{*****************************************************************************}
{** CModalDialog implements a generic Modal Dialog Class                   
**}
{** Copyright 1991 Lawson D. English (use at will but remember me as well) 
**}
{** From Symantec Tech Support suggestions.                                
**}
{**}
{*****************************************************************************}


unit CModalDialog;



interface

uses
TCL, DialogIntf;

implementation


{**}
{ * IModalDialog}
{ *}
{ *Initialize a ModalDialog object.}
{ *}
{ **}
procedure CModalDialog.IModalDialog (aFilter: procPtr; aDefaultItem: Integer);
begin

itsDefaultItem := aDefaultItem;
itsFilterProc := aFilter;


{ you would have to override here to create dialogs on-the-fly }
end;

{ * IResModalDialog * }
{ *}
{ *Initialize a ModalDialog object using a resource * }
{ * }
{ ** }
procedure CModalDialog.IResModalDialog (aDLOGid: Integer; aFilter: procPtr;
aDefaultItem: Integer);
begin
itsDLOGid := aDLOGid;
IModalDialog (aFilter, aDefaultItem);
end;

procedure CModalDialog.free;
begin
DisposDialog(itsDialogPtr);

inherited Free;
end;

procedure CModalDialog.ProcureDialogPtr;
var
theDLOGid: Integer;
begin
theDLOGid := itsDLOGid;
{ a dialog must always be invisible when gotten }
{ to avoid sending updates to gDeskTop, hence "WindowPtr(0)"}
itsDialogPtr := GetNewDialog(theDLOGid, nil, WindowPtr(0));
end;

procedure CModalDialog.DeactivateTCLDeskAndShowDialog;
var
theDialogPtr: DialogPtr;
begin
theDialogPtr := itsDialogPtr;
gDeskTop.Deactivate;        { we can't have gDeskTop active when }
BringToFront(theDialogPtr); { the dialog comes to the front }
ShowHide(theDialogPtr, TRUE);
end;

procedure CModalDialog.ActivateTCLDeskAfterHideDialog;
var
theDialogPtr: DialogPtr;
begin
theDialogPtr := itsDialogPtr;
ShowHide(theDialogPtr, FALSE);
SendBehind(theDialogPtr, WindowPtr(0)); { Hide the dialog before gDeskTop }
gDeskTop.Activate;                      { is activated }
end;

{**}
{ * SetDialogID}
{ *}
{ *For those times when you need to have a generic dialog with various IDs}
{ * and just say "Go"}
{ **}
procedure CModalDialog.SetDialogID (aDLOGid: integer);
begin
itsDLOGid := aDLOGid;
end;

{**}
{ * Go * }
{ *}
{ *Generic RUN using defaults of no procPtr and itemHit of 1 for RETURN * }
{ * }
{ ** }
procedure CModalDialog.Go;

begin
itsDefaultItem := 1;   { implements the generic "RETURN" default dialog}
itsFilterProc := nil;  { with no filterProc }

RunModalDialog;
free;
end;

{**}
{ * RunModalDialog}
{ *}
{ *Run ModalDialog object... NO! REally?}
{ *}
{ **}
procedure CModalDialog.RunModalDialog;
var
theDialogItem: Integer;
theFilterProc: ProcPtr;
theDLOGid: Integer;
begin
theFilterProc := itsFilterProc;

ProcureDialogPtr;
DeactivateTCLDeskAndShowDialog;

repeat
ModalDialog(theFilterProc, theDialogItem);
DoModalDialogStuff(theDialogItem);
until (theDialogItem = itsDefaultItem);
ActivateTCLDeskAfterHideDialog;
end;

procedure CModalDialog.DoModalDialogStuff(theDialogItem:Integer);
begin
{ needs to be overriden in your own dialog classe }
end;
end.




end.
*******************************************************************************
 

--  
Uucp: ...{gatech,ames,rutgers}!ncar!asuvax!stjhmc!300!15.88!Lawson.English
Internet: Lawson.English@p88.f15.n300.z1.fidonet.org

wdh@well.sf.ca.us (Bill Hofmann) (05/11/91)

For the second time this week, if you create a window, you must call SetPort
before you use it.  Jim Reekes is wrong here, the original poster *does*
pass the address of a block of memory big enough to hold a DialogRecord, and
does all the right things to get rid of memory.  He is right to point out
the silliness of calling ShowWindow more than once (or even once, if the
DLOG indicates the window should be visible).

Some things to keep in mind:

-ShowWindow, NewWindow, GetNewWindow, NewDialog, GetNewDialog, and every
other window and dialog manager routine *does not*, repeat, *NOT* ever
call SetPort to make anything the current port, at least as you should know.
-You *must* call SetPort yourself, no one will do it for you.

-Bill Hofmann
Not a ToolBox Engineer

Lawson.English@p88.f15.n300.z1.fidonet.org (Lawson English) (05/13/91)

Bill Hofmann writes in a message to All

BH> For the second time this week, if you create a window, you must 
BH> call SetPort before you use it. Jim Reekes is wrong here, the 
BH> original poster *does* pass the address of a block of memory 
BH> big enough to hold a DialogRecord, and does all the right things 
BH> to get rid of memory. He is right to point out the silliness 
BH> of calling ShowWindow more than once (or even once, if the DLOG 
BH> indicates the window should be visible).

A not-so-well know bug in the Think Class Libraries requires one to send gDeskTop
a Deactivate message before creating and showing a dialog (it must be hidden
before it is shown). Otherwise, gDeskTop gets an update event message when the
dialog goes away and somehow tries to send its window a deactivate message.
As the dialog is NOT part of gDeskTop's window list, and no longer even exists,
this causes mucho problemas. I posted a dialog manager class that gets around
the problem. Did anyone actually see it, or should I repost?



Lawson
 

--  
Uucp: ...{gatech,ames,rutgers}!ncar!asuvax!stjhmc!300!15.88!Lawson.English
Internet: Lawson.English@p88.f15.n300.z1.fidonet.org