MoreFinderEvents.cp

    //
    //  MoreFinderEvents 1.0
    //  ====================
    //
    //  an AppleEvents package for non-scriptable versions of Finder
    //
    //  by Pete Gontier, Apple Macintosh Developer Technical Support
    //
    //  Copyright (c) 1996 Apple Computer, Inc., All Rights Reserved.
    //
    //  Complaints and kudos to: <gurgle@apple.com>
    //
    //  Thanks to Jon Pugh for 'FinderEvents.p' XCMD and code review
    //  of this package.
    //
    //  INTRODUCTION
    //  ============
    //
    //  The AppleEvents API is icky and you probably don't relish the
    //  idea of developing an intimate relationship with it. And you
    //  don't feel your application should yet require the scriptable
    //  Finder, which first shipped in System 7.5. Yet you still want
    //  to make Finder do backflips and cartwheels.
    //
    //  Enter MoreFinderEvents.
    //
    //  This package provides you with a painless API for sending some
    //  of the more simple forms of the FinderEvents, which have been
    //  with us since System 7.0.0. With this API you can copy a
    //  file or open a control panel or empty the trash or any of
    //  several other things usually done by Finder at the user's
    //  request.
    //
    //  THEORY OF OPERATION
    //  ===================
    //
    //  There's nothing terribly tricky here. We're just using the
    //  AppleEvents API along with some simple C++ code to make it
    //  a little more palatable. The only thing worth noting is that
    //  Finder doesn't use the AppleEvents API on its end. Apparently
    //  the Finder which shipped with System 7.0.0 needed to feature-
    //  freeze before the AppleEvent Manager, so it has its own code
    //  for examining AppleEvents. Since the Finder code is tailored
    //  specifically to support FinderEvents, it tends to get cranky
    //  when fed data which is not absotively posilutely correct.
    //  If you modify this library and Finder starts crashing, that's
    //  probably why.
    //
    //  BUILDING
    //  ========
    //
    //  I wrote this library intermittently over a period of months with
    //  with Metrowerks CodeWarrior CW7, CW8, and CW9. Consequently,
    //  I don't know for certain whether it compiles with any other
    //  compilers, although it's Universal Headers 2.0-savvy and should
    //  work just fine.
    //
    //  Note that despite the fact that this is C++ code, all entry
    //  points (the function prototypes in "MoreFinderEvents.h") are
    //  declared 'pascal', meaning you can call this code from C and
    //  Pascal. There's no Pascal interface file for this library
    //  at the moment primarily because to me an interface file would
    //  imply that the author had ported "TestFinderEvents.c" to Pascal
    //  to make sure the interface file worked. If someone wants to do
    //  that and send me the results, I'll be more than happy to include
    //  them in future versions of this package.
    //
    //  KNOWN PROBLEMS
    //  ==============
    //
    //  [1] One of the following two things is true:
    //
    //      [a] The handling of 'iconPosition' in
    //      'MFE_DragLow' is buggy.
    //
    //      [b] Finder doesn't handle this parameter properly.
    //
    //  The practical effect is that the icon lands in an unpredictable
    //  position in the destination folder window.
    //
    //  [2] Some FinderEvents result in Finder behavior which doesn't
    //  work well unless Finder is the front application. For example,
    //  you can submit a request to open the Page Setup dialog for a
    //  given folder window and Finder will happily open the Printing
    //  Manager and the appropriate dialog from the background. When
    //  the user brings Finder forward, the dialog won't (always) update
    //  properly. Bad. It's not clear what to do about this; some
    //  developers would probably rather decide for themselves when to
    //  bring Finder forward. I'd love to hear your feedback.
    //
    //  [3] It's been suggested you're better off using the scriptable
    //  Finder. It's said these older FinderEvents will go away
    //  eventually. These things are true, so you should use the
    //  scriptable Finder if it's available. See 'd e v e l o p'
    //  magazine, issue 20, pages 65 to 78 for details. Does this
    //  mean you'll have two chunks of code in your application?
    //  Well... er... yes. Maybe someday I'll update this library so
    //  that a single call will work regardless of the presence of
    //  a scriptable Finder. Wouldn't that be cool?
    //
    //  TO DO
    //  =====
    //
    //  There are a few events defined in Chapter 8 of the AppleEvents
    //  Registry for which I have not yet implemented glue. If you're
    //  a squeaky wheel, I'll try to make sure you get some grease.
    //
    //      move window
    //      size window
    //      zoom window
    //      set view
    //
    //  LEGAL NOTICE
    //  ============
    //
    //  You may incorporate this sample code into your applications
    //  without restriction. This sample code has been provided "AS
    //  IS" and the responsibility for its operation is 100% yours.
    //  You are not permitted to redistribute the source as "Apple
    //  sample 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
    //  code, but that you've made changes.
    //
 
