sources/LaunchApplication.c

// 
// Apple Macintosh Developer Technical Support
//
//  Copyright (work in progress)  Apple Computer, Inc All rights reserved.
//
// You may incorporate this sample code into your applications without
// restriction, though the sample code has been provided "AS IS" and the
// responsibility for its operation is 100% yours.  However, what you are
// not permitted to do is to redistribute the source as "DSC 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.
// 
 
#include <Processes.h>
 
 
#include "LaunchApplication.h"
 
static OSErr FindApplicationProcess (OSType creatorToFind, ProcessSerialNumberPtr processSN);
static OSErr FindApplicationOnDisk (OSType creatorToSearchFor, short startVRefNum, FSSpec* foundApp);
 
 
//------------------------------------------------------------------------------------
 OSErr LaunchApplication (OSType creator)
//------------------------------------------------------------------------------------
{
    ProcessSerialNumber theAppPSN;
    FSSpec              theAppToLaunch;
    OSErr               err  = noErr;
 
    // Check for already running app
    
    err = FindApplicationProcess(creator , &theAppPSN );
    if(err == noErr)
    {
        err = SetFrontProcess( &theAppPSN );
    } 
    else
    {
        err = FindApplicationOnDisk (creator, 0, &theAppToLaunch);
        if(err == noErr)
        {
            LaunchParamBlockRec launchPB;
            
            launchPB.launchBlockID          = extendedBlock;
            launchPB.launchEPBLength        = extendedBlockLen;
            launchPB.launchFileFlags        = 0;
            launchPB.launchControlFlags     = launchContinue | launchNoFileFlags;
            launchPB.launchAppSpec          = &theAppToLaunch;
            launchPB.launchAppParameters    = nil;
            
            LaunchApplication( &launchPB );
            
        }   
    }
    
    return err;
    
 
 
 
}
 
 
 
 
//
// This runs through the process list looking for the requested application.  It searches for
// applications of type 'APPL', 'APPC' (control panel apps), 'APPE' (extension apps), and
// 'APPD' (desk accessory apps).  Once the requested application has been found processSN
// will contain the process serial number of the running application that was just found.
//
static OSErr FindApplicationProcess (OSType creatorToFind, ProcessSerialNumberPtr processSN)
 {
    ProcessInfoRec          procInfo;
    FSSpec                  procSpec;
    Str31                   processName;
    OSErr                   err             = noErr;
 
    // Clear out the PSN so we're starting at the beginning of the list.
    processSN->lowLongOfPSN = kNoProcess;
    processSN->highLongOfPSN = kNoProcess;
 
    // Initialize the process information record.
    procInfo.processInfoLength = sizeof (ProcessInfoRec);
    procInfo.processName = &processName[0];
    procInfo.processAppSpec = &procSpec;
 
    // Loop through all the processes until we find the process we want or
    // error out because of some reason (usually, no more processes).
    do {
        err = GetNextProcess (processSN);
        if (err == noErr) {
            err = GetProcessInformation (processSN, &procInfo);
        }
    } while ((procInfo.processSignature != creatorToFind || !(procInfo.processType == 'APPL' ||
             procInfo.processType == 'APPC' || procInfo.processType == 'appe' ||
             procInfo.processType == 'APPD')) && err == noErr);
 
    return (err);
}
 
