[comp.sys.mac.programmer] CDEV/INIT Data Exchange, another way

cak3g@astsun7.astro.Virginia.EDU (Colin Klipsch) (06/06/90)

In article <8560@goofy.Apple.COM> lsr@Apple.COM (Larry Rosenstein) writes:
>In article <1990Jun5.142604.11826@asterix.drev.dnd.ca> 
>louis@asterix.drev.dnd.ca (Louis Demers) writes:
>>     What happens if you disable the init and it is not loaded.  Your
>>     resource is still there pointing to some area it has no right to
>
>I would place a magic series of bytes in memory and have the cdev check 
>that the resource points to the expected magic bytes.
>
>I've used this technique before, but users complained that the file in 
>their system folder was modified each time they booted, so it was always 
>being backed up.

Here's my experience in writing TappyType, for what it's worth. . .

The first technique I used was the one stated above: the INIT, on startup
would store a pointer to my system heap variables in a resource of
type "TapT", ID#2 (if I remember correctly).  This has the advantage of
being more "compatible" than just about any other solution I've heard
of, but which has the above disadvantage.  Plus, if the user is
especially mischievious and does things like: locks the CDEV file,
temporarily moves it to another folder, deletes the resource, etc.,
you've got problems.  Using an entire auxilliary file for CDEV-INIT
communication is subject to the same problems, and more.

Of course, you can argue that anyone pretentious enough to commit any
of these crimes against your CDEV deserves what he or she gets.

The next method I used -- briefly -- was searching the system heap for
a block of the right size, and which began with a particular magic
string.  After rereading the Memory Manager chapter and various tech
notes, however, I soon realized that this is a BAD idea.  It relies
completely on the format of memory blocks, which very very very
probably will change in the future at arbitrary times.  I suppose you
could search the system heap for a magic string without paying
attention to block boundaries, but this still seems rather iffy.

The current solution is a bit of a hack, but requires no auxilliary
resources, nor installing a driver:

The INIT installs the code and variables in system heap memory from
a resource (remember to use DetachResource!), and patches a few traps.
Among them is _AddResource.  For CDEV-INIT communication, I needed a trap
which could be called in some way that my patch could uniquely identify as
being a call from the CDEV.

My patch looks at every AddResource call.  If the call is made with
a resource of my particular type, my particular ID, if the name
pointer points to four bytes of zeros, and if the return address is near
a particular magic string, then my patch assumes the call was made
by my CDEV.  It then puts a pointer to my system heap variables in
the four bytes of zeros pointed to by the name pointer, which my CDEV
can pick up and use.  The patch ends by jumping to the original
AddResource address that the INIT found on startup (with NGetTrapAddress).

Note that adding a resource which already exists is harmless, so the
resource I "add" from the CDEV is just my options resource, which my
CDEV has previously loaded anyway.  If TappyType has not been
installed, then my patch won't be there, and the the four bytes of
zeros will remain just that after the call.  Otherwise, if it's non-zero,
it's a pointer to my system heap variables.

To be even more anal-retentive, one could do the following:

- check that the handle being added is a resource, and that it's
  the right size
- check to see that the current resource file is of type "cdev" and
  creator "TapT" (using GetFileInfo)
- pass a pointer to four zero bytes as before, but after the four
  bytes is also a pointer to the magic string; check for it

In fact, I like that last one much better, now that I thought of it.
I think I'll rewrite it that way for the next version. . .

Hope this helps.  AddResource is undoubtedly not the only trap you
could use; it's just the first feasible one I thought of.  The time
overhead is low, particularly if you write it in assembly (as one
should probably).

I invite (constructive) criticism on this method.  Anyone from Apple
see anything wrong with this?  (Anyone from anywhere, for that matter?)

----------------------------------------------------------------------
"May the forces of evil become confused on the way to your house."
                                                      -- George Carlin
Bemusedly,                   | DISCLAIMER:
  Colin Klipsch              |   This text is actually a horrendously
  UVa Astronomy Department   |   garbled excerpt from _Mating_Rituals_
  Charlottesville, Virginia  |   of_West_African_Ostriches_, Vol IV,
  cak3g@virginia.edu         |   by Davis & Griffin, 1913, p. 137
____________________________/ \_______________________________________

marykuca@uvicctr.UVic.CA.UUCP (Brent Marykuca) (06/08/90)

It seems to me that all this kludgy-sounding passing around of resources
and testing for "magic strings" can be eliminated fairly easily by using
the Gestalt Manager, which can be found in all Macintosh systems after
6.0.4, and in glue form in MPW 3.2.  Have the INIT use NewGestalt to install
a new Gestalt selector function which returns a pointer (or handle, or
whatever is required) to the data to be exchanged.  Then have the cdev
call Gestalt to pick up the handle.  If the INIT hasn't been loaded, then
Gestalt will return an error.  This seems clean (no prefs file cluttering   
up your System Folder, no validity checking to do on the resource if it
is there) and compatible.