#define SystemSevenOrLater      1
#define OLDROUTINELOCATIONS     0
#define OLDROUTINENAMES         0
 
#include "MoreFinderEvents.h"
 
#ifndef __AEREGISTRY__
#   include <AERegistry.h>
#endif
 
#ifndef __APPLEEVENTS__
#   include <AppleEvents.h>
#endif
 
#if PRAGMA_ALIGN_SUPPORTED
#   pragma push
#   pragma options align=mac68k
#endif
 
struct MFE_Window
{
    long            fWindowType;
    DescType        fAliasType;
    long            fAliasLen;
    AliasRecord     fAliasRecord;
};
 
typedef MFE_Window *MFE_WindowP, **MFE_WindowH;
 
struct FinderEvent : public AppleEvent
{
    OSErr       fErr;
    AppleEvent  fReply;
    AESendMode  fSendMode;
 
    FinderEvent (AEEventClass,AEEventID);
    ~FinderEvent (void);
    OSErr Send (void);
};
 
#if PRAGMA_ALIGN_SUPPORTED
#   pragma pop
#endif
 
enum
{
    kCreatorCode_Finder = 'MACS',
    kCreatorCode_AtEase = 'mfdr'
};
 
static pascal OSErr GetCreatorOfFinderLikeProcess (OSType *processSignature)
{
    OSErr err = noErr;
 
    ProcessSerialNumber     psn     = { kNoProcess, kNoProcess };
    ProcessInfoRec          pir     = { sizeof (pir), nil };
 
    *processSignature = 0;
 
    pir.processAppSpec = nil;
 
    for (;;)
    {
        err = GetNextProcess (&psn);
        if (err)
        {
            if (err == procNotFound) err = noErr;
            break;
        }
 
        err = GetProcessInformation (&psn,&pir);
        if (err) break;
 
        if (pir.processSignature == kCreatorCode_Finder ||
            pir.processSignature == kCreatorCode_AtEase)
        {
            *processSignature = pir.processSignature;
            break;
        }
    }
 
    return err;
}
 
/////////////////////////////////////////////////////////////////////////////////
#pragma mark -
 
/*
 
// cheap 68K-only debugging tool
 
static pascal asm void DisassembleAppleEvent (AEDesc *)
{
    MOVE.L  (A7)+,A0
    MOVE.L  (A7)+,A1
    PEA     @1
    DC.W    0xABFF
    JMP     (A0)
@1: DC.B "\p ; aevt rA1"
}
 
*/
 
OSErr FinderEvent::Send (void)
{
    return fErr = AESend (this,&fReply,fSendMode,kAENormalPriority,-1,nil,nil);
}
 
FinderEvent::FinderEvent (AEEventClass eventClass, AEEventID eventID)
{
    OSType finderLikeProcess;
 
    descriptorType          = typeNull;
    dataHandle              = nil;
    fReply.descriptorType   = typeNull;
    fReply.dataHandle       = nil;
 
    if (!(fErr = GetCreatorOfFinderLikeProcess (&finderLikeProcess)))
    {
        AEAddressDesc address;
        if (!(fErr = AECreateDesc (typeApplSignature, &finderLikeProcess,
            sizeof (finderLikeProcess), &address)))
        {
            OSErr err2 = noErr;
            fErr = AECreateAppleEvent (eventClass,eventID,&address,
                kAutoGenerateReturnID,kAnyTransactionID,this);
            err2 = AEDisposeDesc (&address);
            if (!fErr && !(fErr = err2))
            {
                fSendMode = kAENoReply | kAEAlwaysInteract | kAECanSwitchLayer;
            }
        }
    }
}
 
FinderEvent::~FinderEvent (void)
{
    if (dataHandle)
        fErr = AEDisposeDesc (this);
    if (fReply.dataHandle)
        fErr = AEDisposeDesc (&fReply);
}
 
/////////////////////////////////////////////////////////////////////////////////
#pragma mark -
 