//
// Finds the requested application on disk searching the desktop database and returns an FSSpec to that app.
// It first searches local disks, then searches remote disks.  Once a possible application has been found
// in the desktop database the application is verified to actually exist because the desktop database is
// often not totally accurate.
//
// If startVRefNum is negative then it will be treated as a VRefNum and the search will start on specified
// volume.  If the value of startVRefNum is 0 or positive then the search starts on the first local disk,
// runs through all local disks, and then searches the first remote volume and runs through all remote
// volumes looking for the requested application.
//
static OSErr FindApplicationOnDisk (OSType creatorToSearchFor, short startVRefNum, FSSpec* foundApp) {
    HVolumeParam            volPB;
    HIOParam                paramPB;
    DTPBRec                 desktopPB;
    GetVolParmsInfoBuffer   volinfo;
    FInfo                   fndrInfo;
    OSErr                   err             = fnfErr;           // default return value
 
    volPB.ioNamePtr = nil;
    paramPB.ioNamePtr = nil;
    paramPB.ioBuffer = (Ptr)&volinfo;
    paramPB.ioReqCount = sizeof (volinfo);
 
    // Try to find the application on the specified disk, if one was specified, first.
    if (startVRefNum < 0) {
        volPB.ioVolIndex = startVRefNum;
        if (PBHGetVInfoSync ((HParmBlkPtr)&volPB) == noErr) {
            // Call PBDTGetPath to find the desktop file on the volume.
            desktopPB.ioCompletion = nil;
            desktopPB.ioNamePtr = nil;
            desktopPB.ioVRefNum = volPB.ioVRefNum;
            desktopPB.ioIndex = 0;      // Find the most recent application.
 
            if (PBDTGetPath (&desktopPB) == noErr) {
                desktopPB.ioFileCreator = creatorToSearchFor;
                desktopPB.ioNamePtr = (*foundApp).name;
 
                if (PBDTGetAPPLSync (&desktopPB) == noErr) {
                    // At this point we think we have found the correct application.
                    (*foundApp).vRefNum = volPB.ioVRefNum;
                    (*foundApp).parID = desktopPB.ioAPPLParID;
 
                    // Verify that this application actually exists.
                    if (FSpGetFInfo (foundApp, &fndrInfo) == noErr && fndrInfo.fdCreator == creatorToSearchFor) {
                        // This application actually exists, we can use it.
                        err = noErr;
                    }
                }
            }
        }
    }
 
    if (err != noErr) {
        // The application wasn't on the starting disk, so loop over every local volume looking for the application.
        for (volPB.ioVolIndex = 1; PBHGetVInfoSync ((HParmBlkPtr)&volPB) == noErr; volPB.ioVolIndex++) {
            // Call PBHGetVolParms call to ensure the volume is a local volume.
            paramPB.ioVRefNum = volPB.ioVRefNum;
 
            if (PBHGetVolParmsSync ((HParmBlkPtr)&paramPB) == noErr && volinfo.vMServerAdr == 0) {
                // Call PBDTGetPath to find the desktop file on the volume.
                desktopPB.ioCompletion = nil;
                desktopPB.ioNamePtr = nil;
                desktopPB.ioVRefNum = volPB.ioVRefNum;
                desktopPB.ioIndex = 0;      // Find the most recent application.
 
                if (PBDTGetPath (&desktopPB) == noErr) {
                    desktopPB.ioFileCreator = creatorToSearchFor;
                    desktopPB.ioNamePtr = (*foundApp).name;
 
                    if (PBDTGetAPPLSync (&desktopPB) == noErr) {
                        // At this point we think we have found the correct application.
                        (*foundApp).vRefNum = volPB.ioVRefNum;
                        (*foundApp).parID = desktopPB.ioAPPLParID;
 
                        // Verify that this application actually exists.
                        if (FSpGetFInfo (foundApp, &fndrInfo) == noErr && fndrInfo.fdCreator == creatorToSearchFor) {
                            // This application actually exists, we can use it.
                            err = noErr;
                            break;
                        }
                    }
                }
            }
        }
    }
 
    if (err != noErr) {
        // If we didn't find the application locally, check remote volumes.
 
        // Loop over every local volume looking for the application.
        for (volPB.ioVolIndex = 1; PBHGetVInfoSync ((HParmBlkPtr)&volPB) == noErr; volPB.ioVolIndex++) {
            // Call PBHGetVolParms call to ensure the volume is a remote volume.
            paramPB.ioVRefNum = volPB.ioVRefNum;
 
            if (PBHGetVolParmsSync ((HParmBlkPtr)&paramPB) == noErr && volinfo.vMServerAdr != 0) {
                // Call PBDTGetPath to find the desktop file on the volume.
                desktopPB.ioCompletion = nil;
                desktopPB.ioNamePtr = nil;
                desktopPB.ioVRefNum = volPB.ioVRefNum;
                desktopPB.ioIndex = 0;      // Find the most recent application.
 
                if (PBDTGetPath (&desktopPB) == noErr) {
                    desktopPB.ioFileCreator = creatorToSearchFor;
                    desktopPB.ioNamePtr = (*foundApp).name;
 
                    if (PBDTGetAPPLSync (&desktopPB) == noErr) {
                        // At this point we have found the correct application.
                        (*foundApp).vRefNum = volPB.ioVRefNum;
                        (*foundApp).parID = desktopPB.ioAPPLParID;
 
                        // Verify that this application actually exists.
                        if (FSpGetFInfo (foundApp, &fndrInfo) == noErr && fndrInfo.fdCreator == creatorToSearchFor) {
                            // This application actually exists, we can use it.
                            err = noErr;
                            break;
                        }
                    }
                }
            }
        }
    }
 
    return err;
}