From: sold@kit.uni-kl.de (Christoph Sold)
Newsgroups: comp.sys.mac.programmer
Subject: Re: Help with piggybacking data on window records
Date: 14 Aug 92 20:13:33 GMT
Organization: Universitaet Kaiserslautern

In article <1992Aug10.223248.3217@ucsvc.ucs.unimelb.edu.au> u2005681@ucsvc.ucs.unimelb.edu.au writes:
>Subject: Help with piggybacking data on window records
>From: u2005681@ucsvc.ucs.unimelb.edu.au
>Date: 10 Aug 92 12:32:48 GMT
>Hi People,
>This is my first application so don't be too harsh.
>I'm trying to piggyback data on a window record so I can keep
>track of pop-up menus and icon buttons in a multi-window
>application.
>I store my handles in a struct called winPrivate.
>When I create a new window I do the following:
>
>wp = GetNewWindow(rMainWindow, (Ptr)0L, (WindowPtr)-1);
>myPrivate = NewHandleClear(sizeof(winPrivate));
>SetWRefCon(wp, (long)myPrivate);
>
>This seem to allocate the space required (I check if
>the allocation fails).
>
>When trying to retrieve the data in a routine I do the
>following:
>
>void Routine(WindowPtr wp, .....);
>{
>	winPrivateHndl		dataHandle;
>	short				x;
>	Rect				theRect;
>	
>	dataHandle = (winPrivateHndl) GetWRefCon(wp);
>	HLock(dataHandle);
>	.
>	.
>	x = (*dataHandle)->x1; /* for example */
>	DrawPicture((*dataHandle)->thePicHandle, &theRect);
>	.
>	.
>	.
>	HUnlock(dataHandle);
>}
>
>I seem to be able to retrieve data this way but I am losing
>memory by the kilobyte. Every time I call a routine that
>accesses the piggyback data, I seem to end up with a
>locked memory block (even though I UNLOCK it when I leave
>the routine. When I close the window and dispose of all my
>handles, I don't seem to regain any memory!
>Can anybody help me with this problem?
>Am I doing the piggyback thing correctly?
>Am I accessing the piggybacked data correctly?
>Thanks in advance for any suggestions, I am at the end of
>my wisdom.
>
>Chris Hofflin


Chris,

here the two ways to piggyback data into any (handle|pointer)-referenced 
block:

1) packing private data onto the back of the OS record
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The big idea is the OS wants n bytes, at n+1 bytes you can do what you want. 
Here is a picture of this memory layout:

byte 0	+-----------+
	|OS record  |
	|	    |
byte n	+-----------+
byte n+1|your data  |
	|...        |

	|	    |
	+-----------+

To do this, you allocate _one_ memory block, and use a pointer|handle to it 
as you wish. In pascal, this is define this way:

TYPE
 WindowRecord = Record
	{some fields here}
  END; {WindowRecord}

 PrivateRecord = Record
	{more fields here}
  END; {PrivateRecord}

 PiggyPack = Record
	w: WindowRecord;
	p: PrivateRecord;
  END; {PiggyPack}

Now you can pass a pointer to PiggyPack equivalent to a pointer to 
WindowRecord, since both start at the same location.

Unfortunately, this will crash as soon as apple chooses to add more fields 
to a (Window)Record -- since your private data is in the place new fields 
will be allocated. Method 1: bad way. Don't use it, until you have written 
warranty from apple the os record will never change.

2) packing a reference to your data in a useful place
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Fortunately, nearly every os record has a "for your convenience"-field, 
often named "refCon". Just four bytes long, this is enough to catch a 
pointer or handle. It is also the way you choose (some kudos for this. well 
done!). It looks in memory this way:

	+------------+
	| os record  |
	| ...        |	+------------+
	|"refCon" ----->|private data|
	+------------+	|...	     |
			+------------+
To do this one, you have to manage your memory careful: The operating system 
knows _nothing_ about the handle|pointer in the refCon field.
Error #1 is to de-allocate the os record without deallocating the private 
data first: This is a (major) memory leak, since the private block is still 
in the heap, and is referenced by nothing else. No way to recover it.
Error #2 is to de-allocate the private data without setting it's reference 
variable to NULL|NIL. This way you can reference a previously freed block -- 
possibly with some valid data in it. But this block will be overwritten -- 
sooner or later.
Knowing this two error causes, we can define the only way to do this sort of 
pig(gy)-bag. Again, in Pascal:

TYPE
 WindowRecord = RECORD
	{some fields here}
	refCon: LongInt; {four bytes apple never uses--behold the Coom 
Toolbox}
  END; {WindowRecord}
 WindowPtr = ^WindowRecord;

 PrivateRecord = RECORD
	{some private data here}
  END; {PrivateRecord}
 PrivatePtr = ^PrivateRecord;

VAR
  w: WindowPtr;
  p: PrivatePtr;

{Allocation}

 w := NewPtr(sizeOf(WindowRecord)); {allocate a window record}
 w.refCon := NIL; {just to be sure, since there is no private data yet}
 {.. do something with the window record, e.g.:}
 wp := GetNewWindow(kMyWindow, w, Ptr(-1));

 {...}
 p = NewPtr(sizeOf(PrivateRecord));{allocate a private record}
 w.refCon := LongInt(p); {now refCon points to your private data}
 {alternatively, use for WindowRecords only}
 SetWRefCon(wp, LongInt(p));

{Deallocation}
 p := w.refCon;
 {for WindowRecords only, you can use}
 p := GetWRefCon(wp);
 ReleasePtr(p);
 w.refCon := NIL; {*** important! *** see error #2}
 {alternatively, for window records only}
 SetWRefCon(wp, 0); {since nil is 0}

 {...}
 CloseWindow(wp);



OK, thats the whole point. Additionally, I suggest to switch from C to 
either C++ or Pascal, since both of these langauages provide strong 
typechecking. This is essential for a beginner, since the compiler will 
catch much more of typcal beginners typos and errors. Also recommended is 
Inside Macintosh, Memory management chapters (for the theoretical 
background), as well as the Programmers FAQ posted periodically here.

Hope this helped a little.

-Christoph

Christoph P. Sold                   CATS Software GmbH
                                    Mussbacher Landstr.2
                                    W-6730 Neustadt (Weinstrasse)
ger.xse0035@applelink.apple.com     Germany

"If an apple is fun, what the heck is an appletree?"
Newsgroups: comp.sys.mac.programmer