static pascal OSErr MFE_Simple (AEEventID eventID)
{
    OSErr err = noErr;
    FinderEvent finderEvent (kAEFinderEvents,eventID);
    if (!(err = finderEvent.fErr))
        err = finderEvent.Send ( );
    return err;
}
 
static pascal OSErr MFE_HideSpecialWindow (OSType aliasUserType)
{
    OSErr err = noErr;
    FinderEvent finderEvent (kAEFinderEvents,kAEClose);
    if (!(err = finderEvent.fErr))
    {
        MFE_Window finderWindow;
 
        finderWindow.fWindowType                = 0;
        finderWindow.fAliasType                 = 'find';
        finderWindow.fAliasLen                  = 4;
        finderWindow.fAliasRecord.userType      = aliasUserType;
        finderWindow.fAliasRecord.aliasSize     = 0;
 
        if (!(err = AEPutParamPtr (&finderEvent, keyDirectObject,
            typeFinderWindow, &finderWindow, sizeof (finderWindow))))
                err = finderEvent.Send ( );
    }
 
    return err;
}
 
static pascal OSErr MFE_ShowSpecialWindow
    (AEEventClass eventClass, AEEventID eventID)
{
    FinderEvent finderEvent (eventClass,eventID);
 
    if (finderEvent.fErr)
        return finderEvent.fErr;
    else
        return finderEvent.Send ( );
}
 
static pascal OSErr MFE_AddParentAlias
    (FinderEvent &finderEvent, const AliasRecord **aliasH)
{
    //
    //  The goal here is to add a parent alias to 'finderEvent' as the direct
    //  object as specified by the Apple Event Registry. If 'aliasH' is not a
    //  volume, we add an alias of the parent of 'aliasH'. If 'aliasH' is a
    //  volume, we want to add 'aliasH' itself as the container because lots
    //  of events expect that bizarre behavior. We make a new alias to it
    //  because we have no way of telling whether the incoming alias is a full
    //  alias or not, and Finder demands one (and it tends to throw nasty
    //  tantrums if it's not).
    //
 
    OSErr err = noErr;
 
    FSSpecPtr fssP = FSSpecPtr (NewPtr (sizeof (FSSpec)));
    if (!(err = MemError ( )))
    {
        if (!(err = ::HandToHand ((Handle *) &aliasH)))
        {
            Boolean wasChanged;
            if (!(err = ResolveAlias (nil, AliasHandle (aliasH), fssP,
                &wasChanged))) // aliasH is a copy
            {
                if (fssP->parID == fsRtParID ||
                    !(err = FSMakeFSSpec (fssP->vRefNum, fssP->parID, nil, fssP)))
                {
                    AliasHandle parentAlias;
                    if (!(err = NewAlias (nil,fssP,&parentAlias)))
                    {
                        HLock (Handle (parentAlias));
                        if (!(err = MemError ( )))
                        {
                            Size parentAliasSize =
                                GetHandleSize (Handle (parentAlias));
                            if (!(err = MemError ( )))
                                err = AEPutParamPtr (&finderEvent, keyDirectObject,
                                    typeAlias, *parentAlias, parentAliasSize);
                        }
                        DisposeHandle (Handle (parentAlias));
                    }
                }
            }
            ::DisposeHandle (Handle (aliasH));
            if (!err) err = MemError ( );
        }
        DisposePtr (Ptr (fssP));
        if (!err) err = MemError ( );
    }
 
    return err;
}
 
static pascal OSErr MFE_AddChildAlias
    (AEDescList &selection, const AliasRecord **aliasH)
{
    OSErr err = noErr;
 
    SInt8 hState = HGetState (Handle (aliasH));
    if (!(err = MemError ( )))
    {
        HLock (Handle (aliasH));
        if (!(err = MemError ( )))
        {
            Size aliasSize = GetHandleSize (Handle (aliasH));
            if (!(err = MemError ( )))
            {
                err = AEPutPtr (&selection,0,typeAlias,*aliasH,aliasSize);
            }
            HSetState (Handle (aliasH), hState);
        }
    }
 
    return err;
}
 
