Satimage Previous | Next
Interfacing your code to AppleScript
Home Documentation Smile Computing Working with external codes The XCode projects Interfacing your code to AppleScript  
Tip: to open a file, open one of the sample projects, select File > Open Quickly in XCode, then enter the file's name.

Introduction

The calls sent by applications to one another, as well as the commands used in a script, use the same event communication channel: the AppleEvents. An AppleEvent is really two codes of 4 bytes. In a script, AppleScript displays an English-like form for those codes: the AppleScript dictionaries provide the translation.

A scriptable program is an executable able of handling AppleEvents, thus able of interacting with a script. What the templates provide is a set of functions to enable you to handle the AppleEvents easily, while keeping your own program independent from this handling. When you bring improvements to your code, you will be able to use the improved code on another platform.

Here are links to some documentation by Apple about scriptable applications and inter-application communication.

Handling AppleEvents in the sample projects

In SampleOsax projects, the file «ProjectName».cp handles the AppleEvents. It uses a simple mechanism where the entry point is one function, OsaxEventHandler. In SampleApp projects, AppleEvents are handled in the file main.cp. Most of the handling of the AppleEvents uses functions that the files in Satimage includes define, and that you should not have to change.
The projects provided demonstrate several different examples of how to handle AppleEvents.

A call to your code will spawn an AppleEvent (really 2 codes of 4 bytes), that you catch and handle in the function
pascal OSErr OsaxEventHandler(const AppleEvent* message, AppleEvent* reply, long refCon).
Describe your AppleEvents in the global variable EventDescription gEventDescriptionList[]. This variable contains the list of the events that your application will receive. Each item in the list is 3 elements: the first two are the two codes of four bytes, the last one is a (unique) integer that OsaxEventHandler receives as its refCon parameter.

The functions which may be called at this step have two arguments of the AppleEvent* type: message and reply. message is what the function will receive from AppleScript, reply is what the function will return to the script. The function's job is to make the inputs into the appropriate form, call your code as appropriate, and handle either an error or the result to return.

The AEDesc structure
The message is really a list of objects provided as key-value pairs, where each key (four bytes) is for a given parameter of the function. You first get each object as an AEDesc, the structure for an AppleScript variable. (Note: the AppleEvent and AEDesc types are equivalent.)
You will have to handle an AEDesc, first to import the data sent from AppleScript, then to build the result that the script expects. Satimage includes/AEDescUtils.h defines the functions you need for that, for instance AEDescToDouble, or FloatArrayToDesc.
Reading the inputs
To get the value of a parameter stored as an AEDesc, use AEGetParamDesc (see also examples of usage in the sample source files provided).
OSErr
AEGetParamDesc(
  const AppleEvent * theAppleEvent,
  AEKeyword theAEKeyword,
  DescType desiredType,
  AEDesc * result)
  • theAppleEvent is the AppleEvent that we are parsing, namely message.
  • theAEKeyword is the key associated to the desired parameter. For instance the key for the direct parameter is keyDirectObject. The key may be a code of your own (such as 'Par1') that you would define in the .sdef file.
  • desiredType describes the type that you require for the parameter. If the user provided a parameter not of the desired type (for instance, a string with a number while the command expects a real), AEGetParamDesc will apply the coercions available in an attempt to return the desired type.
      The constants to describe every data type are given in AEDataModel.h, where you find for instance typeChar, typeLongFloat, etc.
      In addition, the Satimage includes/AEDescUtils.h file defines constants to describe two data types for exchanging arrays with Smile: typeListOfLongFloat and typeMatrix.
  • result is the AEDesc where the result is stored
  • If AEGetParamDesc cannot retrieve the parameter it will return an error, for instance errAECoercionFail = -1700, or errAEDescNotFound = -1701. The MacErrors.h file describes all errors.
Making the result to return
Once your code has run, the function must return a result to the script. The result may be any kind of data that an AEDesc can describe: an elementary type provided by Apple, or an array of real or a matrix, or a list or a record (list of key-value pairs) made of other elements.
Make an AEDesc, for instance AEDesc resultDesc, with the data to return, then call AEPutParamDesc(reply, keyAEResult, &resultDesc);
Memory handling

When your code is scriptable, probably you will find that you run it more frequently than before. Thus, if your code leaks - that is, if it does not release all the memory that it allocates when called once - or if you are not very careful (for instance, if your code uses a structure or a pointer that a previous call may in some circumstances have released) you will experience problems such as dreadful sluggishness or repeated crashes. Crashes of your application, and/or of the calling application.

You must release the memory that each AEDesc allocates with the AEDisposeDesc function. To be sure never to release a non-allocated pointer (which is a fatal error), initialize yours AEDesc carefully. Call AEInitializeDesc, or follow the model below when declaring an AEDesc.

AEDesc d={typeNull, 0};//initialization
err=AEGetParamDesc(message, keyDirectObject, typeLongInteger, &d);//an example
AEDisposeDesc(&d); //releasing memory
Generating an error

Instead of returning a result, your program may return an error, that the script may handle. You just have to have OsaxEventHandler return a (non-zero) error number.
It may help much both the programmer and the scripter to have descriptive error messages. To produce helpful error messages, use the two following functions defined in Satimage includes/AEDescUtils.h.

  • OSErr PutReplyParamError(OSErr err,const AppleEvent* message,AppleEvent* reply,DescType key,DescType desiredType);, returns a text which describes the error about a bad parameter
  • void PutReplyStringError(AppleEvent* reply, const char* strErr);, returns an error with a programmer-defined error message.

Suppose the dictionary of your application MyApp contains a verb MyVerb. Suppose MyVerb calls your code and returns an error with an error number and an error message, the calling script may handle the error with a try ... end try wrapper as below.


try
     tell application "MyApp" to set x to MyFunction
on error s number n
     msg("error number " & n & ": " & s)
end try

Version française
Copyright ©2008 Paris, Satimage