Just Finder.c

/*
    File:       Just Finder.c
 
    Contains:   A System 7 application that kills all processes with faces except the Finder.
                Handy for working with Fonts or any other component where you get that blasted
                Finder alert saying "Close everything but the Finder before doing this."
    
    Written by: Matt Deatherage 
 
    Copyright:  Copyright © 1993-1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source code" after having made
                changes. If you're going to re-distribute the source, we require that you make
                it clear in the source that the code was descended from Apple sample source
                code, but that you've made changes.
 
    Change History (most recent first):
                7/26/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
 
*/
 
/*------ Includes ------------------------------------------------------------------------*/
#include <quickdraw.h>
#include <fonts.h>
#include <windows.h>
#include <menus.h>
#include <textedit.h>
#include <dialogs.h>
#include <errors.h>
#include <types.h>
#include <string.h>
#include <processes.h>
#include <Events.h>
#include <AppleEvents.h>
 
/*------ Prototypes ----------------------------------------------------------------------*/
 
void InstallAEHandlers(void);
pascal OSErr oappHandler(AppleEvent *theAppleEvent, AppleEvent *reply, long refCon);
pascal OSErr odocHandler(AppleEvent *theAppleEvent, AppleEvent *reply, long refCon);
pascal OSErr pdocHandler(AppleEvent *theAppleEvent, AppleEvent *reply, long refCon);
pascal OSErr quitHandler(AppleEvent *theAppleEvent, AppleEvent *reply, long refCon);
OSErr CheckRequiredParms(AppleEvent *theAppleEvent);
Boolean ShouldWeKill(ProcessInfoRec* processInfo);
void KillThemAll(void);
 
/*------ main ----------------------------------------------------------------------------*/
 
void main()
 
{
 
GrafPort myPort;
 
 
    InitGraf((Ptr) &qd.thePort);
    OpenPort((GrafPtr) &myPort);
    InitFonts();
    InitWindows();
    InitMenus();
    TEInit();
    InitDialogs(nil);
    InitCursor();
    
    KillThemAll();
    
 
} /* main */
 
/*******************************************************************************************
*
* According to the rules, all applications that say they're high-level event aware must
* respond to the four required Apple events.  This application has no user interface and
* doesn't deal with documents at all, nor will it quit prematurely.  However, rules is
* rules, so we install four very minimal required event handlers.  The ones with parameters
* return an OSErr that indicates whether all the parameters were read (they weren't, if
* there were any, so it usually returns errAEEventNotHandled).
*
*******************************************************************************************/
 
 
 
/*------ InstallAEHandlers ---------------------------------------------------------------*/
 
void InstallAEHandlers(void)
{
 
    AEInstallEventHandler(kCoreEventClass,kAEOpenApplication,NewAEEventHandlerProc(oappHandler),
                          0,false);
    AEInstallEventHandler(kCoreEventClass,kAEOpenDocuments,NewAEEventHandlerProc(odocHandler),
                          0,false);
    AEInstallEventHandler(kCoreEventClass,kAEPrintDocuments,NewAEEventHandlerProc(pdocHandler),
                          0,false);
    AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,NewAEEventHandlerProc(quitHandler),
                          0,false);
}
 
/*******************************************************************************************
*
* CheckRequiredParams is a common Apple event utility routine -- it returns noErr if
* someone has retrieved all the required parameters from the Apple event passed.  If there
* are still some left, it returns errAEEventNotHandled.
*
* The check is simple -- look for the "missed keyword" attribute.  If there isn't one,
* everything is fine.
*
*******************************************************************************************/
 
/*------ CheckRequiredParms ---------------------------------------------------------------*/
 
OSErr CheckRequiredParms(AppleEvent *theAppleEvent)
{
 
    OSErr myErr;
    DescType attrType;
    Size attrSize;
    
    myErr = AEGetAttributePtr(theAppleEvent, keyMissedKeywordAttr, typeWildCard,
                              &attrType, 0L, 0, &attrSize);
                              
    if (myErr == errAEDescNotFound)
        myErr = noErr;
        
    if (myErr == noErr)
        myErr = errAEEventNotHandled;
        
    return(myErr);
    
}
 