static pascal OSErr MFE_SendSingleSelectionEvent
    (const AliasRecord **aliasH, AEEventID eventID)
{
    OSErr err = noErr;
 
    FinderEvent finderEvent (kAEFinderEvents,eventID);
    if (!(err = finderEvent.fErr))
    {
        if (!(err = MFE_AddParentAlias (finderEvent,aliasH)))
        {
            AEDescList selection = { typeNull, nil };
            if (!(err = AECreateList (nil,0,false,&selection)))
            {
                if (!(err = MFE_AddChildAlias (selection,aliasH)))
                    if (!(err = AEPutParamDesc (&finderEvent,keySelection,
                        &selection)))
                            err = finderEvent.Send ( );
 
                AEDisposeDesc (&selection);
            }
        }
    }
 
    return err;
}
 
static pascal OSErr MFE_SendSingleFileEventViaHandle
    (Handle h, AEEventID eventID, DescType descType)
{
    OSErr err = noErr;
 
    FinderEvent finderEvent (kAEFinderEvents,eventID);
    if (!(err = finderEvent.fErr))
    {
        SInt8 hState = HGetState (h);
        if (!(err = MemError ( )))
        {
            HLock (h);
            if (!(err = MemError ( )))
            {
                Size size = GetHandleSize (h);
 
                if (!(err = MemError ( )))
                    if (!(err = AEPutParamPtr (&finderEvent, keyDirectObject,
                        descType, *h, size)))
                            err = finderEvent.Send ( );
 
                HSetState (h, hState);
                if (!err) err = MemError ( );
            }
        }
    }
 
    return err;
}
 
static pascal OSErr MFE_CreateFinderWindowHandle
    (const AliasRecord **aliasH, MFE_WindowH *fwhp, long windowType = 0)
{
    OSErr err = noErr;
 
    Size size = ::GetHandleSize (Handle (aliasH));
    if (!(err = MemError ( )))
    {
        *fwhp = MFE_WindowH (NewHandle (size + sizeof (MFE_Window) -
            sizeof (AliasRecord)));
        if (!(err = MemError ( )))
        {
            MFE_Window finderWindow;
 
            finderWindow.fWindowType    = windowType;
            finderWindow.fAliasRecord   = **aliasH;
 
            finderWindow.fAliasType     = typeAlias;
            finderWindow.fAliasLen      = size;
 
            MFE_Window *finderWindowP = **fwhp;
            BlockMoveData (*aliasH,&(finderWindowP->fAliasRecord),size);
            *finderWindowP = finderWindow;
        }
    }
 
    return err;
}
 
static pascal OSErr MFE_SendSimpleFinderWindowEvent
    (const AliasRecord **aliasH, AEEventID eventID, long windowType = 0)
{
    OSErr err = noErr;
 
    MFE_WindowH finderWindowH = nil;
    if (!(err = MFE_CreateFinderWindowHandle (aliasH,&finderWindowH,windowType)))
    {
        err = MFE_SendSingleFileEventViaHandle (Handle (finderWindowH),
            eventID, typeFinderWindow);
        DisposeHandle (Handle (finderWindowH));
        if (!err) err = MemError ( );
    }
 
    return err;
}
 
static pascal OSErr MFE_AddDestinationAlias
    (const AliasRecord **destAlias, FinderEvent &finderEvent)
{
    OSErr err = noErr;
 
    SInt8 destAliasState = HGetState (Handle (destAlias));
    if (!(err = MemError ( )))
    {
        HLock (Handle (destAlias));
        if (!(err = MemError ( )))
        {
            Size destAliasSize = GetHandleSize (Handle (destAlias));
 
            if (!(err = MemError ( )))
                err = AEPutParamPtr (&finderEvent, keyDirectObject, typeAlias,
                    *destAlias, destAliasSize);
 
            HSetState (Handle (destAlias), destAliasState);
            if (!err) err = MemError ( );
        }
    }
 
    return err;
}
 
static pascal OSErr MFE_ResolveAlias (const AliasRecord **aliasH, FSSpecPtr fssP)
{
    Boolean dontCare;
    short aliasCount = 1;
    const unsigned long rulesMask = kARMNoUI | kARMSearch;
 
    return MatchAlias (nil,rulesMask,AliasHandle(aliasH),&aliasCount,
        fssP,&dontCare,nil,nil);
}
 
typedef pascal OSErr (*vParamValidator)
    (const FSSpec &fssToDrag, const FSSpec &fssTarget);
 
