AppleEventCore.c

/*------------------------------------------------------------------------------
*
*  Apple Developer Technical Support
*
*  AppleEvent specific routines
*
*  Program:    AEObject-Edition Sample
*  File:       AppleEventCore.c - C Source
*
*  by:         C.K. Haun <TR>
*
*  Copyright © 1990-1992 Apple Computer, Inc.
*  All rights reserved.
*
*------------------------------------------------------------------------------
* This file handles the generic AppleEvent handlers, as well as intialization
* and installing the handlers I'll need for this application.
* Also installs my coercion handlers and has the interaction Idle proc.
* Other, more specific AE handling (like edition functions) are in the functional 
* area files.
* Functions that deal specifically with AppleEventª Objects are in the 
* file AEObject.c.
*----------------------------------------------------------------------------*/
/* AESample v1: Removed all ShowMe error notifications, replaced with macro to display */
/* errors non-intrusively in AESatus window */
 
#define __AEM__
 
#pragma segment Main
/* DoHighLevel will be in main, just because */
#include "Sampdefines.h"
 
 
 
/* DoHighLevel processes our high level events */
/* All the HLEs that come into my application get dispatched to here. */
/* All I do is call AEProcessAppleEvent, because I do not support any */
/* other types of High Level Events, justs AEs. */
void DoHighLevel(EventRecord AERecord)
{
    OSErr fred2;
    /* Process the event. */
    /* Here's what happens; 
    1) If this is an AppleEvent, and we have a handler installed for this event, 
    AEProcessAppleEvent jumps to that handler 
    else
    2) If there is a system handler for this event, AEProcessAppleEvent jumps to that
    else
    3) return errEventNotHandled.
    */
    /* ¥¥¥¥ NOTE: If you are sending AppleEvents to yourself (using a PSN of kCurrentProcess */
    /* in the address AEDesc to AECreateAppleEvent) then events sent to yourself that */
    /* way will NOT come here!  They will be dispatched directly to the  */
    /* correct handler by the AppleEvent manager!  Try it in this sample. */
    fred2 = AEProcessAppleEvent(&AERecord);
    if ((fred2 != userCanceledErr) && (fred2 != noErr) && (fred2 != errAEEventNotHandled))
        mAEErrorDisplay("\pAppleEvent Proccessing.", fred2)
        /* if it was a userCanceledErr (from the quit routine) and the reply has been */
        /* sent from there already */
        /* if it's a errAEEventNotHandled, then someone sent us  an event we're not prepared for, */
        /* that is not a reason to put up a dialog.  Since anyone can send us events, then */
        /* we could get all kinds of stray stuff, so just ignore it. */
        /* you may want to check for debugging reasons */        
}
 
/* end DoHighLevel */
 
#pragma segment AppleEvents
/***************************************************************************************
 
MissedAnyParameters
 
Used to check for any unread required parameters. Returns true if we missed at
least one.
 
*****************************************************************************************/
Boolean MissedAnyParameters(AppleEvent *message)
{
    OSErr err;
    DescType ignoredActualType;
    AEKeyword missedKeyword;
    Size ignoredActualSize;
    EventRecord event;
    
    err = AEGetAttributePtr(message, keyMissedKeywordAttr, typeKeyword, &ignoredActualType, (Ptr)&missedKeyword,
                            sizeof(missedKeyword), &ignoredActualSize);
    
    /* no error means that we found some more.*/
    
    if (!err) {
        event.message = *(long *)&ignoredActualType;
        event.where = *(Point *)&missedKeyword;
        mAEErrorDisplay("\pMissedAnyParameters: got parameters I don't know what to do with.", err)
        err = errAEEventNotHandled;
    }
    
    /* errAEDescNotFound means that there are no more parameters. If we get */
    /* an error code other than that, flag it. */
    
    else if (err != errAEDescNotFound) {
        mAEErrorDisplay("\pMissedAnyParameters: after AEGetAttributeDesc.", err)
        
    }
    return(err != errAEDescNotFound);
}
 
