[comp.sys.mac.programmer] sharing data between cdevs and INITs

t-benw@microsoft.UUCP (Benjamin Waldmin) (08/19/88)

Does anyone out there have a suggestion for sharing data between a cdev
and a patch installed as an INIT? 

To be more precise, I have a cdev which a user uses to set
options which change the behavior of an INIT.  For example, in a
screen saver, the user sets the time until screen blanking in the control
panel, and then the screen saver (presumably an INIT which patched a trap)
changes its behavior accordingly.

So, the problem is: how does the cdev know where to put the data so
that the INIT can find it, or how does the INIT know where the cdev put the
data? The cdev can either put data in some block or change the patch that
the INIT has installed, but there's no way for the cdev to find the patch
either (using GetTrapAddress won't work, since another patch may have
been installed on top of mine).

I've thought of allocating space for the variables (in the system heap)
when the INIT is loaded, then saving the address of this space in a resource
on the disk, and having the cdev read this resource.  The problem with this
is that I have to write to disk, which I'd prefer not to do (What if the
user has the disk locked - then the cdev crashes, since the address it
reads from disk will br bogus).

Alternatively, I could have the cdev try to find the patch in the system 
heap (looking for a certain byte pattern to identify it).  The problem
with this is that I can't just traverse blocks in the heap to look for
my block, since I can't assume memory manager data structure formats, since
Apple says not to (I don't know why - I can't imagine Apple changing these).
Thus, I'd have to look through every byte in the heap, which would be slow.


So, after all this, does anyone have any suggestions?


Thanks a lot in advance!

Ben Waldman

uw-beaver!microsoft!t-benw

lsr@Apple.COM (Larry Rosenstein) (08/19/88)

In article <1739@microsoft.UUCP> t-benw@microsoft.uucp (Benjamin Waldman) writes:
>Does anyone out there have a suggestion for sharing data between a cdev
>and a patch installed as an INIT? 

Here's what I have done in my cdevs that have INITs.

First, I keep configuration information in a resource in the file.  When the
INIT installs itself, it copies this information into the system heap.
The INIT saves the address of the data (eg, a handle) in some well-defined
place.  (Generally, I reserve a few bytes of storage in the same heap block
containing the installed code.)

I also had the INIT installer record in a special resource, where the INIT
was installed.  The cdev can then read this resource, locate the INIT, and
read/set its configuration.  I also have the INIT leave some 4-byte ID
around in memory.  The cdev checks this to ensure that the address it has is
valid.  (You could increase this to 8-bytes for extra insurance.)  

The cdev needs to at least change the configuration resource stored in the
file.  If it can locate the installed INIT in RAM, then it cal also change
the configuration in RAM, which will cause changes to take effect
immediately.  If the cdev can't locate the INIT in RAM (because it hasn't
been installed, or the special ID wasn't located), then changes will take
effect when you reboot, andI added a little message to that effect in the
cdev window.

This does require that the INIT be able to write to the disk, which means
the modification date on the file changes each time you boot.  If the disk
is write-protected, then the INIT won't be able to record its location in
RAM.  In some cases, the INIT would be installed in the same spot as last
time, so the cdev will still be able to locate it.  Otherwise, configuration
changes won't take effect immediately.  (If the disk is always
write-protected, then there is no way to save any configuration changes.)

I wouldn't try to scan through the system heap for the reasons you
mentioned.  

An alternative is to make configuration changes take effect when you reboot.
This is easy to implement, since the cdev doesn't have to do anything with
the INIT, but it is inconvenient for the user.

You could also use a shared configuration file.  The cdev can easily modify
that file, but the INIT would have to periodically check to see if it has
changed.  (You could watch for changes in modification dates.)

		 Larry Rosenstein,  Object Specialist
 Apple Computer, Inc.  20525 Mariani Ave, MS 46-B  Cupertino, CA 95014
	    AppleLink:Rosenstein1    domain:lsr@Apple.COM
		UUCP:{sun,voder,nsc,decwrl}!apple!lsr

t-benw@microsoft.UUCP (Benjamin Waldmin) (08/20/88)

In article <15808@apple.Apple.COM> lsr@apple.com.UUCP (Larry Rosenstein) writes:
>In article <1739@microsoft.UUCP> t-benw@microsoft.uucp (I) wrote:
>>Does anyone out there have a suggestion for sharing data between a cdev
>>and a patch installed as an INIT? 
>
>Here's what I have done in my cdevs that have INITs.
>
>First, I keep configuration information in a resource in the file.  When the
>INIT installs itself, it copies this information into the system heap.
>The INIT saves the address of the data (eg, a handle) in some well-defined
>place.  (Generally, I reserve a few bytes of storage in the same heap block
>containing the installed code.)
>
>I also had the INIT installer record in a special resource, where the INIT
>was installed.  The cdev can then read this resource, locate the INIT, and
>read/set its configuration.  I also have the INIT leave some 4-byte ID
>around in memory.  The cdev checks this to ensure that the address it has is
>valid.  (You could increase this to 8-bytes for extra insurance.)  

The problem with this is that the INIT has to write to disk at startup
time, which I'd rather not do.

