Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
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 |
} |
} |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-10-30