static pascal OSErr MFE_DragLow
    (AEEventID eventID, vParamValidator validateParams,
        const AliasRecord **aliasToDrag, const AliasRecord **destAlias)
{
    OSErr err = noErr;
 
    //
    //  If we need to validate our inputs, allocate two FSSpec records
    //  on the heap and resolve the parameter aliases.
    //
 
    if (validateParams)
    {
        FSSpecPtr specToDragP   = nil;
        FSSpecPtr specDestP     = nil;
 
        do
        {
            specToDragP = FSSpecPtr (NewPtr (sizeof (FSSpec)));
            err = MemError ( ); if (err) break;
            specDestP = FSSpecPtr (NewPtr (sizeof (FSSpec)));
            err = MemError ( ); if (err) break;
            err = MFE_ResolveAlias (aliasToDrag,specToDragP);
            if (err) break;
            err = MFE_ResolveAlias (destAlias,specDestP);
            if (err) break;
            err = validateParams (*specToDragP,*specDestP);
        }
        while (false);
 
        if (specToDragP)
            DisposePtr (Ptr (specToDragP));
        if (specDestP)
            DisposePtr (Ptr (specDestP));
    }
 
    //
    //  If there were no errors validating our inputs,
    //  build and send the event.
    //
 
    if (!err)
    {
        AEDescList selection = { typeNull, nil };
 
        do
        {
            FinderEvent finderEvent (kAEFinderEvents,eventID);
            err = finderEvent.fErr;
            if (err) break;
            err = AECreateList (nil,0,false,&selection);
            if (err) break;
            err = MFE_AddChildAlias (selection,aliasToDrag);
            if (err) break;
            err = AEPutParamDesc (&finderEvent,keySelection,&selection);
            if (err) break;
            Point iconPosition = { 0,0 }; // no offset, thank you
            err = AEPutParamPtr (&finderEvent, keyMiscellaneous, typeLongInteger,
                &iconPosition, sizeof (iconPosition));
            if (err) break;
            err = MFE_AddDestinationAlias (destAlias,finderEvent);
            if (err) break;
            err = finderEvent.Send ( );
        }
        while (false);
 
        if (selection.dataHandle)
            AEDisposeDesc (&selection);
    }
 
    return err;
}
 
/////////////////////////////////////////////////////////////////////////////////
#pragma mark -
 
pascal OSErr MFE_PrintWindow (const AliasRecord **aliasH)
{
    return MFE_SendSimpleFinderWindowEvent (aliasH,kAEPrintWindow);
}
 
pascal OSErr MFE_PageSetup (const AliasRecord **aliasH)
{
    return MFE_SendSimpleFinderWindowEvent (aliasH,kAEPageSetup);
}
 
pascal OSErr MFE_CloseWindow (const AliasRecord **aliasH)
{
    return MFE_SendSimpleFinderWindowEvent (aliasH,kAEClose);
}
 
pascal OSErr MFE_CloseGetInfo (const AliasRecord **aliasH)
{
    return MFE_SendSimpleFinderWindowEvent (aliasH,kAEClose,kAEInfo);
}
 
pascal OSErr MFE_CloseSharing (const AliasRecord **aliasH)
{
    return MFE_SendSimpleFinderWindowEvent (aliasH,kAEClose,kAESharing);
}
 
pascal OSErr MFE_MakeAlias (const AliasRecord **aliasH)
{
    return MFE_SendSingleSelectionEvent (aliasH,kAEAliasSelection);
}
 
pascal OSErr MFE_Duplicate (const AliasRecord **aliasH)
{
    return MFE_SendSingleSelectionEvent (aliasH,kAEDuplicateSelection);
}
 
pascal OSErr MFE_OpenSelection (const AliasRecord **aliasH)
{
    return MFE_SendSingleSelectionEvent (aliasH,kAEOpenSelection);
}
 
pascal OSErr MFE_PrintSelection (const AliasRecord **aliasH)
{
    return MFE_SendSingleSelectionEvent (aliasH,kAEPrintSelection);
}
 
pascal OSErr MFE_OpenGetInfo (const AliasRecord **aliasH)
{
    return MFE_SendSingleSelectionEvent (aliasH,kAEGetInfoSelection);
}
 
pascal OSErr MFE_OpenSharing (const AliasRecord **aliasH)
{
    return MFE_SendSingleSelectionEvent (aliasH,kAEGetPrivilegeSelection);
}
 
pascal OSErr MFE_Reveal (const AliasRecord **aliasH)
{
    return MFE_SendSingleSelectionEvent (aliasH,kAERevealSelection);
}
 