Actually, I've found a rather neat solution -- the device manager!!

I create a device driver.  In response to the open call, it patches the trap
I want to patch (and I open the driver at startup, in an INIT).  In response
to a status call, I return the address of my variables that I want the
cdev to access.  The only trick part is for the cdev to figure out the refnum
of the driver.  But Apple has already solved that problem - they tell you
how to do this in Tech note #71.

Unfortunately, there's one problem - Think C 3.0!!!
My driver has to be in the system heap, so it can stay alive all the time.
But while THINK C lets you set the resource flags for a code resource, it
doesn't let you do this for a driver.  So every time, after building the
driver, I have to start ResEdit, and set the system heap bit.

But, wait, it gets worse.  Think C lets you have global data in your
drivers by allocating a block in the heap for you, locking it, pointing
A4 to it, and referencing your globals off of A4.  But my data has to
be in the system heap!!!  So, in my open routine, I have to allocate
another block, in the system heap this time, copy my globals from where
Think C put them, and point A4 to my globals.  (Actually, I store the
handle somewhere in my code, and when my trap patch is called, I get
the handle from this location, lock it, dereference it, and set A4 to
the dereference).

There's still one problem, however.  Think C locks the handle that IT
allocated every time my driver is called.  Suppose I call my driver
later on, after the application heap has been reinitialized (which is
OK, since my driver is in the system heap). The handle that THINK C
allocated is no longer valid.  I wonder if THINK C's driver
glue will check the return value of its call to HLock, and what it will
do after this.  If it just calls me blindly anyway, it's fine, since
I'll just unlock Think C's handle and return.  Otherwise, I don't
know what will happen.  Things are complicated further by the fact that
that old handle may still point to a valid block (someone else's),
or may not point to anything at all.

#include <LSC_is_so_great_I_could_just_die.h>
Yes, I really do like LSC, but I really think that this is a severe
flaw.  I suppose what I really should do is hack through Think C's
driver glue, find their newhandle call, and change some bits so that
it allocates a block in the system heap for the global data rather than
the application heap. 

Rich, could you be of any help here?  Is there some magic word I
could change in THINK C so that the newhandle call is done to allocate
a block in the system heap?

Also, will version 3.1 let us keep drivers in the system heap?


Thanks,

Ben Waldman
Software Design Engineer,
Microsoft Corp.

Disclaimer:  These are my own thoughts, opinions, and ideas, and in no
way represent the views of my employer.

joachim@iraul1.ira.uka.de (Joachim Lindenberg) (08/20/88)

In article <1739@microsoft.UUCP> t-benw@microsoft.uucp (Benjamin Waldman) writes:
>Does anyone out there have a suggestion for sharing data between a cdev
>and a patch installed as an INIT? 
>
Use the INIT to load a driver which does the actual work. The driver may
use a handle stored in its DCtlEntry to hold its variables. The cdev will
have to scan the unit table for the driver and use the same handle.

Joachim

castan@munnari.oz (Jason Castan) (08/22/88)

In article <1745@microsoft.UUCP>, t-benw@microsoft.UUCP (Benjamin Waldmin) writes:
> Actually, I've found a rather neat solution -- the device manager!!
> 
> I create a device driver.  In response to the open call, it patches the trap
> I want to patch (and I open the driver at startup, in an INIT).  In response
> to a status call, I return the address of my variables that I want the
> cdev to access.  The only trick part is for the cdev to figure out the refnum
> of the driver.  But Apple has already solved that problem - they tell you
> how to do this in Tech note #71.
> 
> But, wait, it gets worse.  Think C lets you have global data in your
> drivers by allocating a block in the heap for you, locking it, pointing
> A4 to it, and referencing your globals off of A4.  But my data has to
> be in the system heap!!!  So, in my open routine, I have to allocate
> another block, in the system heap this time, copy my globals from where
> Think C put them, and point A4 to my globals.  (Actually, I store the
> handle somewhere in my code, and when my trap patch is called, I get
> the handle from this location, lock it, dereference it, and set A4 to
> the dereference).
> 

You are doing the same algorithm that moire uses except that i use 
a control call to modify moire's behaviour. To make Think C allocate 
your globals in the system heap, just set the system heap bit for your
DATA -16000 resource !

Yes, i agree its a flaw that Think C doesnt allow easy handling of
resources. I just got 3.0 today :-) and had to reconcile myself that
i wont be able to use its debugger with moire ;-( because of its
design. However under multifinder, its a trivial thing to write a 
program that modifies all the bits to load the DRVR and DATA rez's to
the system heap.

For a solution involving less code and more hack to inter-process
communication, try patching an uncommonly used trap like DebugStr().

Set your INIT to patch DebugStr(). When you want to communicate with
the INIT, save ToolScratch (or CurApName) and put a signature in the
low mem global. DebugStr checks the low mem global, sees that it's 
actually a message for the INIT, and the stringPtr passed is actually
the message.  DebugStr merely returns after processing the message.
if the signature is not valid, call the real trap.

On return from DebugStr, restore the old value of the global. I just
thought of this today actually while working on another problem.
I like it a whole lot, and should be reliable if you choose a good
signature (if ShowInit does it, so can i !).

> Thanks,
> 
> Ben Waldman
> Software Design Engineer,
> Microsoft Corp.
> 

Hope this helps,
John Lim
ps : cant help but give another plug for Think C 3.0. Great work !
	Cant wait for the bugs to sprout out of the screen so i can
	squash them...

anson@spray.CalComp.COM (Ed Anson) (08/23/88)

In article <1745@microsoft.UUCP> t-benw@microsoft.uucp (Benjamin Waldman) writes:
>But, wait, it gets worse.  Think C lets you have global data in your
>drivers by allocating a block in the heap for you, locking it, pointing
>A4 to it, and referencing your globals off of A4.  But my data has to
>be in the system heap!!!

When you allocate globals for a driver, they are stored in a DATA resource.
You could try just setting the System bit for that resource. It may solve
your problem.

Im not sure, but it looks like LSC is using GetResource, not NewHandle.
-- 
=====================================================================
   Ed Anson,    Calcomp Display Products Division,    Hudson NH 03051
   (603) 885-8712,      anson@elrond.CalComp.COM

t-benw@microsoft.UUCP (Benjamin Waldmin) (08/24/88)

In article <2413@spray.CalComp.COM> anson@spray.UUCP (Ed Anson) writes:
>In article <1745@microsoft.UUCP> t-benw@microsoft.uucp (Benjamin Waldman) writes:
>>But, wait, it gets worse.  Think C lets you have global data in your
>>drivers by allocating a block in the heap for you, locking it, pointing
>>A4 to it, and referencing your globals off of A4.  But my data has to
>>be in the system heap!!!
>
>When you allocate globals for a driver, they are stored in a DATA resource.
>You could try just setting the System bit for that resource. It may solve
>your problem.
>
>Im not sure, but it looks like LSC is using GetResource, not NewHandle.

Actually, someone from THINK mailed me a neat suggestion (the day after my
posting - how's that for customer service!).  Since my driver is being
opened by an INIT, I can just save the current zone, set the zone to be
the system heap, and open my driver.  Since the system heap will be the
current zone when the driver is opened, both it and its data will be
in the system heap. Finally I restore the old zone.  Since I do want to
keep the driver in memory though, and not close it when the INIT is closed,
I do a GetResource('DRVR',theID), OpenDriver(...), and a DetachResource.

Still though, if I wasn't opening the driver at INIT time, I'd have to
go and manually set the bits with ResEdit every time I rebuilt the
driver, which is a pain.  I guess this is just a small oversight on
THINK's part, which, one hopes, will be corrected in the next release.

Ben Waldman
Software Design Engineer,
Microsoft Corp.

Disclaimer:  These are my thoughts, opinions, and ideas only, and do not
reflect those of my employer in any way.

jmunkki@santra.HUT.FI (Juri Munkki) (08/26/88)

In <1745@microsoft.UUCP> t-benw@microsoft.uucp (Benjamin Waldman) writes:
>The problem with this is that the INIT has to write to disk at startup
>time, which I'd rather not do.

Right... I did this by looking at memory manager structures the way that
is suggested in the QuicKeys manual.

>Actually, I've found a rather neat solution -- the device manager!!

I think this is a better solution than the method I use. I haven't read
TN#71, so there are some points I do not understand. How about some code
fragments?

>Unfortunately, there's one problem - Think C 3.0!!!
>My driver has to be in the system heap, so it can stay alive all the time.
>But while THINK C lets you set the resource flags for a code resource, it
>doesn't let you do this for a driver.  So every time, after building the
>driver, I have to start ResEdit, and set the system heap bit.

Ok. I had the same problem when building an INIT with LSC 2.15. That's why
I wrote a small Fixer program to do the job. It also does some other things,
but that's the reason I wrote it for. I posted it here a few months ago and
it has been archived at least at macserve@pucc (BITNET).

>But, wait, it gets worse.  Think C lets you have global data in your
>drivers by allocating a block in the heap for you, locking it, pointing
>A4 to it, and referencing your globals off of A4.  But my data has to
>be in the system heap!!!  So, in my open routine, I have to allocate
>another block, in the system heap this time, copy my globals from where
>Think C put them, and point A4 to my globals.  (Actually, I store the

>There's still one problem, however.  Think C locks the handle that IT
>allocated every time my driver is called.  Suppose I call my driver
>later on, after the application heap has been reinitialized (which is
>OK, since my driver is in the system heap). The handle that THINK C
>allocated is no longer valid.  I wonder if THINK C's driver

Since you open your driver from an INIT, you can simply set the current
zone to the system heap. This will most probably make LSC allocate your
variables from the system heap. I haven't tried this exactly, but I set
the current zone in my INIT before I allocate some buffers & stuff and
it works fine. I couldn't figure out how to make LSC allocate system
heap blocks otherwise. When the driver returns, restore the correct
heap zone. (You have to get it, change it, open your driver and restore
the zone).

I hope this works. Please try to write a document & post it.

Juri Munkki
jmunkki@santra.hut.fi
jmunkki@fingate.bitnet