/* This is the standard Open Application event.  You'll get this as one of the (if not the ) */
/* first events in your application.  So, we open up a blank document */
/* You will _NOT_ get this if you were launched with an event (like an 'odoc' or 'pdoc' ) */
/* so do _not_ do application initialiaztion things here!  This routine may never */
/* get called! */
pascal OSErr AEOpenHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
{
    OSErr myErr = noErr;
#pragma unused (messagein,refIn)
    gCurrentReply = reply;
 
    ChangePlane(AddNewWindow(true));                        /* open our initial, blank window */
    /* and make sure it's the front window */
    
    mAEErrorDisplay("\pOpen App Handler", myErr)
    gCurrentReply = nil;
    return(myErr);
}
 
/* end AEOpenHandler */
 
/* Open Doc, opens our documents.  Remember, this can happen at application start AND */
/* anytime else.  If your app is up and running and the user goes to the desktop, hilites one */
/* of your files, and double-clicks or selects Open from the finder File menu this event */
/* handler will get called. Which means you don't do any initialization of globals here, or */
/* anything else except open then doc.  */
/* SO -- Do NOT assume that you are at app start time in this */
/* routine, or bad things will surely happen to you. */
 
pascal OSErr AEOpenDocHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
{
    OSErr myErr = noErr;
#pragma unused ( refIn)
    gCurrentReply = reply;
 
    /* In previous versions I was coming to the front on this message.  This is _not_ */
    /* the proper thing to do.  I have changed things to call AEInteractWithUser.  */
    /*  The user (or the AEm will bring us forward if appropriate).  But we may _not_ be  */
    /*required to come forward, it may be perfectly reasonable to stay in the background */
    /* so don't do anything unless you really need to, like to put up a dialog */
    /* which we don't have to, so just press on */
    myErr = processOpenPrint(messagein, false);
    gCurrentReply = nil;
    return(myErr);
}
 
/* Same logic as OpenDoc */
pascal OSErr AEPrintHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
{                                                           /* no printing handler in yet, so we'll ignore this */
    /* the operation is functionally identical to the ODOC event, with the additon */
    /* of calling your print routine.  */
#pragma unused (refIn)
    gCurrentReply = reply;
    processOpenPrint(messagein, true);
    gCurrentReply = nil;
    return(noErr);
}
 
/* Standard Quit event handler, to handle a Quit event from the Finder, for example.  */
/* ¥¥¥¥¥ DO NOT CALL EXITTOSHELL HERE ¥¥¥¥¥ or you will never have a happy life.  */
/* OK, it's a few months after I wrote that comment, and I've seen a lot of code */
/* come through DTS that calls ExitToShell from quit handlers.  Let me explain... */
/* When an AppleEvent Handler is called (like this quit handler) you are ALMOST */
/* 100% in your application world.  A5 is right, you can call any toolbox function, */
/* you can call your own routines, everything _seems_ like you are in complete  */
/* control.  Well, almost but not quite.  The routine has been dispatch to from the */
/* AppleEvent Manager's space, so you _must_ return to that at some point! */
/* Which is why you can't call ETS from here.  When you call ExitToShell from an */
/* AE Handler, the most likely thing that happens is the FInder quits, and your  */
/* application keeps running.  Which ain't what you want, y'know? */
/* so, DON'T CALL EXITTOSHELL FROM AN APPLEEVENT HANDLER!!!!!!!!!!!!!! */
pascal OSErr AEQuitHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
{
#pragma unused (messagein,refIn)
    OSErr theErr;
    Str32 userCanx;
    gCurrentReply = reply;
    theErr = PrepQuit();                                    /* prepQuit sets the Stop flag for us.  It does _NOT_ quit, you */
    /* should NEVER quit from an AppleEvent handler.  Calling */
    /* ExitToShell here would blow things up */
    
    if (!theErr){
    gCurrentReply = nil;
        return(noErr);}
    if (theErr == (userCanceledErr)) {
        /* reply to the application that told us to quit */
        GetIndString(userCanx,kGeneralStrings,kUCanxString);
        AEPutParamPtr(reply, keyErrorNumber, typeLongInteger, (Ptr)&theErr, sizeof(OSErr));
        AEPutParamPtr(reply, keyErrorString, typeChar, (Ptr)&userCanx, userCanx[0]);
    }
    gCurrentReply = nil;
        return(theErr);
    
}
 
