UserGuide

Advanced Apple Events

From Xojo Documentation

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.

Newer versions of macOS may require additional permissions or entitlements in order to use Apple Events.

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 your code, so you may need to use Declare statement.

// TITLE: In a module, create the following methods.
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 UInt16

Var err As Integer
Var oDesc As OSType
Var 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 UInt16

Var data As MemoryBlock
Var err As Integer
Var oType As OSType
Var aSize As Integer
Var paramSize As Integer
Var paramType As String

ae.SizeAndTypeOfParam(param, True, paramSize, paramType)
If paramType = "" Then // No parameter with this key
Return Nil
End If

data = New MemoryBlock(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 UInt16

Var 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:

// TITLE: Sending a bad AppleEvent and getting the error number
Var ae As AppleEvent
Var 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
MessageBox("Couldn't send AppleEvent")
Return
End If

Var type As String = "long"
Var 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
MessageBox("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.

This method must be stored in a module

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)

Var myHandle As MemoryBlock
Var err As Integer
Var mb As MemoryBlock
Var 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:

Var s, t As String

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.

Restrictions

In order to use AppleEvents on newer versions of macOS (Mojave and later) you may need to include the NSAppleEventsUsageDescription key in your plist file.

More information here:

See Also

AppleEvent class, UserGuide:AppleScripts topic