[comp.sys.mac.programmer] Init Delete Thyself

mxmora@unix.sri.com (Matthew Xavier Mora) (05/23/91)

How do you get an init to delete itself? I have a message init that 
displays a message at startup like, "We have installed new printer drivers
on your machine please call ext xxxx if you have any problems."

After the user clicks ok, the init should delete itself. Currently I have
the Init delete the INIT code resource out of the resource fork. This
makes the init benign, but still leaves the init file in the system folder.
This is not optimal. 

I would like to completly delete the file from the system folder but there
seems to be a few problems. For one, the file is busy and the second thing, 
if you delete a file in the system folder at init time, then you screw up the
index count of the system folder. 

So how do you get an init to delete itself? Do I have to set up some vbl task
or something?

Thanks

Matt

resnick@cogsci.uiuc.edu (Pete Resnick) (05/23/91)

mxmora@unix.sri.com (Matthew Xavier Mora) writes:

>I would like to completly delete the file from the system folder but there
>seems to be a few problems. For one, the file is busy and the second thing, 
>if you delete a file in the system folder at init time, then you screw up the
>index count of the system folder. 

You definitely want Joe Holt's Notification Manager code. Basically,
here's what it does:

1. From the INIT, load up a resource that contains code to be executed
later, including deleting the file, throwing away your notification
string, etc. into a pointer in the System heap. This will be your
Notification Manager response routine.

2. Post the notificication from the INIT that says whatever you want
(or nothing at all for that matter).

3. When the INIT process is done, your response routine is called, and
not at interrupt time, so you can do all sorts of nasty things,
including deleting your file.

4. The last few lines of your response routines will put the address of
ToolScratch, an 8-byte area for tempory storage in low memory, onto the
stack and the instructions for DisposPtr of your response routine and
the JMP to what was in the stack pointer into ToolScratch. This will
remove the response routine from memory, and go on its merry way.

If lots of people write who want the code, I can post it. It is the
cleanest way to do error messages and other things from INITs that I
have ever seen.

pr
--
Pete Resnick             (...so what is a mojo, and why would one be rising?)
Graduate assistant - Philosophy Department, Gregory Hall, UIUC
System manager - Cognitive Science Group, Beckman Institute, UIUC
Internet/ARPAnet/EDUnet  : resnick@cogsci.uiuc.edu
BITNET (if no other way) : FREE0285@UIUCVMD

resnick@cogsci.uiuc.edu (Pete Resnick) (05/24/91)

Well, the mail has been coming in, so I will post the code here with
the original message from Joe Holt. It really works well.

From: jholt@adobe.COM (Joe Holt)
Date: 18 Jun 90 18:03:04 GMT
Subject: Informative INITs (code incl.; was: Re: Need help w/ Public Folder)

michael wrote:
> ASIDE: It sure seems to me that INIT's need some way to communicate with
> users other than just X-ing themselves out.  I've been thinking about a
> mechanism where if there is a problem the user can read about it in the
> Chooser (or Control Panel) when they bring up the normal UI.  The UI
> would say something like, "Couldn't find a Public Folder" or "AppleTalk
> is not turned on."  What do people think of this idea?

I ran into the totally uniformative nature of x'ing the INIT icon out at boot
time while writing Flash (an AppleTalk file transfer enhancement -- similar
to Public Folder -- hello michael, nice to meet you!).  I believe there are
on the order of forty or fifty different reasons why Flash wouldn't install
itself.  Everything from "AppleTalk not installed" and "Yer system's too
old" to "out of memory" and "Flash is damaged".  A lot of help an X is when
joe user is trying to track down the problem.

Of course, bringing up an error window at INIT time is not only contrary to
guidelines and tough to do but also just plain ugly.

I solved the problem by using Apple's Notification Manager.

If Flash has a problem, it gets a string from a STR# resource which describes
the problem and puts it into a pointer in the system heap.  It also loads a
very small code resource and creates a Notification Manager structure. The
code resource is a NM response procedure (read "completion routine") which
gets rid of the structure and string and then itself.

Flash sets up the NM structure and posts a NM message.  Flash then cleans up
its act like a good boy scout and leaves no trace of itself.  At this point
the only things left around are the string, the code resource (detached) and
the NM structure.

The NM message stays dormant until the Mac has finished booting and begins
processing events (most likely in the Finder).  Then the NM displays the
message in an alert and the user gets a nice informative message.  (If you
have Flash and want to see this, take the file "ADSP" out of your system
folder and reboot.)

When the user closes the note, the code stub gets control.  It disposes of
the string, the NM structure and finally itself, leaving no remains.

