Replying to an AppleEvent

Hi!

I’m trying to make my backup application (Apple-)scriptable.
I created an .sdef file and I am able to catch all the relevant AppleEvents in the App.HandleAppleEvent Event. I can also send a valid reply back to the AppleScript. So far, so good.

But:
I woould like to be able to create an AppleScript which basically says:

set myResult to create backup of "SourceFolder" in "DestinationFolder"

To get the myResult back to the AppleScript, I would have to wait for the actual backup to be finished until I can reply to the AppleEvent with the myResult.
(The app has a GUI and a few threads, which handle the actual copying.)
How can I keep the AppleEvent around until I can properly reply to it? I tried to store the AppleEvent in a global variable, but that didn’t work. (the Ptr was set to 0 immediately after leaving the App.HandleAppleEvent EventHandler. Trying to access that variable afterwards resulted in a crash of the .debug.app.)

App.HandleAppleEvent

#if TargetMacOS if eventClass = "qtla" then dim s1 as FolderItem = theEvent.FolderItemParam( "sour" ) dim s2 as FolderItem = theEvent.FolderItemParam( "dest" ) myAE = theEvent // global Variable RETURN True // changing this to FALSE didn't help end if #endif

Later - when the backup is finished - I would like to return to this: (a method in App)

dim theReply as new AppleEventRecord theReply.StringParam( "rmes" ) = "Backup successful" myAE.ReplyRecord = theReply

Any suggestions?
Thanks,
Mike

Why do you need the AppleEvent itself? Can’t you use EventClass and EventID instead?

What you describe sounds like you receive the event autoreleased, where it is safe to use in the same method you receive it but it fades once you try to forward it. If it is so, it should help to take ownership by casting a retain (and later a release) on it.

That sounds exactly what I need. How do I do that?

The usual structure is

Declare Function retain lib "Foundation" Selector "retain" (id as ptr) as ptr Declare Sub release lib "Foundation" Selector "release" (id as ptr)

Both to be cast on the AppleEvent’s Ptr property. Retain forwards the retained ptr again.
For Desktop where ptr and handles are Integers, you might want to change the datatypes to integer too, or you’d have to cast a ptr on the Integer Ptr property. Let me know if it helps!

It’s worth noting that the usual way to handle long running processes like this is to send a run command and then have the script poll for the status. Otherwise the calling script is effectively “locked up” waiting for a response.

Apple Events are, more or less depending on the context, atomic operations. The default behavior would be for you to do the backup called from the Handle Apple Event event and then return at the end of it. The script will wait for it to complete. Once you return at the end of that handler you’re telling the script that your action has been completed and it will continue. This pretty much makes doing something threaded in the app, or handling more than one event at a time impossible. Normally this isn’t a problem as most actions happen very quickly, but backing up something large might be a problem there.

Once you return true or false from the handler that event is done, it’s already sent the reply back to the script so you can’t use those structures again later unless you “suspend” the event. This will allow you to do what you want. Unfortunately Xojo doesn’t natively support any of this. You can do it with declares but it’s non-trivial. I had it working with declares but only used it rarely as it felt fragile to me. You also cannot suspend an event that you sent to yourself via a script running in your own application.

The best solution for that is in the MBS plugins where they provide a way to suspend an event and then get it back later to finish.

When you suspend an event your app can continue to thread the work and even can receive other apple events and suspend them too and start off multiple threads or queries or whatever. The script that made the call will be stuck at that point waiting for you until the event times out or until you return it. If you never return it the script may hang forever unless they used a “with timeout” block around the call.

For something that potentially takes a long time I’ve usually added an extra switch to the verbs something about “with async” or “without waiting” so that people can do a call synchronous or add the without waiting property in which case I will return immediately and start the action in a thread or however. They will just have to know that the next line of code in the script won’t be able to access whatever you created because it’s still chewing on it. A way to possibly return some kind of object identifier for the thread and being able to check on it’s progress from the script would be nice, but is not usually necessary.

Thank you all for your helpful comments!
Ulrich, I tried your approach, but couldn’t get it to work. (As James pointed out, it seems really not-trivial.)
Greg and James, I was contemplating the idea to poll the app, but initially decided against it. You have encouraged me to go down this road further. (A backup can take several hours in my setting - so I won’t risk having an AppleScript timing out…)
Again: Thanks for your insights!

So, have you been able to solve it? I’ve got a fully working Xojo framework that’s based on “Cocoa Scripting”, in case you want to support more complex Applescripting in your app.