[comp.sys.mac.programmer] Moving resources into the system file

chaffee@uvm-gen.UUCP (Alex D. Chaffee,231 Votey,,6581273) (07/19/89)

I am writing a utility, called Valet, whose purpose in life is to move
resources from one file to another.  With the exception of one small flaw,
it is ready to be posted into the public domain.  This flaw concerns the
system file: 

I "pack" a resource from another file into the (open) System file.  Using my
"Get info" command assures me that it is intact.  I quit the program and
immediately restart it.  I open the System file again, do a get info, and
bang!  The resource is corrupt.  The name and ID are fine, but
SizeResource() returns -113.  Yes, negative 113, or -111, or some
arbitrarily huge number.  ResEdit doesn't like it either.  But if I open
ResEdit after "packing" but before quitting (I'm using MultiFinder) and open
the resource, it looks fine; then if I quit both and rerun Valet, _the
resource _is_ fine_.  Apparently ResEdit knows how to save the resource, but
I don't.

This only happens under MultiFinder.  Under Finder, packing, quitting and
restarting works perfectly.

Before I order the Programmer's Guide to MultiFinder (which I somehow doubt
would be much help, since I have done nothing even remotely sneaky or
Finder-dependent), could someone tell me what, if anything, I can do to
ensure that a resource I write to the system file stays healthy?

The Fine Print:  I am using a Mac SE, 2.5 megs, LightspeedC C v3.0.  The
problem occurs with or without the symbolic debugger, and from a standalone
app; under Finder, it works when Run or launched.  I have trashed and
rebuilt my project, reinstalled LSC, reinstalled the System (dozens of times
:-), and run under an INIT-free system.

The program flow of "Packing" consists of:
SetResLoad(FALSE);
UseResFile(fileToPack);
count = Count1Resources(type);
for (i = 1; i <= count; ++i) {
        Hand = Get1IndResource(type, i);        /* read the next resource */
        [error checking]
        [ add resource data to an internal data structure ]
}       
SetResLoad(TRUE);
for (    [every resource] ) 
{       [check if it exists]    
        UseResFile(fileToPack);
        SetResLoad(TRUE);
        LoadResource(theItem->H);       if (ResError()) ...
        DetachResource(theItem->H);     if (ResError()) ...
        HNoPurge(theItem->H);
        UseResFile(masterFile); 
        AddResource(theItem->H, theItem->species, newID, newName);      
        if (ResError()) ...
        WriteResource(theItem->H);      if (ResError()) ...
}
[erase internal data structure, without touching the resource handles]
CloseResFile(fileToPack);
UpdateResFile(masterFile);