/* This is the 'ansr', or answer, handler.  You need this if you ever want to */
/* use QueueReply as an option to AESend (which you will) since the reply  */
/* is going to be coming in through your event loop like any other  */
/* AppleEvent.   So here it is */
pascal OSErr AEAnswerHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
{
#pragma unused (refIn) 
    OSErr myErr = noErr;
        gCurrentReply = reply;
    mVerboseOutput("\p\nGot an answer from an event")
    DisplayReturnedData((AEDesc *)messagein);
    gCurrentReply = nil;
    return(myErr);
    /* ¥¥¥¥ NOTE:  One thing you may want to consider, and it's a side-effect of */
    /* the AppleEvent manager's direct dispatching of events if you send them to yourself */
    /* using the kCurrentProcess Process Serial Number constant. */
    /* If you use kCurrentProcess, then the AppleEvent manager will directly dispatch */
    /* to the AppleEvent handler, the event will _not_ go through WaitNextEvent. */
    /* Normally, that's a very good thing.  But there is one potential problem.... */
    /* It means that kQueueReply doesn't quite work the way you think it will when sending to */
    /* yourself.  The reply will _not_ be put in the event queue, instead this Answer hander */
    /* will be called directly.  And if you reply from the answer handler, that will dispatch right  */
    /* back to itself.  And so on.  So be aware that Queued replies are _not_ really  */
    /* queued when you are using kCurrentProcess! */
}
 
 
/* my routine to snatch a section handle out of an AppleEvent */
OSErr GetSectionHandleFromEvent(AppleEvent *AEin, SectionHandle *theSection)
{
    DescType thisType;
    Size returnedSize;
    return(AEGetParamPtr(AEin, keyDirectObject, typeSectionH, &thisType, (Ptr)theSection, sizeof(theSection), &returnedSize));
}
 
/* processOpenPrint handles ODOC and PDOC events.  Both events open a document, one prints it */
OSErr processOpenPrint(AppleEvent *messagein, Boolean printIt)
{
    OSErr err;
    OSErr err2;
    AEDesc theDesc;
    FSSpec theFSS;
    register qq;
    long numFilesToOpen;
    AEKeyword ignoredKeyWord;
    DescType ignoredType;
    Size ignoredSize;
    WindowPtr tWind;
    err = AEGetParamDesc(messagein, keyDirectObject, typeAEList, &theDesc);
    if (err) {
        mAEErrorDisplay("\pGetParamDesc error in Open/Print", err)
    }
    if (!MissedAnyParameters(messagein)) {
        
        /* Got all the parameters we need. Now, go through the direct object, */
        /* see what type it is, and parse it up. */
        
        if (err = AECountItems(&theDesc, &numFilesToOpen)) {
            mAEErrorDisplay("\pAECountItems error in Open/Print", err)
        } else {
            for (qq = 1; ((qq <= numFilesToOpen) && (!err)); ++qq) {
                if (err = AEGetNthPtr(&theDesc, qq, typeFSS, &ignoredKeyWord, &ignoredType, (Ptr)&theFSS, sizeof(theFSS),
                                      &ignoredSize)) {
                    mAEErrorDisplay("\pAEGetNthPtr in Open/Print", err)
                    
                } else {
                    FInfo fileInfo;
                    FSpGetFInfo(&theFSS, &fileInfo);        /* make sure it's a data file */
                    if (fileInfo.fdCreator == kMySignature && fileInfo.fdType == kMyDocumentFileType)
                        tWind = OpenFile(&theFSS);
                    else
                        tWind = nil;                        /* they may have double clicked on the prefs file */
                }
                if (printIt && tWind != nil) {
                    
                    PrintIt(tWind, false);                  /* in Print.c.  Does not yet print, but the idea is there */
                    CloseMyWindow(tWind);
                }
            }                                               /* for qq = ... */
        }                                                   /* AECountItems OK */
    }                                                       /* Got all necessary parameters */
    
    if (err2 = AEDisposeDesc(&theDesc)) {
        mAEErrorDisplay("\pAEDisposeDesc in Open/Print", err2)
    }
    return(err ? err : err2);
}
 
