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.
LaunchWithDoc2.c
/* |
File: LaunchWithDoc2.c |
Contains: Document-launching sample program |
Loosely based on C.K. Haun's LaunchWithDoc |
This snippet includes these useful routines: |
OpenSpecifiedDocument |
finds the creator application for a document, whether or not |
the app is running, launches the app if necessary, and sends |
the Apple Event necessary to get the app to open the document |
FindApplicationFromDocument |
searches the mounted volumes for the application which |
created a document |
LaunchApplicationWithDocument |
launches an application which is not running and passes it |
the OpenDocuments event as part of the launch parameters |
SendOpenDocumentEventToProcess |
sends an OpenDocuments Apple event to a running program |
BuildOpenDocumentsEvent |
utility function to build an 'odoc' event from a list of |
FSSpecs. |
Remember that a target application need not be Apple event aware |
in order for the OpenDocuments event to succeed (the System will |
pull "puppet strings", simulating the events necessary to make the |
target app open the document) |
However, LaunchWithDoc must be high level event aware (as set in the |
SIZE resource) in order to send Apple events using AESend |
Written by: Greg Robbins |
modified by Nitin Ganatra |
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/27/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
#include <QuickDraw.h> |
#include <StandardFile.h> |
#include <Fonts.h> |
#include <Menus.h> |
#include <Dialogs.h> |
#include <Events.h> |
#include <Files.h> |
#include <TextEdit.h> |
#include <Memory.h> |
#include <Errors.h> |
#include <Processes.h> |
#include <AppleEvents.h> |
#include <Aliases.h> |
// prototypes |
OSErr OpenSpecifiedDocument(const FSSpec * documentFSSpecPtr); |
OSErr FindApplicationFromDocument(const FSSpec * documentFSSpecPtr, |
FSSpecPtr applicationFSSpecPtr); |
void ReportError(StringPtr messageStr); |
// what functions changed since LaunchWithDoc2 |
OSErr BuildOpenDocumentEvent(ProcessSerialNumber *targetPSN, |
const FSSpec *theSpecArray, const short numOfSpecs, AppleEvent *odocAppleEvent); |
OSErr SendOpenDocumentEventToProcess(ProcessSerialNumber *targetPSN, |
const FSSpec *theSpecArray, const short numOfSpecs); |
OSErr LaunchApplicationWithDocument(const FSSpec *applicationFSSpecPtr, |
const FSSpec *theSpecArray, const short numOfSpecs); |
// main program |
// |
// the main routine raises a std file dialog to let the |
// user choose a document and then opens the document |
// in the appropriate application |
void main(void) |
{ |
OSErr retCode; |
StandardFileReply documentStdFileReply; |
SFTypeList mySFTypeList; |
// initialize the toolbox |
InitGraf(&qd.thePort); InitFonts(); InitWindows(); InitMenus(); |
TEInit(); InitDialogs(nil); InitCursor(); |
// Simplest case: get a document and open it |
StandardGetFile(nil, -1, mySFTypeList, &documentStdFileReply); |
if (documentStdFileReply.sfGood) { |
retCode = OpenSpecifiedDocument(&documentStdFileReply.sfFile); |
if (retCode != noErr) ReportError("\p OpenDocument failed"); |
} |
/* |
// Another case: get an application to launch and three documents |
// to open on startup |
StandardGetFile(nil, -1, mySFTypeList, &documentStdFileReply); |
if (documentStdFileReply.sfGood) { |
appSpec = documentStdFileReply.sfFile; |
for (index = 0; index < 3; index ++) { |
StandardGetFile(nil, -1, mySFTypeList, &documentStdFileReply); |
if (documentStdFileReply.sfGood) |
docTmpSpec[index] = documentStdFileReply.sfFile; |
} |
retCode = LaunchApplicationWithDocument(&appSpec, docTmpSpec, index); |
} |
*/ |
} |
void ReportError(StringPtr messageStr) |
{ |
DebugStr(messageStr); |
} |
// OpenSpecifiedDocument searches to see if the application which |
// created the document is already running. If so, it sends |
// an OpenSpecifiedDocuments Apple event to the target application |
// (remember that, because of puppet strings, this works even |
// if the target application is not Apple event-aware.) |
OSErr OpenSpecifiedDocument(const FSSpec * documentFSSpecPtr) |
{ |
OSErr retCode; |
ProcessSerialNumber currPSN; |
ProcessInfoRec currProcessInfo; |
FSSpec applicationSpec; |
FInfo documentFInfo; |
Boolean foundRunningProcessFlag; |
// verify the document file exists and get its creator type |
retCode = FSpGetFInfo(documentFSSpecPtr, &documentFInfo); |
if (retCode != noErr) goto Bail; |
// check the current processes to see if the creator app is already |
// running, and get its process serial number (as currPSN) |
currPSN.lowLongOfPSN = kNoProcess; |
currPSN.highLongOfPSN = 0; |
currProcessInfo.processInfoLength = sizeof(ProcessInfoRec); |
currProcessInfo.processName = nil; |
currProcessInfo.processAppSpec = &applicationSpec; |
foundRunningProcessFlag = false; |
while (GetNextProcess(&currPSN) == noErr) { |
if (GetProcessInformation(&currPSN, &currProcessInfo) == noErr) { |
if (currProcessInfo.processSignature == documentFInfo.fdCreator) { |
foundRunningProcessFlag = true; |
break; |
} |
} |
} |
// if the creator is running, send it an OpenDocuments Apple event |
// since there is no need to launch it |
if (foundRunningProcessFlag) |
retCode = SendOpenDocumentEventToProcess(&currPSN, documentFSSpecPtr, 1); |
// else if the creator is not running, find it on disk and launch |
// it with the OpenDocuments event included as a part of the |
// launch parameters |
else { |
retCode = FindApplicationFromDocument(documentFSSpecPtr, &applicationSpec); |
if (retCode == noErr) |
retCode = LaunchApplicationWithDocument(&applicationSpec, |
documentFSSpecPtr, 1); |
} |
Bail: |
return retCode; |
} |
//---------------------------------------------------------------------------- |
// LaunchApplicationWithDocument |
// |
// given an application and any number of documents, |
// LaunchApplicationWithDocument launches the application and passes the |
// application an OpenDocuments event for the document(s) |
//---------------------------------------------------------------------------- |
OSErr LaunchApplicationWithDocument( |
const FSSpec *applicationFSSpecPtr, |
const FSSpec *theSpecArray, |
const short numOfSpecs) |
{ |
OSErr retCode; |
LaunchParamBlockRec launchParams; |
AppleEvent theAppleEvent; |
AEDesc launchParamDesc; |
ProcessSerialNumber targetPSN; |
// to simplify cleanup, ensure that handles are nil to start |
launchParams.launchAppParameters = nil; |
theAppleEvent.dataHandle = nil; |
launchParamDesc.dataHandle = nil; |
if (theSpecArray != nil) { |
// because 'odoc' events require a address descriptor, I just |
// grab the PSN for the current process. It doesn't matter what |
// it is, because it's never used. |
(void) GetCurrentProcess(&targetPSN); |
// build an 'odoc' event given the array of FSSpecs and the ProcessSerialNumber |
retCode = BuildOpenDocumentEvent(&targetPSN, theSpecArray, numOfSpecs, &theAppleEvent); |
if (retCode == noErr) { |
// coerce the AppleEvent into app parameters, for _LaunchApplication |
retCode = AECoerceDesc(&theAppleEvent, typeAppParameters, &launchParamDesc); |
if (retCode != noErr) goto Bail; |
// fill in the launch parameter block, including the |
// Apple event, and make the launch call |
HLock((Handle) launchParamDesc.dataHandle); |
launchParams.launchAppParameters = |
(AppParametersPtr) *(launchParamDesc.dataHandle); |
} |
} |
launchParams.launchBlockID = extendedBlock; |
launchParams.launchEPBLength = extendedBlockLen; |
launchParams.launchFileFlags = launchNoFileFlags; |
launchParams.launchControlFlags = launchContinue; |
launchParams.launchAppSpec = (FSSpecPtr)applicationFSSpecPtr; |
retCode = LaunchApplication(&launchParams); |
Bail: |
// dispose of everything that was allocated |
if (theAppleEvent.dataHandle != nil) (void) AEDisposeDesc(&theAppleEvent); |
if (launchParamDesc.dataHandle != nil) (void) AEDisposeDesc(&launchParamDesc); |
return retCode; |
} |
//---------------------------------------------------------------------------- |
// SendOpenDocumentEventToProcess |
// |
// given an application's serial number and any number of documents, |
// SendOpenDocumentEventToProcess passes |
// the application an OpenDocuments event for the document |
//---------------------------------------------------------------------------- |
OSErr SendOpenDocumentEventToProcess( |
ProcessSerialNumber *targetPSN, |
const FSSpec *theSpecArray, |
const short numOfSpecs) |
{ |
OSErr retCode; |
AppleEvent theAppleEvent, theReplyEvent; |
theAppleEvent.dataHandle = nil; |
retCode = BuildOpenDocumentEvent(targetPSN, theSpecArray, numOfSpecs, &theAppleEvent); |
if (retCode == noErr) |
retCode = AESend(&theAppleEvent, |
&theReplyEvent, |
kAENoReply, |
kAENormalPriority, |
kAEDefaultTimeout, |
nil, |
nil); |
// dispose of the AppleEvent if it was allocated |
if (theAppleEvent.dataHandle != nil) |
(void) AEDisposeDesc(&theAppleEvent); |
return retCode; |
} |
// FindApplicationFromDocument uses the Desktop Database to |
// locate the creator application for the given document |
// |
// this routine will first check the desktop database of the disk |
// containing the document, then the desktop database of all local |
// disks, then the desktop databases of all server volumes |
// (so up to three passes will be made) |
OSErr FindApplicationFromDocument(const FSSpec * documentFSSpecPtr, |
FSSpecPtr applicationFSSpecPtr) |
{ |
enum { documentPass, localPass, remotePass, donePass } volumePass; |
DTPBRec desktopParams; |
HParamBlockRec hfsParams; |
FInfo documentFInfo; |
short volumeIndex; |
Boolean foundFlag; |
GetVolParmsInfoBuffer volumeInfoBuffer; |
OSErr retCode; |
// dkj 12/94 initialize flag to false (thanks to Peter Baral for pointing out this bug) |
foundFlag = false; |
// verify the document file exists and get its creator type |
retCode = FSpGetFInfo(documentFSSpecPtr, &documentFInfo); |
if (retCode != noErr) goto Bail; |
volumePass = documentPass; |
volumeIndex = 0; |
do { |
// first, find the vRefNum of the volume whose Desktop Database |
// we're checking this time |
// if we're on the initial pass (documentPass) just use |
// the vRefNum of the document itself |
if (volumePass == documentPass) |
desktopParams.ioVRefNum = documentFSSpecPtr->vRefNum; |
// otherwise, find the vRefNum of the next volume appropriate |
// for this pass |
else { |
volumeIndex++; |
// convert the volumeIndex into a vRefNum |
hfsParams.volumeParam.ioNamePtr = nil; |
hfsParams.volumeParam.ioVRefNum = 0; |
hfsParams.volumeParam.ioVolIndex = volumeIndex; |
retCode = PBHGetVInfoSync(&hfsParams); |
// a nsvErr indicates that the current pass is over |
if (retCode == nsvErr) goto SkipThisVolume; |
if (retCode != noErr) goto Bail; |
// since we handled the document volume during the documentPass, |
// skip it if we have hit that volume again |
if (hfsParams.volumeParam.ioVRefNum == documentFSSpecPtr->vRefNum) |
goto SkipThisVolume; |
// call GetVolParms to determine if this volume is a server |
// (a remote volume) |
hfsParams.ioParam.ioBuffer = (Ptr) &volumeInfoBuffer; |
hfsParams.ioParam.ioReqCount = sizeof(GetVolParmsInfoBuffer); |
retCode = PBHGetVolParmsSync(&hfsParams); |
if (retCode != noErr) goto Bail; |
// if the vMServerAdr field of the volume information buffer |
// is zero, this is a local volume; skip this volume |
// if it's local on a remote pass or remote on a local pass |
if ((volumeInfoBuffer.vMServerAdr != 0) != |
(volumePass == remotePass)) goto SkipThisVolume; |
// okay, now we've found the vRefNum for our desktop database call |
desktopParams.ioVRefNum = hfsParams.volumeParam.ioVRefNum; |
} |
// find the path refNum for the desktop database for |
// the volume we're interested in |
desktopParams.ioNamePtr = nil; |
retCode = PBDTGetPath(&desktopParams); |
if (retCode == noErr && desktopParams.ioDTRefNum != 0) { |
// use the GetAPPL call to find the preferred application |
// for opening any document with this one's creator |
desktopParams.ioIndex = 0; |
desktopParams.ioFileCreator = documentFInfo.fdCreator; |
desktopParams.ioNamePtr = applicationFSSpecPtr->name; |
retCode = PBDTGetAPPLSync(&desktopParams); |
if (retCode == noErr) { |
// okay, found it; fill in the application file spec |
// and set the flag indicating we're done |
applicationFSSpecPtr->parID = desktopParams.ioAPPLParID; |
applicationFSSpecPtr->vRefNum = desktopParams.ioVRefNum; |
foundFlag = true; |
} |
} |
SkipThisVolume: |
// if retCode indicates a no such volume error or if this |
// was the first pass, it's time to move on to the next pass |
if (retCode == nsvErr || volumePass == documentPass) { |
volumePass++; |
volumeIndex = 0; |
} |
} while (foundFlag == false && volumePass != donePass); |
Bail: |
return retCode; |
} |
//---------------------------------------------------------------------------- |
// BuildOpenDocumentsEvent |
// |
// General utility to turn a ProcessSerialNumber and a list of FSSpecs into |
// an 'odoc' AppleEvent with the ProcessSerialNumber as the target |
// application. Used by SendOpenDocumentEventToProcess, and |
// LaunchApplicationWithDocument. |
//---------------------------------------------------------------------------- |
OSErr BuildOpenDocumentEvent( |
ProcessSerialNumber *targetPSN, |
const FSSpec *theSpecArray, |
const short numOfSpecs, |
AppleEvent *odocAppleEvent) |
{ |
OSErr retCode; |
AppleEvent theAppleEvent; |
AEDesc targetAddrDesc, docDesc; |
AEDescList docDescList; |
AliasHandle docAlias; |
short counter; |
FSSpecPtr specIterator; |
// to simplify cleanup, ensure that handles are nil to start |
targetAddrDesc.dataHandle = nil; |
theAppleEvent.dataHandle = nil; |
docDescList.dataHandle = nil; |
docDesc.dataHandle = nil; |
docAlias = nil; |
// create an address descriptor based on the serial number of |
// the target process |
retCode = AECreateDesc(typeProcessSerialNumber, (Ptr) targetPSN, |
sizeof(ProcessSerialNumber), &targetAddrDesc); |
if (retCode != noErr) goto Bail; |
// make a descriptor list containing just a descriptor with an |
// alias to the document |
retCode = AECreateList(nil, 0, false, &docDescList); |
if (retCode != noErr) goto Bail; |
// start at the beginning of the FSSpec list, and start adding |
// them to the document list descriptor |
// NOTE: we need to make sure we dispose of the aliases and the |
// AE descriptor in this loop, otherwise there will be memory |
// leaks. |
specIterator = (FSSpecPtr)theSpecArray; |
for (counter = 0; counter < numOfSpecs; counter ++) { |
retCode = NewAlias(nil, &specIterator[counter], &docAlias); |
if (retCode != noErr) goto Bail; |
HLock((Handle) docAlias); |
retCode = AECreateDesc(typeAlias, (Ptr) *docAlias, |
InlineGetHandleSize((Handle) docAlias), &docDesc); |
HUnlock((Handle) docAlias); |
if (retCode != noErr) goto Bail; |
// the alias is now in the AEDescriptor, so dispose of it |
DisposeHandle((Handle)docAlias); docAlias = nil; |
retCode = AEPutDesc(&docDescList, 0, &docDesc); |
if (retCode != noErr) goto Bail; |
// the alias is now in the AE document list, so dispose of it |
retCode = AEDisposeDesc(&docDesc); |
if (retCode != noErr) goto Bail; |
} |
// now make the 'odoc' AppleEvent descriptor and insert the |
// document descriptor list as the direct object |
retCode = AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments, |
&targetAddrDesc, kAutoGenerateReturnID, kAnyTransactionID, |
&theAppleEvent); |
if (retCode != noErr) goto Bail; |
retCode = AEPutParamDesc(&theAppleEvent, keyDirectObject, &docDescList); |
if (retCode != noErr) goto Bail; |
*odocAppleEvent = theAppleEvent; |
Bail: |
// dispose of everything that was allocated, except the return AE |
if (targetAddrDesc.dataHandle != nil) (void) AEDisposeDesc(&targetAddrDesc); |
if (docDescList.dataHandle != nil) (void) AEDisposeDesc(&docDescList); |
if (docDesc.dataHandle != nil) (void) AEDisposeDesc(&docDesc); |
if (docAlias != nil) DisposeHandle((Handle) docAlias); |
return retCode; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-07-22