About the only thing that I can see that is wrong with this approach
(and if I've missed anything, I'm certain that somebody will point it
out) is that Gestalt is supposed to be used for feature testing, not
as a mailbox for data.  

Cheers,

Brent Marykuca
Computing User Services
University of Victoria

lsr@Apple.COM (Larry Rosenstein) (06/08/90)

In the future the best approach to this will be to use the Gestalt trap.  
Gestalt will allow one piece of code to register a value under a 
4-character ID, and another piece to look up the value associated with the 
ID.

Also, in most cases, the reason for the CDEV/INIT communication is so that 
changes in the CDEV are immediately reflected in the INIT.  That's a nice 
feature to have, but sometimes it isn't necessary.

Larry Rosenstein, Apple Computer, Inc.
Object Specialist

Internet: lsr@Apple.com   UUCP: {nsc, sun}!apple!lsr
AppleLink: Rosenstein1

benw@microsoft.UUCP (Ben WALDMAN) (06/09/90)

On cdev-INIT communication:
	One problem that a lot of the proposed solutions have is that they
assume they can write to disk.  This won't be the case if the user boots
off a locked floppy.

	The way I do it is to write my what's really my INIT as a driver.
Then, the actual INIT resource opens the driver by name, putting into the
system heap. (And the driver's open routine patches the traps I want to
patch, etc.). The driver also provides a status call, which returns the
address of its globals.

	The cdev, when it wants to communicate with the INIT, can simply
look through the unit table (this is described in a tech note), and find
the driver (by name).  Then, the cdev can make a status call to the driver,
and, voila, the status call returns the info the driver needs.  In my case,
I returned a pointer to the INITs globals (the init is locked in memory),
but you could, of course, return a handle, or an address of a function you
wanted to call, etc.

	The scheme fails if a dorky user changes the name of the DRVR resource
with ResEdit, but will still succeed if the DRVR gets renumbered.

Ben Waldman
Software Design Engineer
Excel Development Team
Microsoft Corp.

Disclaimer:  These are my thoughts, idea, and opinions, and are in no way,
indicative of those of my employer.

lsr@Apple.COM (Larry Rosenstein) (06/09/90)

In article <1121@uvicctr.UVic.CA.UUCP> marykuca@uvicctr.UVic.CA.UUCP 
(Brent Marykuca) writes:
> About the only thing that I can see that is wrong with this approach
> (and if I've missed anything, I'm certain that somebody will point it
> out) is that Gestalt is supposed to be used for feature testing, not
> as a mailbox for data.  

I don't see anything wrong with using it in the way you suggest.  In 
addition to telling the CDEV that the INIT exists (a feature) it returns 
information that the CDEV actually uses.

Larry Rosenstein, Apple Computer, Inc.
Object Specialist

Internet: lsr@Apple.com   UUCP: {nsc, sun}!apple!lsr
AppleLink: Rosenstein1

urlichs@smurf.sub.org (Matthias Urlichs) (06/10/90)

In comp.sys.mac.programmer, article <55103@microsoft.UUCP>,
  benw@microsoft.UUCP (Ben WALDMAN) writes:
< On cdev-INIT communication:

< 	The way I do it is to write my what's really my INIT as a driver.
< Then, the actual INIT resource opens the driver by name, putting into the
< system heap. (And the driver's open routine patches the traps I want to
< patch, etc.). The driver also provides a status call, which returns the
< address of its globals.
< 
< 	The cdev, when it wants to communicate with the INIT, can simply
< look through the unit table (this is described in a tech note), and find
< the driver (by name).  Then, the cdev can make a status call to the driver,

When you already know the driver's name, why not do an OpenDriver(name)?
That'll get you its refnum much easier and safer (WRT compatibility).
Apple specifically recommends not to scan the unit table if at all possible.

< and, voila, the status call returns the info the driver needs.  In my case,
< I returned a pointer to the INITs globals (the init is locked in memory),
< but you could, of course, return a handle, or an address of a function you
< wanted to call, etc.
< 
If you want the driver to do anything, it'd be much cleaner just to call the
driver through _Control and/or _Status calls, no?
That way the whole thing will also work if the user boots with version X of
your driver, then installs Y (not necessarily greater than X), and opens the
control panel...

< 	The scheme fails if a dorky user changes the name of the DRVR resource
< with ResEdit, but will still succeed if the DRVR gets renumbered.
< 
You'll _have_to_ check in the unit table for a free refnum and install your
driver there. You may have to make the unit table bigger; don't forget to zero
the new table, record the new size in the appropriate global, don't free the
old table because you don't know where it came from, and turn off interrupts
while you do it.
-- 
Matthias Urlichs -- urlichs@smurf.sub.org -- urlichs@smurf.ira.uka.de
Humboldtstrasse 7 - 7500 Karlsruhe 1 - FRG -- +49+721+621127(Voice)/621227(PEP)