/*******************************************************************************************
*
* The core AE handlers don't do very much except return errors saying "I didn't do that."
*
*******************************************************************************************/
 
/*------ The core AE handlers -------------------------------------------------------------*/
 
pascal OSErr oappHandler(AppleEvent *theAppleEvent, AppleEvent *reply, long refCon)
{
    #pragma unused(reply,refCon)
 
    return(CheckRequiredParms(theAppleEvent));
 
}
 
pascal OSErr odocHandler(AppleEvent *theAppleEvent, AppleEvent *reply, long refCon)
{
    #pragma unused(theAppleEvent,reply,refCon)
    return(errAEEventNotHandled);
 
}
 
pascal OSErr pdocHandler(AppleEvent *theAppleEvent, AppleEvent *reply, long refCon)
{
    #pragma unused(theAppleEvent,reply,refCon)
    return(errAEEventNotHandled);
 
}
 
pascal OSErr quitHandler(AppleEvent *theAppleEvent, AppleEvent *reply, long refCon)
{
    #pragma unused(reply,refCon)
    return(CheckRequiredParms(theAppleEvent));
 
}
 
/*******************************************************************************************
*
* ShouldWeKill is the routine that decides if a given process, as specified by a
* ProcessInfoRec, should be killed.  We kill all foreground processes that aren't this
* application or the Finder.  The ProcessInfoRec must be fully initialized by a routine
* very similar to GetProcessInfo or else this routine will probalby do very strange things.
*
*******************************************************************************************/
 
/*------ ShouldWeKill ---------------------------------------------------------------------*/
 
Boolean ShouldWeKill(ProcessInfoRec* processInfo)
{
    
    OSErr myErr;
    ProcessSerialNumber thisProcess;
    Boolean twinProcesses;
    
    thisProcess.highLongOfPSN = 0;
    thisProcess.lowLongOfPSN = kCurrentProcess;     // set up the PSN for this application
    
    myErr = SameProcess(&(processInfo->processNumber), &thisProcess, &twinProcesses);
                                                    // sets up twinProcesses to contain TRUE
                                                    // if the passed process and this one are
                                                    // one and the same
    
    if (!(twinProcesses) &&                         // if this isn't _this_ process and
       !(processInfo->processSignature == 'MACS' && processInfo->processType == 'FNDR') &&
                                                    // if it's not the Finder and
       !(processInfo->processMode & modeOnlyBackground))
                                                    // if it's not background-only then
       {
        return true;                                // Yup, kill it, or do other things to it here.
        } else {
        return false;                               // Nope, it's been spared, or do other things.
       }
 
}
 
/*******************************************************************************************
*
* From looking at published sample code such as ProcDoggie and KillEveryoneButMe (1.0.1),
* you'd think killing a process is a relatively straight-forward thing to do.
*
* Think again.
*
* This took quite some time to figure out, thanks to obscure errors like -603 coming back
* from the Process Manager at strange times.  Here's what I learned, some of which is kind
* of obvious and some of which isn't.
*
* 1.  Don't call GetNextProcess on a process that you've just killed.  It tends to confuse
*     things.  (duh...)  Instead, once we kill a process, we start over at the beginning
*     of the process list, skipping those processes we don't wish to kill.  You know
*     you're done when you hit the end of the process list without killing any processes.
*
* 2.  After killing a process, the Process Manager expects you to keep processing events.
*     Remember the three calls to EventAvail() you have to do at the beginning of most
*     real applications before you become the front process?  In similar ways, the Process
*     Manager might become confused if you're firing off lots of kill events but never
*     call WaitNextEvent to get any.
*
*     So how many times should you call WaitNextEvent?  I've seen numbers from 4 to 25,
*     and they didn't necessarily work for me.  Instead, we call WaitNextEvent until we
*     get a null event, the system's surefire way of indicating it's not doing anything
*     major at this time.  If we get a high-level event, we call AEProcessAppleEvent to
*     follow the rules -- but we ignore other events.
*
*     Note that this might not work for you if you're an application with windows and
*     the like, because you could get an update event.  If you don't process the update
*     event, it won't go away.  This application has no windows and doesn't have to
*     worry about that.
*
*******************************************************************************************/
 
 
/*------ KillThemAll ----------------------------------------------------------------------*/
 