"Quitting" is:
        if ([the system file is open]) UpdateResFile(...);
        else CloseResFile(...);
        [dispose of all data -- but don't touch the resource handles]

I have avoided (I believe) all the obvious pitfalls, like calling
CloseResFile on the system file.  I have error-checked beyond the limits of
sanity.  And IT WORKS UNDER UNIFINDER.  Please, somebody... help... 

Alex Chaffee
chaffee@emily.uvm.edu
____________________________

tim@hoptoad.uucp (Tim Maroney) (07/20/89)

Yum, this was a meaty one.

In article <1230@uvm-gen.UUCP> chaffee@uvm-gen.UUCP
(Alex D. Chaffee,231 Votey,,6581273) writes:
>I "pack" a resource from another file into the (open) System file.  Using my
>"Get info" command assures me that it is intact.  I quit the program and
>immediately restart it.  I open the System file again, do a get info, and
>bang!  The resource is corrupt.  The name and ID are fine, but
>SizeResource() returns -113.  Yes, negative 113, or -111, or some
>arbitrarily huge number.  ResEdit doesn't like it either.  But if I open
>ResEdit after "packing" but before quitting (I'm using MultiFinder) and open
>the resource, it looks fine; then if I quit both and rerun Valet, _the
>resource _is_ fine_.  Apparently ResEdit knows how to save the resource, but
>I don't.
>
>This only happens under MultiFinder.  Under Finder, packing, quitting and
>restarting works perfectly.

First, I want to thank you for describing your system configuration and
development system, an important step that most people omit.

>The program flow of "Packing" consists of:
>for (    [every resource] ) 
>{       [check if it exists]    
>        UseResFile(fileToPack);
>        SetResLoad(TRUE);
>        LoadResource(theItem->H);       if (ResError()) ...
>        DetachResource(theItem->H);     if (ResError()) ...
>        HNoPurge(theItem->H);
>        UseResFile(masterFile); 
>        AddResource(theItem->H, theItem->species, newID, newName);      
>        if (ResError()) ...
>        WriteResource(theItem->H);      if (ResError()) ...
>}
>[erase internal data structure, without touching the resource handles]
>CloseResFile(fileToPack);
>UpdateResFile(masterFile);

OK, I think I see the problem.  Do a ReleaseResource after the
WriteResource.  What seems to be happening is that under MultiFinder,
ExitToShell is not releasing application-specific copies of the system
file resources.  Therefore, when you quit, the resources are trashed,
being in an invalid heap.  (Actually, the trashing probably happens the
next time you launch a program.)  Since you have already done the
WriteResource, there should not be a problem from the ReleaseResource,
and it should cause the resource to be re-fetched from the system file
when someone else wants to use it.

ExitToShell *should* be releasing application-heap copies of the system
file resources under MultiFinder.  I know it does under UniFinder.
However, if you think about it, there must be some sort of weird
special casing involved.  There's a dearth of internal information on
MultiFinder, but a little poking with MacsBug reveals that different
applications each have their own copy of the system resource map.  So
one would think that changes made to the system file from one
application would invalidate other maps, and they'd have to be
automatically refreshed somehow.  If they weren't refreshed, their file
indices would be bad, for one thing.

So suppose this refreshing happens on your AddResource or WriteResource
-- then when the other maps get refreshed, something has to fill in the
new resource slots in their resource maps.  If this is just the copy of
the resource in your application heap, then everybody gets a handle
into your app. heap inserted in their system resource map.  When that
heap dies, then their copies are trashed.

ReleaseResource might work if it goes and changes everyone's copy, but
not otherwise.  I notice that if you close the window with ResEdit,
then the resource saves OK -- note that ResEdit does do a
ReleaseResource when it closes a resource window, so that would seem to
indicate that doing your own ReleaseResource will do the trick.

This is all out in outer space as far as degree of speculation goes,
but it seems to be consistent with your symptoms.  The upshot seems to
be that you shouldn't make any changes to the system resource file from
your application, because the undocumented nature of all this stuff
makes whatever you do likely to break.  There's no really good reason
for anyone but the Font/DA Mover and ResEdit to make system file
changes, so why don't you just have Valet refuse to mess with the
system file?

PS.  Your AddResource will add a duplicate if the resource already
exists in the target file.  You should check for its existence first,
then remove it if it exists.  (You could also resize it and copy the
source resource into the resized handle, but that wouldn't fit the way
you've coded this very well.)
-- 
Tim Maroney, Mac Software Consultant, sun!hoptoad!tim, tim@toad.com

"Everything that gives us pleasure gives us pain to measure it by."
    -- The Residents, GOD IN THREE PERSONS

brecher@well.UUCP (Steve Brecher) (07/22/89)

In article <8065@hoptoad.uucp>, tim@hoptoad.uucp (Tim Maroney) writes:

> ExitToShell *should* be releasing application-heap copies of the system
> file resources under MultiFinder.  I know it does under UniFinder.
> However, if you think about it, there must be some sort of weird
> special casing involved.  There's a dearth of internal information on
> MultiFinder, but a little poking with MacsBug reveals that different
> applications each have their own copy of the system resource map.

Application heap handles are removed from the System map by RsrcZoneInit,
which is called during the launch process under uniFinder but not called
under MultiFinder.

There is only one copy of the System map.  Each layer has its own resource
map chain (TopMapHndl is switched), but each chain links to the System map
in the system heap.

[Tim's diagnosis (not quoted) of the original problem was correct, i.e.,
adding application heap handles to the System map under MultiFinder and
then quitting without releasing the resources leaves dangling handles in
the System map.]
-- 

brecher@well.UUCP (Steve Brecher)

tim@hoptoad.uucp (Tim Maroney) (07/23/89)

In article <8065@hoptoad.uucp>, tim@hoptoad.uucp (Tim Maroney) writes:
> ExitToShell *should* be releasing application-heap copies of the system
> file resources under MultiFinder.  I know it does under UniFinder.
> However, if you think about it, there must be some sort of weird
> special casing involved.  There's a dearth of internal information on
> MultiFinder, but a little poking with MacsBug reveals that different
> applications each have their own copy of the system resource map.

In article <12810@well.UUCP> brecher@well.UUCP (Steve Brecher) writes:
>Application heap handles are removed from the System map by RsrcZoneInit,
>which is called during the launch process under uniFinder but not called
>under MultiFinder.

That's correct, but there must be some analogous call which is made
under MultiFinder.  Otherwise, copies of resources in the system map
that appeared in the application heap of one application would be
trashed when the next application ran.  (Good guess, but false --
see below.)

>There is only one copy of the System map.  Each layer has its own resource
>map chain (TopMapHndl is switched), but each chain links to the System map
>in the system heap.

You're right, I was wrong.  I was looking at SysMap and thinking it was
the handle; in fact, SysMapHndl is the handle.  The illusory four-byte
SysMap changed every time because the second word is actually another
low-memory global, which is switched.  Sigh.

>[Tim's diagnosis (not quoted) of the original problem was correct, i.e.,
>adding application heap handles to the System map under MultiFinder and
>then quitting without releasing the resources leaves dangling handles in
>the System map.]

Yes.  I'm just wondering why there's a difference between adding
resources to the map and just accessing them.  They must be thrown away
somehow if they're in the application heap; when the system decides to
throw them away, what's the difference between one that's been added
recently and one that hasn't?

The only way I can see that they would *not* have to be thrown away
would be if they were not ordinarily read into the application heap.
I've just done a bit more probing with MacsBug, using the HD command,
and I can't find any resources in any application heaps that claim to
be from resource file 2, which is the reference number of the system
file.  In fact, when I check DA Handler with a DA open, whose resources
you would think would be in the DA Handler application heap, there are
no resources except from the DA Handler resource file.

So perhaps under MultiFinder all resources in the system file are read
into the system heap whether they have the system heap bit set or not.

I just looked at the system heap with MacsBug and there are indeed a
number of resources there from file 2 which, when examined by ResEdit,
do not have the system heap bit set.  So that would appear to be the
answer.

So, the original questioner could do a ReleaseResource as I suggested,
but a better approach would be to allocate the resource storage in the
system heap before adding it to the system file.  Then the
ReleaseResource becomes unneccessary.

Thanks for the corrections.  They've spurred me on to a better
understanding of MultiFinder.
-- 
Tim Maroney, Mac Software Consultant, sun!hoptoad!tim, tim@toad.com

"Americans will buy anything, as long as it doesn't cross the thin line
 between cute and demonic." -- Ian Shoales

chaffee@uvm-gen.UUCP (Alex D. Chaffee,,,6581273) (07/25/89)

Tim Maroney (tim@hoptoad.uucp) writes:
>Yes.  I'm just wondering why there's a difference between adding
>resources to the map and just accessing them.  They must be thrown away
>somehow if they're in the application heap; when the system decides to
>throw them away, what's the difference between one that's been added
>recently and one that hasn't?

Actually, the difference is that the added handle pointed to data that was in 
the application heap.  Remember, I was loading a resource from another file, 
detaching it, and then adding it to the system file.  When the application 
quit, this heap was no longer valid, but the handle remained.

>So, the original questioner could do a ReleaseResource as I suggested,
>but a better approach would be to allocate the resource storage in the
>system heap before adding it to the system file.  Then the
>ReleaseResource becomes unneccessary.

This is impractical. I would either have to copy the resource between the 
heaps, requiring twice as much memory, or do some pre-load mucking with the 
resource map to set the System Heap bit on, which sounds more dangerous than 
just releasing the resource once IUm through with it.  MultiFinder will load it 
into the system heap the next time anyone wants it anyway.

jerryg@Apple.COM (Jerry Godes) quotes Tech Note 180: MultiFinder Miscellanea

[Confirms that MultiFinder loads all system resources into the system heap.]

I was wrong; it is documented.  Yet my original gripe with this scheme still 
stands.  Isn't MultiFinder supposed to be transparent to programs which don't 
make use of it?   I know, I know... "And the Congress is supposed to represent 
the people... and comic strips aren't supposed to end..."


					 "Gilda Radner isn't supposed to end."

					"Probably the Government's fault."

__________________________			Bloom County
Alex Chaffee
chaffee@emily.uvm.edu
____________________________

brecher@well.UUCP (Steve Brecher) (07/26/89)

In article <8102@hoptoad.uucp>, tim@hoptoad.uucp (Tim Maroney) writes:

> So perhaps under MultiFinder all resources in the system file are read
> into the system heap whether they have the system heap bit set or not.

Correct.  This applies also to resources in suitcase files opened with
Suitcase II.

> So, the original questioner could do a ReleaseResource as I suggested,
> but a better approach would be to allocate the resource storage in the
> system heap before adding it to the system file.  Then the
> ReleaseResource becomes unneccessary.

Allocating the resource storage in the system heap would have avoided the
dangling-handle problem even if ReleaseResource were not called.  But
omitting ReleaseResource would introduce another problem:  heap clogging.  If
not ReleaseResource, then HPurge would be required to avoid possible heap
overflow.  Further, one has better control over application heap management
than system heap management, so I think that the original approach, with
ReleaseResource added, is the best solution.
-- 

brecher@well.UUCP (Steve Brecher)