/* This is an AppleEvent Idle function.  It can be passed to AEInteractWithUser */
/* or AESend. */
/* What it does is this; */
/* If some AppleEvent thing is taking a really long time, the AppleEvent manager */
/* will call WaitNextEvent FOR you (yeah, yeah, only one event loop per app, oh well) */
/* and give you a result (limited) if you need to do something.  That something */
/* will only be an update,activate,OSevent, or null event */
pascal Boolean CommonIdleFunction(EventRecord *whatEvent, long *sleeping, RgnHandle *mouseRgn)
{
    extern Boolean gInBackground;
    
    extern RgnHandle mousergn;
    switch (whatEvent->what) {
        case updateEvt:
            DrawIt((WindowPtr)whatEvent->message);          /* draw whatever window needs an update */
            
            break;
        case activateEvt:
            if (whatEvent->modifiers & activeFlag)
                DrawIt((WindowPtr)whatEvent->message);
            break;
        case app4Evt:                                       /* or kOSEvent, I'm old fashioned */
            switch ((whatEvent->message >> 24) & 0x0FF) {       /* high byte of message */
                case suspendResumeMessage:                  /* suspend/resume is also an activate/deactivate */
                    gInBackground = (whatEvent->message & kResumeMask) == 0;
                    if (gInBackground) {
                        SpitClip();                         /* export the clipboard please */
                        /* deactivate the current TextEdit record as necessary */
                        if (FrontWindow() != nil) {
                            windowCHandle tempCH = (windowCHandle)GetWRefCon(FrontWindow());
                            if ((*tempCH)->boxHandle != nil)
                                TEDeactivate((*tempCH)->boxHandle);
                        }
                    } else {
                        if (FrontWindow() != nil) {
                            windowCHandle tempCH = (windowCHandle)GetWRefCon(FrontWindow());
                            if ((*tempCH)->boxHandle != nil)
                                TEActivate((*tempCH)->boxHandle);
                        }
                    }
                    break;
            }
            break;
        case nullEvent:
            AdjustCursor(whatEvent->where, mousergn);
            *sleeping = 20;                                  /* may as well sleep a while */
            *mouseRgn = mousergn;
            break;
     
    }
    return(false);                                          /* If it takes for-ever I will wait, for you...  Sorry. */
}
 
AEIdleUPP gCommonIdleFunctionUPP = NewAEIdleProc(CommonIdleFunction);
 