pascal OSErr MFE_PutAway (const AliasRecord **aliasH)
{
    return MFE_SendSingleSelectionEvent (aliasH,kAEPutAwaySelection);
}
 
pascal OSErr MFE_UnmountVolume (short vRefNum)
{
    FSSpecPtr fssP = (FSSpecPtr) ::NewPtr (sizeof (*fssP));
    OSErr err = ::MemError ( );
    if (!err)
    {
        if (!(err = FSMakeFSSpec (vRefNum, fsRtDirID, "\p", fssP)))
        {
            AliasHandle aliasH = nil;
            OSErr err = NewAliasMinimal (fssP, &aliasH);
            if (!err)
            {
                err = MFE_PutAway (aliasH);
                DisposeHandle (Handle (aliasH));
                if (!err) err = MemError ( );
            }
        }
 
        DisposePtr (Ptr (fssP));
        if (!err) err = MemError ( );
    }
    return err;
}
 
pascal OSErr MFE_ShowAbout (void)
{
    return MFE_ShowSpecialWindow (kCoreEventClass,kAEAbout);
}
 
pascal OSErr MFE_ShowClipboard (void)
{
    return MFE_ShowSpecialWindow (kAEFinderEvents,kAEShowClipboard);
}
 
pascal OSErr MFE_HideAbout (void)
{
    return MFE_HideSpecialWindow ('abot');
}
 
pascal OSErr MFE_HideClipboard (void)
{
    return MFE_HideSpecialWindow ('clip');
}
 
#pragma mark -
 
pascal OSErr MFE_Sleep (void)
{
    return MFE_Simple (kAESleep);
}
 
pascal OSErr MFE_Restart (void)
{
    return MFE_Simple (kAERestart);
}
 
pascal OSErr MFE_ShutDown (void)
{
    return MFE_Simple (kAEShutDown);
}
 
pascal OSErr MFE_EmptyTrash (void)
{
    return MFE_Simple (kAEEmptyTrash);
}
 
#pragma mark -
 
static pascal OSErr MFE_CopyParamValidator
    (const FSSpec &fssToDrag, const FSSpec &fssTarget)
{
    //
    //  Finder does an infinite recursion when you attempt to
    //  copy a file over itself. Here we test a file to be moved
    //  against a potential destination directory. If they match,
    //  we claim we were passed bogus parameters, which isn't a lie.
    //
 
    OSErr err = noErr;
 
    CInfoPBPtr cipbp = CInfoPBPtr (NewPtrClear (sizeof (*cipbp)));
    if (!(err = MemError ( )))
    {
        cipbp->dirInfo.ioDrDirID    = fssTarget.parID;
        cipbp->dirInfo.ioVRefNum    = fssTarget.vRefNum;
        cipbp->dirInfo.ioNamePtr    = StringPtr (fssTarget.name);
 
        if (!(err = PBGetCatInfoSync (cipbp)))
            if (fssToDrag.parID == cipbp->dirInfo.ioDrDirID)
                err = paramErr;
 
        DisposePtr (Ptr (cipbp));
        if (!err) err = MemError ( );
    }
 
    return err;
}
 
static pascal OSErr MFE_MoveParamValidator
    (const FSSpec &fssToDrag, const FSSpec &fssTarget)
{
    //
    //  Finder will happily attempt (and fail) to move something
    //  to a different volume. The item gets moved to the directory
    //  with the same ID on the source volume -- if it exists.
    //  If it doesn't exist, we never hear about the problem and
    //  nothing happens.
    //
    //  Also: moving a file onto itself is dumb, so we call the copy
    //  validator, which checks this for us.
    //
 
    OSErr err = noErr;
 
    if (fssToDrag.vRefNum != fssTarget.vRefNum)
        err = paramErr;
    else
        err = MFE_CopyParamValidator (fssToDrag,fssTarget);
 
    return err;
}
 
pascal OSErr MFE_Copy
    (const AliasRecord **aliasToDrag, const AliasRecord **destAlias)
{
    return MFE_DragLow (kAEDrag,MFE_CopyParamValidator,aliasToDrag,destAlias);
}
 
pascal OSErr MFE_Move
    (const AliasRecord **aliasToDrag, const AliasRecord **destAlias)
{
    return MFE_DragLow (kAEMove,MFE_MoveParamValidator,aliasToDrag,destAlias);
}