It works very well.  It requires no disk I/O or temporary files, occupies
about 90 bytes of system heap plus the length of your message, and has the
advantage of being 100% compatible with current and future systems, without
introducing a new concept to the user (e.g. "Go to the Chooser to read
startup error messages" -- yuck!).

What follows are the three Think C files StartupError.h, StartupError.c, and
StartupError RESP.c.  The first two are used within your INIT's project.
The third is the code resource which must be compiled separately.  If
there are any questions that might be of interest to the net, please
post them here.


-------- CUT HERE FOR StartupError.h --------


/****************************************************************************
 ***
 *** StartupError.h
 ***
 *** Informative error messages from INITs
 ***
 *** History:
 ***   jhh 18 jun 90 -- response to news posting
 ***
 ***/

#ifndef _H_STARTUP_ERROR
#define _H_STARTUP_ERROR


/****************************************************************************
 **
 ** Public Functions
 **
 **/

void
StartupError(int errorNumber);


#endif  /* ifndef _H_STARTUP_ERROR */


-------- CUT HERE FOR StartupError.c --------


/**-------------------------------------------------------------------------
 **
 ** Include Files
 **
 **/

#include "StartupError.h"


/**-------------------------------------------------------------------------
 **
 ** Private Macros
 **
 **/

/**

    T_NMInstall and T_Unimplemented are Mac toolbox trap numbers used to
    test for the existence of the Notification Manager.

 **/

#define T_NMInstall         (0xA05E)
#define T_Unimplemented     (0xA89F)

/**

    STARTUP_ERROR_STR_ is the resource ID of the STR# containing the
    error message corresponding to the error number passed to
    StartupError().

    RESPONSE_RESP is the resource ID of the RESP code resource compiled
    separately and stuck in your INIT's resources.

 **/

#define STARTUP_ERROR_STR_  (128)
#define RESPONSE_RESP       (128)


/***************************************************************************
 ***
 *** void    StartupError(int errorNumber);
 ***
 *** When your INIT runs into a problem, clean things up, show the X'ed
 *** version of your icon and call StartupError() with an error number
 *** corresponding to the message you want displayed.
 ***
 *** History:
 ***   jhh 18 jun 90 -- response to news posting
 ***
 ***/

void
StartupError(int errorNumber)
{
    register NMRec  *note;
    register Handle responseCode;
    register long   size;
    register THz    svZone;
    Str255          errorText;

/**

    Make sure we've got a Notification Manager.

 **/

    if (NGetTrapAddress(T_NMInstall, OSTrap) !=
            NGetTrapAddress(T_Unimplemented, ToolTrap)) {

/**

    All of the memory we allocate from here on out is in the System
    Heap.  First create a Notification Manager record and fill it in.
    Note that you can expand this notification method with sounds and
    icons by adding the appropriate code.  See the Notification
    Manager technote #184 for details.

 **/

        svZone = TheZone;
        TheZone = SysZone;
        note = (NMRec *)NewPtr(sizeof(NMRec));
        if (!note)
            goto exit;
        note->qType = nmType;
        note->nmMark = 0;
        note->nmSIcon = 0L;
        note->nmSound = (Handle)-1;

/**

    Get the error message corresponding to the error number given.
    For maximum performance, the STR# resource should be tagged
    "Preload" and not "System Heap".  This way, you can be sure
    the messages will be there even if memory space is the cause of
    the error.

    We create a pointer in the System Heap just big enough for the
    string and copy the string into it.  Point the NM record at this
    string.

 **/

        GetIndString(errorText, STARTUP_ERROR_STR_, errorNumber);
        size = *(unsigned char *)errorText;
        note->nmStr = (StringPtr)NewPtr(size);
        if (!note->nmStr) {
            DisposPtr(note);
            goto exit;
        }
        BlockMove(errorText, note->nmStr, size);

/**

    The response procedure also must be in a pointer in the System
    Heap.  You need to include the compiled code resource in your
    INIT's resources of type 'RESP'.

    Create a pointer just big enough for it and point the NM record
    at it, also.

 **/

        responseCode = GetResource('RESP', RESPONSE_RESP);
        if (!responseCode) {
            DisposPtr(note->nmStr);
            DisposPtr(note);
            goto exit;
        }
        size = GetHandleSize(responseCode);
        note->nmResp = (ProcPtr)NewPtr(size);
        if (!note->nmResp) {
            DisposPtr(note->nmStr);
            DisposPtr(note);
            goto exit;
        }
        BlockMove(*responseCode, note->nmResp, size);

/**

    Now post the note.  As soon as startup is complete, the NM
    will display the note for the user's edification.  Hurrah.

 **/

        NMInstall(note);
exit:
        TheZone = svZone;
    }
}


-------- CUT HERE FOR StartupError RESP.c --------


/**

    Compile this code in a separate project.  Set the project type to
    Code Resource, type 'RESP' ID 128.  Once compiled, you can build it
    and merge it into your INIT's resource file ("...project.rsrc") or
    build it into a separate file and use ResEdit to copy it in.  This
    code is independent of the INIT, so it can be used as-is for any
    INIT you write.

 **/


/**

    ToolScratch is an 8-byte area of low memory used by the Mac toolbox
    and other people as a temporary holding place.

 **/

extern long     ToolScratch : 0x09CE;


/**************************************************************************
 ***
 *** pascal void    main(QElemPtr nmReqPtr);
 ***
 *** This is the code resource type 'RESP' which you need to compile
 *** separately and include among your INIT's resources.
 ***
 *** This code is called when the user closes the Notification Manager's
 *** note dialog.  The NM passes to us the address of the NM record which
 *** was set up by StartupError().  We use this to clean up and leave.
 ***
 *** This is written is assembly for size.  If you have problems with
 *** assembly, I s'pose it could be written in C, but the trick at the
 *** end would be hard to duplicate...
 ***
 *** History:
 ***   jhh 18 jun 90 -- response to news posting
 ***
 ***/

pascal void
main(QElemPtr nmReqPtr)
{
    asm {

/**

    First remove the note from the Notification Manager's notification
    queue.

 **/

            move.l      nmReqPtr, A0
            _NMRemove

/**

    Grab the string's address from the NM rec and dispose of it.  Then
    get rid of the NM rec itself.

 **/

            move.l      nmReqPtr, A0
            move.l      OFFSET(NMRec,nmStr)(A0), A0
            _DisposPtr
            move.l      nmReqPtr, A0
            _DisposPtr

/**

    Now the tricky part.  This code lives within a small block in the
    System Heap which we want to get rid of.  But it's not safe to
    dispose of the very block you're calling from!  So, we create a
    little bit of code in ToolScratch which does the dispose for us
    and execute it last.

 **/

            move.l      4(A7), A1
            move.l      #ToolScratch, A0
            move.l      A0, 4(A7)
            move.l      #0x2040A01F, (A0)   ; movea.l D0, A0 / _DisposPtr
            move.w      #0x4ED1, 4(A0)      ; jmp (A1)
            lea         main, A0
            move.l      A0, D0              ; Pascal clobbers A0 on exit
    }
}


-------- END --------

[I apologize for all of the spaces in the posting; I used them instead of
tabs because of the funky tabs my terminal emulator gives.]

--
Pete Resnick             (...so what is a mojo, and why would one be rising?)
Graduate assistant - Philosophy Department, Gregory Hall, UIUC
System manager - Cognitive Science Group, Beckman Institute, UIUC
Internet/ARPAnet/EDUnet  : resnick@cogsci.uiuc.edu
BITNET (if no other way) : FREE0285@UIUCVMD

glenn@gla-aux.uucp (Glenn Austin) (05/28/91)

In article <24620@unix.SRI.COM>, mxmora@unix.sri.com (Matthew Xavier Mora) writes:
> How do you get an init to delete itself? I have a message init that 
> displays a message at startup like, "We have installed new printer drivers
> on your machine please call ext xxxx if you have any problems."

Try using the Notification Manager.  However, this will only work if you
are running MultiFinder or System 7.0.

Simply install a notification procedure.

===============================================================================
| Glenn L. Austin                | "Turn too soon, run out of room.           |
| Macintosh Wizard and           |    Turn too late, much better fate."       |
| Auto Racing Driver             |   -- Jim Russell Racing School Instructors |
|-----------------------------------------------------------------------------|
| Usenet:  glenn@gla-aux.uucp         | CI$:       76354,1434                 |
| GENie:   G.AUSTIN3                  | AOnline:   GAustin                    |
===============================================================================

jcav@quads.uchicago.edu (john cavallino) (05/29/91)

In article <0E010021.skluad@gla-aux.uucp> glenn%gla-aux.uucp@skinner.cs.uoregon.edu writes:
>In article <24620@unix.SRI.COM>, mxmora@unix.sri.com (Matthew Xavier Mora) writes:
>> How do you get an init to delete itself? I have a message init that 
>> displays a message at startup like, "We have installed new printer drivers
>> on your machine please call ext xxxx if you have any problems."
>Try using the Notification Manager.  However, this will only work if you
>are running MultiFinder or System 7.0.

The Notification Manager is present in System 6.0 and newer, whether or not
Multifinder is running.

-- 
John Cavallino                      |     EMail: jcav@midway.uchicago.edu
University of Chicago Hospitals     |    USMail: 5841 S. Maryland Ave, Box 145
Office of Facilities Management     |            Chicago, IL  60637
B0 f++ w c+ g+ k s(+) e+ h- pv (qv) | Telephone: 312-702-6900