void KillThemAll(void)
{
 
    ProcessSerialNumber theProcess;
    ProcessInfoRec ourProcessInfo;
    OSErr myErr;
    Str31 theProcessName;
    FSSpec theProcessSpec;
    AppleEvent ourQuitEvent, ourReplyEvent;
    AEAddressDesc ourPSNDesc;
    EventRecord theEvent;
    
    // set up the ourProcessInfo record to point to space we've allocated...
    
    ourProcessInfo.processInfoLength = sizeof(ProcessInfoRec);
    ourProcessInfo.processName = (StringPtr)&theProcessName;
    ourProcessInfo.processAppSpec = &theProcessSpec;
    
    theProcess.highLongOfPSN = 0;
    theProcess.lowLongOfPSN = kNoProcess;       // return the first process
 
    while (true) 
        {
        
        // Call WaitNextEvent until we get a null event, meaning all is calm
        
        do {
            WaitNextEvent(everyEvent, &theEvent, 600L, 0L);
            if (theEvent.what == kHighLevelEvent)
                AEProcessAppleEvent(&theEvent);
            } while (theEvent.what != nullEvent);
            
        myErr = GetNextProcess(&theProcess);
        if (myErr == procNotFound)  
            break;                              // if no process, we're done!
 
        myErr = GetProcessInformation(&theProcess,&ourProcessInfo);
        // Check to see if we want to kill this process
            
        if (ShouldWeKill(&ourProcessInfo))
            {
        
            // Create a process serial number descriptor for this process
            
            myErr = AECreateDesc(typeProcessSerialNumber, (Ptr)&theProcess,
                                 sizeof(theProcess), &ourPSNDesc);
            if (myErr == noErr)
                {
                
                // Create the 'quit' Apple event for this process.  This might return error
                // -603 for desk accessories if you don't call WaitNextEvent enough times
                // like above.
                
                myErr = AECreateAppleEvent(kCoreEventClass,kAEQuitApplication, &ourPSNDesc,
                                           kAutoGenerateReturnID, kAnyTransactionID, &ourQuitEvent);
                if (myErr == noErr)
                    {
                    
                    // send the event 
                    
                    myErr = AESend(&ourQuitEvent, &ourReplyEvent, kAENoReply,
                                   kAENormalPriority, kNoTimeOut, 0L, 0L);
                                   
                    if (myErr == noErr)
                        {
                        
                        // Sadly, some applications won't respond to Apple events and do
                        // what they're supposed to do until they're brought to the front,
                        // so we do that, and continue our work in the background.
 
                        SetFrontProcess(&theProcess);
                        
                        }
 
                        // dispose of the event only if we succesfully created it.
                        
                        myErr = AEDisposeDesc(&ourQuitEvent);
                    }
                
                // dispose of the descriptor only if we succesfully created it.
                
                myErr = AEDisposeDesc(&ourPSNDesc);
            
                }
            
            // Now, since we killed a process, we don't want to call GetNextProcess
            // on the one we just eliminated!  (That would be bad.)  So we reset our
            // process record to contain the serial number of "no process", so the
            // next call to GetProcessInfo returns the first process, and we start
            // all over again.
            
            theProcess.highLongOfPSN = 0;
            theProcess.lowLongOfPSN = kNoProcess;       // return to the first process
            }
        }
}