/* This function sends a GetData AppleEvent to someone, and displays the */
/* results in the AEStatus window */
void SendGetData(short which)
{
    AEDesc thisAddress;
    AppleEvent theEvent;
    AEDesc reply;
    OSErr myErr = noErr;
    /* first make an address */
    MakeAddress(&thisAddress);
    myErr = AECreateAppleEvent(kAECoreSuite, kAEGetData, &thisAddress, kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
    myErr = BuildGetDataEvent(&theEvent, which);            /* in AEObjects.c */
    if (!myErr) {
        /* reply may or may not be used, depending on the reply mode you have set */
        myErr = DoSend(&theEvent, &reply);
        if (!myErr) {
            /* display the results, if we were in WaitReply */
            if (gReplyMode == 1)
                DisplayReturnedData(&reply);
        }
    }
}
/* This function sends a SetData AppleEvent to someone, and displays the */
/* results in the AEStatus window */
void SendSetData(short which)
{
    AEDesc thisAddress;
    AppleEvent theEvent;
    AEDesc reply;
    OSErr myErr = noErr;
    /* first make an address */
    MakeAddress(&thisAddress);
    myErr = AECreateAppleEvent(kAECoreSuite, kAESetData, &thisAddress, kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
    myErr = BuildSetDataEvent(&theEvent, which);            /* in AEObjects.c */
    if (!myErr) { 
        DoSend(&theEvent, &reply);
    }
}
 
 
/* Dialog box handler for the Interaction Settings you want to use for this sample */
void SetInteractionLevels(void)
{
    /* call my common start routine and preset some buttons */
    DialogPtr tdial = CommonDStart(kInteractionDialog, gLocalInteraction + 6, gAESendInteraction + 9);
    short hitItem = 0;
    /* save some stuff in case the user cancels */
    short saveMode1 = gLocalInteraction;
    short saveMode2 = gAESendInteraction;
    short saveMode3 = gAESwitchLayer;
    
    /* one additial item to set */
    SetControlValue(SnatchHandle(tdial, kSwitchLItem), gAESwitchLayer);
    
    /* run the dialog */
    {
        ModalFilterUPP upp = NewModalFilterProc(standardDialogFilter);
    
        while (hitItem != ok && hitItem != cancel) {
            ModalDialog(upp, &hitItem);
            switch (hitItem) {
                case kSelfIItem:
                case kLocalIItem:
                case kAllIItem:
                    /* check boxes, just toggle them */
                    SetControlValue(SnatchHandle(tdial, gLocalInteraction + kSelfIItem), false);
                    gLocalInteraction = hitItem - kSelfIItem;
                    SetControlValue(SnatchHandle(tdial, gLocalInteraction + kSelfIItem), true);
                    break;
                    
                case kNeverIItem:
                case kCanIItem:
                case kAlwaysIItem:
                    /* more toggleable checkboxes */
                    SetControlValue(SnatchHandle(tdial, gAESendInteraction + kNeverIItem), false);
                    gAESendInteraction = hitItem - kNeverIItem;
                    SetControlValue(SnatchHandle(tdial, gAESendInteraction + kNeverIItem), true);
                    
                    break;
                case kSwitchLItem:
                    gAESwitchLayer = (gAESwitchLayer ? false : kAECanSwitchLayer);
                    SetControlValue(SnatchHandle(tdial, kSwitchLItem), gAESwitchLayer);
                    break;
                default:
                    break;
            }
        }
        DisposeDialog(tdial);
        DisposeRoutineDescriptor(upp);
    }
    
    if (hitItem == cancel) {
        /* if they canceled, restore the old values */
        gLocalInteraction = saveMode1;
        gAESendInteraction = saveMode2;
        gAESwitchLayer = saveMode3;
        
    } else {
    /* set interaction levels to what the user picked  */
    AESetInteractionAllowed(gLocalInteraction);
    }
}
 
/* Dialog box handler for the addressing mode you want to use for this sample */
void SetTargetAddress(void)
{
    /* bring up dialog and preset some stuff */
    DialogPtr tdial = CommonDStart(kAddressingDialog, gAddressMode + kSelfAddressCurrItem, 0);
    short hitItem = 0;
    short saveMode = gAddressMode;
    /* if an address is already specified, draw it in the box */
    ParamText(&targetName, "", "", "");
    /* dim out the 'Select Target' button if the right check box is not set */
    if (gAddressMode + kSelfAddressCurrItem != kOtherAppItem)
        HiliteControl(SnatchHandle(tdial, kSelectProcItem), 255);       /* can't find the constant for dimmed, somebody tell me where it is,please */
 
    {
        ModalFilterUPP upp = NewModalFilterProc(standardDialogFilter);
        /* run the dialog */
        while (hitItem != ok && hitItem != cancel) {
            ModalDialog(upp, &hitItem);
            switch (hitItem) {
                case kSelfAddressCurrItem:
                case kSelfAddressPSNItem:
                case kOtherAppItem:
                    /* checkbox toggling, and also setting the hilite state of the 'Set Target' */
                    /* button as necessary */
                    SetControlValue(SnatchHandle(tdial, gAddressMode + kSelfAddressCurrItem), false);
                    gAddressMode = hitItem - kSelfAddressCurrItem;
                    SetControlValue(SnatchHandle(tdial, gAddressMode + kSelfAddressCurrItem), true);
                    if (gAddressMode + kSelfAddressCurrItem != kOtherAppItem)
                        HiliteControl(SnatchHandle(tdial, kSelectProcItem), 255);
                    else
                        HiliteControl(SnatchHandle(tdial, kSelectProcItem), 0);
                    break;
                case kSelectProcItem:
                    /* Call the PPC browser to select a target */
                    BrowseForTarget(&gTargetAddress);
                    break;
                default:
                    break;
            }
        }
        DisposeRoutineDescriptor(upp);
    }
    DisposeDialog(tdial);
    /* restore old mode if they canceled */
    if (hitItem == cancel)
        gAddressMode = saveMode;
    
}
 
/* Dialog box handler for the Reply Settings you want to use for this sample */
void SetReplyMode(void)
{
    DialogPtr tdial = CommonDStart(kReplyModeDialog, gReplyMode + kNoReplyItem, 0);
    short hitItem = 0;
    short saveMode = gReplyMode;
    
    /* set up to gray out the 'no reply' button */
    HiliteControl(SnatchHandle(tdial, kNoReplyItem), 255);
    
    {
        ModalFilterUPP upp = NewModalFilterProc(standardDialogFilter);
        while (hitItem != ok && hitItem != cancel) {
            ModalDialog(upp, &hitItem);
            switch (hitItem) {
                case kNoReplyItem:
                case kWaitReplyItem:
                case kQueueReplyItem:
                    SetControlValue(SnatchHandle(tdial, gReplyMode + kNoReplyItem), false);
                    gReplyMode = hitItem - kNoReplyItem;
                    SetControlValue(SnatchHandle(tdial, gReplyMode + kNoReplyItem), true);
                    break;
                default:
                    break;
            }
        }
        DisposeRoutineDescriptor(upp);
    }
    
    DisposeDialog(tdial);
    if (hitItem == cancel)
        gReplyMode = saveMode;
}
 
 
/*  CoerceBooleanToChar creates a desc that says True or False.  It's not a very */
/* interesting coercion, but it's a nice sample.  I use it for my output window */
pascal OSErr CoerceBooleanToChar(DescType origData, Ptr inPtr, Size theSize, DescType toType, long refCon, AEDesc *result)
{
#pragma unused (refCon)
    OSErr myErr = noErr;
    /* make sure everything is fine first */
    if (origData != typeBoolean || toType != typeChar) {
        /* something is goofy here */
        myErr = errAECoercionFail;
    } else {
        /* a boolean should be two bytes. if it isn't, I'm confused */
        if (theSize == sizeof(short)) {
            short theBool = *((short *)inPtr);
            short index;
            Str32 theText;
            index = theBool ? kTrueWord : kFalseWord;
            GetIndString(theText, kGeneralStrings, index);
            myErr = AECreateDesc(typeChar, (Ptr)&theText[1], theText[0], result);
        } else {
            myErr = errAECoercionFail;
        }
    }
    return(myErr);
}
 
/*  CoerceQDRectToChar is the same kinda thing */
pascal OSErr CoerceQDRectToChar(DescType origData, Ptr inPtr, Size theSize, DescType toType, long refCon, AEDesc *result)
{
#pragma unused (theSize,refCon)
    OSErr myErr = noErr;
    /* make sure everything is fine first */
    if (origData != typeQDRectangle || toType != typeChar) {
        myErr = errAECoercionFail;
    } else {
        short *myRect = (short *)inPtr;
        register qq;
        Str255 theString;
        Str32 tempString;
        Str32 spaceString = "\p ";
        
        theString[0] = 0;
        GetIndString(theString, kGeneralStrings, kSayRectangle);
        for (qq = 0; qq < kFour; qq++) {
            NumToString(*myRect, tempString);
            AppendString(theString, tempString);
            AppendString(theString, spaceString);
            myRect = myRect + 1;                            /* I have had trouble with MPW C doing a += on a pointer, so I'll do it this way */
        }
        myErr = AECreateDesc(typeChar, (Ptr)&theString[1], theString[0], result);
    }
    return(myErr);
}
 
/* these next two I really do find useful, dealing with Toolbox strings */
pascal OSErr CoerceCharToPString(DescType origData, Ptr inPtr, Size theSize, DescType toType, long refCon, AEDesc *result)
{
#pragma unused (origData,toType,refCon)
    OSErr myErr = noErr;
    Str255 theString;
    theString[0] = theSize;
    BlockMove(inPtr, (Ptr)&theString[1], theSize);
    myErr = AECreateDesc(typeMyPString, (Ptr)&theString[0], theString[0] + 1, result);
    
    return(myErr);
}
 
pascal OSErr CoercePStringToChar(DescType origData, Ptr inPtr, Size theSize, DescType toType, long refCon, AEDesc *result)
{
#pragma unused (origData,toType,refCon)
    OSErr myErr = noErr;
    myErr = AECreateDesc(typeChar, (Ptr)(inPtr + 1), theSize - 1, result);
    
    
    return(myErr);
}
 
/* CoerceAliasToTargetID takes an applicaiton alias and coerces it to a process target ID */
/* Of course, to do this is needs to find and launch the application */
/* This handler uses pointers to the data, since the AppleEvent managr can handle this type */
/* of manipulation more efficiently than passing descs.  You can install a desc handler */
/* instead, if you'd like. */
/* I actually don't use this in this sample, but I left it in as an example */
/* of a coercion handler */
pascal OSErr CoerceAliasToTargetID(DescType origData, Ptr inPtr, Size theSize, DescType toType, long refCon, AEDesc *returnID)
{
#pragma unused (origData,toType,refCon)
    OSErr myErr;
    LaunchParamBlockRec launchThis;
    FSSpec theSpec;
    Boolean changed;
    Handle theAlias = NewHandle(theSize);
    HLock(theAlias);
    BlockMove(inPtr, (Ptr)*theAlias, theSize);
    HUnlock(theAlias);
    launchThis.launchAppSpec = &theSpec;
    /* the caller may have already done this, but it doesn't hurt to do it again */
    myErr = ResolveAlias(nil, (AliasHandle)theAlias, launchThis.launchAppSpec, &changed);
    if (myErr)
        return(myErr);
    /* launch the thing */
    launchThis.launchBlockID = extendedBlock;
    launchThis.launchEPBLength = extendedBlockLen;
    launchThis.launchFileFlags = nil;
    /* launchdontswitch because we just want to use the service.  Also, it may be a */
    /* background only application, so like you don't want it to come up, y'know? */
    launchThis.launchControlFlags = launchContinue + launchNoFileFlags + launchDontSwitch;
    launchThis.launchAppParameters = nil;
    myErr = LaunchApplication(&launchThis);
    if (myErr) {
        mAEErrorDisplay("\pLaunchApplication error", myErr)
        return(myErr);
    }
    /* it launched.  the PSN has been stored in the launchProcessSN field, now we have to make */
    /* that a target */
    /* fill in all the details for the target */
    /* we'll just use the PSN to communicate */
    myErr = AECreateDesc(typeProcessSerialNumber, (Ptr)&launchThis.launchProcessSN, sizeof(ProcessSerialNumber), returnID);
    mAEErrorDisplay("\pCreateDesc error after launch", myErr)
    return(myErr);
}
 
 
#undef __AEM__