AppleEvent
From Real Software Documentation
| This class is only available on the OS X platform. For cross-platform development, use #If...#Endif with the Target... specifiers to make sure you will not attempt to use this class on an incompatible platform. |
AppleEvent objects can be used to communicate with other Macintosh applications and the Macintosh System software. If you are compiling your application for use on other operating systems, be sure to check the global Boolean constants TargetWin32, TargetMacOS, and TargetLinux. These constants return True if the application is running on the respective operating system.
| Methods | ||||
|
| Constructors | |
|
AppleEvents Basics
An AppleEvent is a self-contained block of data which consists in a sequence of key-type-value data (called an AppleEvent Descriptor, or AEDesc per Apple's terminology). Each descriptor can contain other descriptors as an ordered array or as a mixture of keyed data. The AppleEvent as a whole is itself and AppleEvent Descriptor. This flexibility makes the power of AppleEvents but it also has a price: their complexity of use.
Anatomy of an AppleEvent
An AppleEvent is composed of:
- A command, composed of an event class and an event ID (both a four-character code).
- Some so called attributes, notably the target, i.e. the application to which the AppleEvent should be sent. The target can be any already running application, either on the local or a distant computer.
- Some parameters, each being identified by a four-character code. Internally, each parameter also stores its type, its size and its data. In Real Studio, you should use the appropriate xxxParam() properties to get or set parameters of a given type. The special parameter '----' is called the direct parameter.
- An automatically generated reply AppleEvent which holds the result of the AppleEvent.
AppleScript
AppleScript is a way of using AppleEvents very easily through a sort of natural speaking language. The text script is first compiled as a sequence of AppleEvents, then it can be executed any number of times with different parameters. AppleScript was part of the Apple's Open Scripting Architecture (OSA). OSA allows everyone to develop a full programming language based on AppleEvents but, as of now, only AppleScript survived as an OSA language.
See Introduction to AppleScript Language on Apple's website.
The Mandatory AppleEvents
Per Apple's programming rules, every application interacting with the user should support the following AppleEvents:
- aevt/oapp: sent to the application when it is opened ('oapp' stands for Open APPlication).
- aevt/odoc: sent to the application when one or more documents are to be opened ('odoc' stands for Open DOCument)
- aevt/quit: sent to quit the application.
- aevt/pdoc: sent to the application to print a document ('pdoc' stands for Print DOCument). Mandatory only if printing is sensible in the context of your application.
- aevt/rapp: sent to application when user reopens the application, either by double-clicking again on the icon, or clicking on the icon in the dock ('rapp' stands for Reopen APPlication).
Object Descriptors
AppleEvents are sort of object oriented as they give instructions on what to do on a given object (a button, a window...). However, they are intimately linked to AppleScript which is supposed to be a very simple and natural language, hence the use of common words (before, after, first, last, any...) which are sometimes difficult to implement in another programming language. This is why there are 9 different Get...ObjectDescriptor methods.
In Real Studio, you would use the "dot notation" to access an object and any of its property, e.g.
In AppleScript, you would use "of" instead, like in:
set s to Text of Control1 of ContainerControl1 of Window1 ("of Application" is usually omitted)
Describing an Object
According to the context, there may be different ways of describing an object: the frontmost window, the next row, the first word... However, the application you sent an AppleEvent to should reply by giving you a better description of the object, such as its unique ID. In such a case, you should use the object descriptor that the application sent to you.
Also, the flexibility of AppleEvents allows a function to return any kind of data. Most notably, you can get an array (i.e. a list in AppleEvent terminology) of values instead of a single one. As an example, the following AppleScript command
tell application "Finder" to get name of windows --this is interpreted as "give me the name of every opened window"
may legally return nothing or an empty list if there is no window opened, a string or a list with only one string item if there is 1 window opened, or a list of strings when there are more than one window opened.
Getting an Object Descriptor
As said before, there are 9 different Get...ObjectDescriptor functions corresponding to the different ways to obtain a reference to an object. Choose the one which suits best your needs:
- GetIndexedObjectDescriptor: you want one object in an array. The key value is the index of the object in the array.
- GetNamedObjectDescriptor: you want to describe an object by its name.
- GetOrdinalObjectDescriptor: you want to get the first, last, middle, any (random) or all object(s).
- GetRangeObjectDescriptor: you want to get a range of objects in an array.
- GetStringComparisonObjectDescriptor: you want to get an object by comparison with another object descriptor.
- GetTestObjectDescriptor: you want to select objects using a test (like an If...Then...Else statement).
- GetUniqueIDObjectDescriptor: you want to get the object given a unique ID that was passed to you earlier by the application (you cannot guess the unique ID).
- GetPropertyObjectDescriptor: you want to access a property of an object.
NOTE: whenever you want to get an object at the application level, e.g. a window, you should pass Nil as the Object parameter.
Notes
AppleEvent objects are used to send and receive information between your application and other Macintosh applications or the Mac OS. To send an AppleEvent from your application to another application, create an AppleEvent with the AppleEvent constructor, fill in the AppleEvent's properties with any necessary data, then call the AppleEvent's Send function to send it.
AppleEvents can also be received by your application. When an AppleEvent is received, the Application object's HandleAppleEvent event is executed and the AppleEvent is passed to the event as a parameter. All intrinsic AppleEvents are first passed to the Application object's HandleAppleEvent event handler. If you return True from this event, the default behavior of the AppleEvent will not be executed. For more information on receiving AppleEvents, see the Application class.
Replying To An AppleEvent
When an AppleEvent is received (via an AppleEvent handler) the ReplyBoolean, ReplyInteger, and ReplyString properties can be used to automatically send a reply back to the application that sent the AppleEvent. When sending an AppleEvent (via the Send method) the Reply* properties, such as ReplyInteger, can be used to get any reply the target application has sent back once it receives the AppleEvent.
For example, the line:
indicates that the application successfully handled the AppleEvent message.
To determine whether the other application successfully handled a message, use code such as:
Dim s as String
If ae.Send then //AE successfully sent
If ae.replyBoolean then
s = "Yes (handled)"
else
s = "Yes (unhandled)"
end if
else
s = "No"
end if
Examples
In this example, the TextEdit application (which must be running for this particular example to work) is instructed to open two documents ("My Document" and "My Other Document") that are located in the folder with the Real Studio project:
Dim list as AppleEventDescList
a = New AppleEvent("aevt", "odoc", "com.apple.textedit")
list = New AppleEventDescList
list.AppendFolderItem GetFolderItem("My Document")
list.AppendFolderItem GetFolderItem("My Other Document")
a.DescListParam("----") = list
If Not a.Send Then
MsgBox "The AppleEvent could not be sent."
End If
Advanced AppleEvents
Carbon and Cocoa
AppleEvents are implemented in the Carbon framework. However, Cocoa does not support (yet ?) sending AppleEvents. As a consequence, you must use Carbon declares even in a Cocoa application.
Managing Errors
The boolean value returned by the AppleEvent's Send command only informs you that the event has been received by the target application. However, your command may have failed, e.g. if you refer to a non-existent object.
Errors in AppleEvents are stored as a parameter in the reply AppleEvent. The error number is stored as 'errn' and the optional error message as 'errm' or 'errs'. However, only the direct parameter '----' can be retrieved from a reply in Real Studio, so you may need to use Declare statement.
Sub SizeAndTypeOfParam(extends ae as AppleEvent, param as string, inReply as boolean, byref size as integer, byref type as string)
//Get the size and type of one parameter. Set inReply to true if you want to access the reply AppleEvent
declare function AESizeOfParam lib "Carbon" (evnt as integer, AEKeyword as OSType, byref oDesc as OSType, byref oSize as integer) as short
dim err as integer
dim oDesc as OSType
dim oSize as integer
if inReply then
err = AESizeOfParam( ae.replyptr, param, oDesc, oSize )
else
err = AESizeOfParam( ae.ptr, param, oDesc, oSize )
end if
if err<>0 then //We get a -1701 error if there is no parameter with this keyword
type = ""
size = 0
else
type = oDesc
size = oSize
end if
End Sub
Function ReplyRawData(extends ae as AppleEvent, param as string, byref type as string) As MemoryBlock
//Get a binary data param in the reply AppleEvent
declare function AEGetParamPtr Lib "Carbon" (AEPtr as integer, AEKeyword as OSType, inType as OSType, byref outType as OSType, data as Ptr, maxSize as integer, byref actSize as integer) as short
dim data as MemoryBlock
dim err as integer
dim oType as OSType
dim aSize as integer
dim paramSize as integer
dim paramType as string
ae.SizeAndTypeOfParam( param, true, paramSize, paramType )
if paramType="" then //No parameter with this key
return nil
end if
data = newMemoryBlock( paramSize )
//Get the data
err = AEGetParamPtr( ae.ReplyPtr, param, type, oType, data, data.Size, aSize )
if err<>0 then
return nil
else
//Update the actual type and return the data
type = oType
return data.StringValue( 0, aSize )
end if
End Function
Sub ReplyRawData(extends ae as AppleEvent, param as string, type as string, assigns data as MemoryBlock)
//Add some binary data as a reply AppleEvent parameter
declare function AEPutParamPtr Lib "Carbon" (AEPtr as integer, AEKey as OSType, dType as OSType, data as Ptr, dsize as integer) as short
dim err as integer
err = AEPutParamPtr( ae.Replyptr, param, type, data, data.size )
End Sub
You can now get or set any parameter in the reply AppleEvent and use it to get or set an error. As an example, the following code should return an error:
dim ae as AppleEvent
dim o as AppleEventObjectSpecifier
//We will try to activate the 100th window of the Finder. It is very likely to raise an error.
ae = new AppleEvent( "misc", "actv", "com.apple.finder" )
o = GetIndexedObjectDescriptor( "cwin", nil, 100 )
ae.ObjectSpecifierParam( "----" ) = o
if not ae.Send then
MsgBox "Couldn't send AppleEvent"
return
end if
dim type as string = "long"
dim data as MemoryBlock
//Get the 'errn' parameter of the reply. It should be -1728 in such case (Object not found).
data = ae.ReplyRawData( "errn", type )
if data<>nil then //There is an error number parameter
MsgBox "Finder returned error " + Str( data.Int32Value( 0 ))
end if
Getting a Textual Representation
It is often useful to get the textual representation of an AppleEvent, because such string contains all the attributes, parameters, types and data. The following method takes an AppleEvent and a boolean which indicates if you want the description of the AppleEvent itself or its reply. It returns a string.
Function PrintDesc(extends ae as AppleEvent, getReply as boolean = false) As string
Soft declare function AEPrintDescToHandle lib "Carbon" (theEvent as integer, hdl as Ptr) as integer
Soft declare sub DisposeHandle lib "Carbon" (hdl as ptr)
dim myHandle as MemoryBlock
dim err as integer
dim mb as MemoryBlock
dim result as string
//Will hold the pointer to the data
myHandle = New MemoryBlock( 4 )
if getReply then
err = AEPrintDescToHandle( ae.ReplyPtr, myHandle )
else
err = AEPrintDescToHandle( ae.Ptr, myHandle )
end if
if err<>0 then return "" //Check for error
//Get the data
mb = myHandle.Ptr( 0 )
mb = mb.Ptr( 0 )
result = mb.CString( 0 )
DisposeHandle myHandle.Ptr(0) //We must free the handle to get memory back
return result
End Function
Considering the example above "Sending a bad AppleEvent and getting the error number", you can use the following code to get the AppleEvent's descriptions:
s = ae.PrintDesc
//returns 'misc'\'actv'{ '----':'obj '{ 'want':'cwin', 'from':'null'(), 'form':'indx', 'seld':100 } }
t = ae.PrintDesc( true ) //Pass "true" to get the reply's description
//returns 'aevt'\'ansr'{ 'erob':'obj '{ 'want':'cwin', 'from':'null'(), 'form':'indx', 'seld':100 }, 'errn':-1728 }
For s, 'misc'\'actv' is the command. It is immediately followed by the parameters between curly brackets. The only parameter is '----' of type 'obj ' (note the extra space), i.e. an object specifier. It is composed of the data you used to create it:
- 'want' is the class you asked for; here 'cwin'
- 'from' is the parent object. As we passed nil, it is represented as 'null'() in the textual representation
- 'form' is the form of the request. As we asked the window by its index, the form is of type 'indx'.
- 'seld' is the selector descriptor, i.e. the value(s) to be used according to the form of the request. Here, it is equal to the integer value 100 since we asked for the window whose index is 100.
For t, 'aevt'\'ansr' is the signature for any AppleEvent reply ('ansr' stands for answer). There are 2 parameters:
- 'erob', of type 'obj ', contains the object descriptor which caused the error ('erob' stand for ERror OBject).
- 'errn': an integer parameter of value -1728.
See Also
- AppleEventDescList, AppleEventObjectSpecifier classes; Application object; If…#Else…#Endif statement; TargetLinux, TargetMacOS, TargetWin32 constants.
- Apple Events Programming Guide, a legacy guide on Apple's website.
- Introduction to AppleScript Language on Apple's website.
- For a list of system-defined errors, see file /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Headers/MacErrors.h
