In
the document's data fork. Maintaining the scripts within the data for a
document is a more secure method, since it makes it harder for users to extract
the scripts. It's also more difficult for you, though, because you may have to
keep track of the location within the document's data, and then convert the
script into the form required for execution. You'll want to store three pieces
of information: the four-character ID 'scpt' (typeOSAGenericStorage), the
length of the script data that follows, and the script data itself. The ID
isn't essential, but it may come in handy, especially if there are other types
of data present or if you load your document's data sequentially.
There are many ways to keep track of multiple types of data in a document file.
If you have a lot of different types of data in the file, you can even develop
a small database for the data, complete with a directory, so that you can gain
quick access to particular types of data, including the script. A simpler way
is to maintain the data in one long stream, embedding the script data within
the stream. If you know the location of the script within the stream, you can
just load and execute it when a user wants to run it. One developer I know
reads all the data in the data fork (including scripts) sequentially when the
file is opened, so that he doesn't need to keep track of the script's location
within the file. Listing 2 shows an example of loading script data from the
data fork of a document file.
Listing 2. Extracting script data from a document's data fork
FUNCTION RunEmbeddedScriptFromDataFork(theAlias: AliasHandle;
scriptLoc: LongInt): OSAError;
VAR
fileSpec: FSSpec;
scriptData: Handle;
scriptDesc: AEDesc;
dataType: DescType;
scriptID, resultID: OSAID;
myErr, ignoredErr: OSAError;
refNum: Integer;
scriptLen, readLen: LongInt;
specChanged: Boolean;
BEGIN
(* Open the file. *)
myErr := ResolveAlias(NIL, theAlias, fileSpec, specChanged);
IF myErr <> noErr THEN MyErrorProc(myErr);
myErr := FSpOpenDF(fileSpec, fsRdPerm, refNum);
IF myErr <> noErr THEN MyErrorProc(myErr);
(* Grab the data. *)
IF MemError <> noErr THEN MyErrorProc(MemError);
myErr := SetFPos(refNum, fsFromStart, scriptLoc);
readLen := sizeof(dataType);
IF myErr = noErr THEN myErr := FSRead(refNum, readLen, @dataType);
(* dataType should be typeOSAGenericStorage. *)
readLen := sizeof(scriptLen);
IF myErr = noErr
THEN myErr := FSRead(refNum, readLen, @scriptLen);
IF myErr = noErr THEN scriptData := NewHandle(scriptLen);
IF MemError <> noErr THEN MyErrorProc(MemError);
myErr := FSRead(refNum, scriptLen, scriptData^);
IF myErr <> noErr THEN MyErrorProc(myErr);
myErr := FSClose(refNum);
(* Prepare and run the script. *)
myErr := AECreateDesc(typeOSAGenericStorage, scriptData^,
GetHandleSize(scriptData), scriptDesc);
DisposeHandle(scriptData);
IF myErr <> noErr THEN MyErrorProc(myErr);
myErr := OSALoad(gGenericComponent, scriptDesc, kOSAModeNull,
scriptID);
IF myErr <> noErr THEN MyErrorProc(myErr);
myErr := OSAExecute(gGenericComponent, scriptID, kOSANullScript,
kOSAModeNull, resultID);
ignoredErr := OSADispose(gGenericComponent, scriptID);
ignoredErr := AEDisposeDesc(scriptDesc);
IF myErr <> noErr THEN MyErrorProc(myErr);
(* You might want to do something with the result. *)
IF resultID <> kOSANullScript
THEN MyDealWithResult(resultID);
RunEmbeddedScriptFromDataFork := myErr;
END;
The information in this column is not offered as a complete solution, but is
intended to get you moving with implementing attachability. There are many
other issues surrounding attachability that are worth exploring, such as
getting time during script execution, using attached scripts to allow users to
tinker with some of the core functionality of your application, and providing a
consistent way for your users to edit attached and embedded scripts. I plan to
delve into these other issues in upcoming columns.
Making your application capable of attaching or embedding scripts puts new
power into your users' hands, giving them unprecedented ability to develop
custom solutions to their problems. It's not hard to do, and the benefits are
enormous. Do it today.
CAL SIMONE (mainevent@his.com, AppleLink MAIN.EVENT) Few people know it, but
before Cal was in the software business, he used to produce records (the
musical kind) in Washington DC and New York. At a time when computers were used
mostly to make robotic dance music, Cal was one of the first to painstakingly
create "human" performances in pop records with about 60 MIDI synthesizers and,
of course, a Macintosh. He now works toward a day when every application will
be scriptable.*