Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2001-134
$eventId
ID of event: wwdc2001
$eventContentId
ID of session without event part: 134
$eventShortId
Shortened ID of event: wwdc01
$year
Year of session: 2001
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC01 • Session 134

Building AppleScript Applications

Mac OS • 56:34

Having trouble porting your scriptable application or OSA client app to Mac OS X? Wondering how to debug without MacsBug, AETracker, and all your familiar Mac OS 9 tools? Want to know how to integrate scripts directly into your Carbon or Cocoa application? This session shows you the nuts and bolts of developing scriptable applications and OSA clients, give you tips and tricks on debugging Apple events in gdb, and teaches you some interesting things to do with ProjectBuilder.

Speakers: Chris Espinosa, Mark Alldritt

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Okay, welcome back. The fewer, the heartier, the people without lives or families to spend Memorial Day weekend with. This is part two of our AppleScript Festival. This is Building AppleScript Applications. I'm Chris Espinosa, the manager of the Components and Scripting Group, and we're going to take you through some of the particular issues in implementing your Mac OS X applications.

Some on just porting the application over, some on taking advantage of new features and idiosyncrasies of Mac OS X. It's a real nuts and bolts coding. I'm going to spend a lot of time in ProjectBuilder, going to spend a lot of time in C code. It will be Cocoa-free. So, I... Yeah, that always gets a mixed response. What you'll learn is how to port your scriptable apps to 10, which is basically carbonized. There's really not a lot more than that.

A couple of special issues. What to do about the FS spec problem, which you may not have run into yet, but as soon as you do, it will be tragic. How to deal with new Mac OS X constructs like Sheets. I thank Mark Aldred for bringing up both of these to us.

He was the lone voice crying in the wilderness on the Mac OS X implementers list. What about Sheets? What about FS specs? And we were so busy porting it to Mac OS X and getting the product out that we didn't really get together and say, you know, he's right. This is a problem until getting ready for this presentation. So we've got some fixes. And how to debug scriptable applications and scripts. One of the greatest losses that people feel in working on... I want to go see what they're doing over there. They're having a hell of a time.

Maybe they're giving away T-shirts. One of the greatest losses people feel in moving to Mac OS X as a development environment is the loss of maxbug and all of those great decommands that help you know what's going on in your system. Well, we're going to be showing you how to make up some of that loss in this session. So let's jump right into it. You've got source code. It's in ProjectBuilder. You want to make it scriptable. How do you do it? It's really very simple. If we could have demo one up.

This is our old friend, SimpleText, which, as you saw in the last session, has some scripting capabilities. It's got Apple event handlers in it, and it's got an ability to execute Apple scripts, but it's not scriptable per se. Well, we can fix that. So if I want to take simple text and make it scriptable, it's a simple matter of adding a file. And yes, we do want to copy it in the new groups folder.

So we add a terminology file, and the terminology file is our AETE, as we know it and love it. In this case, though, since this application is a bundled application and it has different variants, what I have to do here is go to the target, look at the resources, Notice that terminology.r doesn't have any variants. So I need to go back to the files. Right. There we go. And pick localization platforms and add a localized variant for English. Okay, so now terminology.r has an English variant that looks like everything else. And then I do a build.

And this is going to do a complete build, so it'll take a little while. Really, all you need to do is add a terminology resource. And AppleScript will recognize the application as scriptable if it has an AET in it. Cocoa applications have a special Info.plist flag called NSAppleScriptEnabled, which is set to true.

You can go ahead and set that in your Info.plist, even if you're not a Cocoa application. That will speed things up for us. We'll recognize you as a scriptable application more readily if that's true. But really, all you need to do is add a terminology resource. So here we go. We've built. Now we should have a simple text executable. And if we drag and drop this on script editor. Script editor. editor shows us the simple text dictionary.

[Transcript missing]

There we go.

There we go. So, scriptable, simple text. That's all you needed to do. Now, when you have a scriptable application, you're suddenly faced with the problem of, okay, now I want to see what's going on. And Project Builder has a very functional debugger where you can look at variables and where you can step through applications step by step, but where if you're used to looking at it in Project Builder, in Code Warrior, where you could format the variables in different ways as characters or whatever, or in MaxBug, where you could look at it in hex or ASCII as well as decimal, you'll find that there are some inherent limitations in Project Builder and its underlying debug engine, GDB, that don't have the things you're familiar with and really rely on in debugging Apple events, mainly because Apple events are deep, complex, variable-length data structures that involve a lot of things. That are 32-bit quantities that are interpreted by us as four characters, but aren't actually string or character types.

And so GDB basically says, hey, this is an unbounded, long, opaque data type that has a lot of hex in it, so I'm going to show you the hex or maybe the octal if you're lucky. This is not very helpful for debugging. You want to look at four-character codes. You want to find out where your Apple event handlers are installed.

You want to set breakpoints in interesting places. You want to look at Apple events and AEDesks. And you want to do these all in your GDB environment without resorting to the command line or special tools. Well, here's some help for you. First of all, write these down and memorize them. They're very, very handy. If you want to look at an OS type, all you have to do is cast it.

If you want to print the value of a variable, like the descriptor type of an A desk, if you just print desk.descriptorType, you'll get it in hexadecimal. If you want to see what the four-character code is, you cast it to a char4, and that will print it the characters. If you want to set it, you can set it in GDB by saying set desk.descriptorType equals, but then you have to type the hex or the decimal.

If you want to set it as four characters, you just cast it to a star, long star of a string. The string will essentially give you a pointer to a temporary, and then you cast that to a long star, and then you assign that, and then that will do the assignment as a four-byte quantity. These are two very helpful things to setting and getting descriptor types or OS types in GDB. Breaking. If you want to set breakpoint, you can set breakpoints in the Apple Event Manager that will be hit before the Apple event is dispatched to your application.

This is especially important with the Carbon event model. With the Carbon event model, you don't usually have a place where you call AEProcessAppleEvent. Carbon just dispatches directly to you. If somebody is sending you an Apple event, and none of your handlers are getting fired, how do you know? There's no bottleneck in your code to set a breakpoint on. Well, you can set a breakpoint in the Apple Event Manager.

Okay. Okay, now I'm at AEM Dispatcher. This is right. And at AEM Dispatcher, Register 3 is the pointer to my Apple event. And if I look at it, in hex it's 61657674, and then a short hex value which looks like a handle. So that looks like an AE desk, and that's great, except it's hex.

So let's use the trick I used before, which is print casting to char 4. of a dereference of $R3 and it says it's AEVT. Yay! So I know it's an AEVT. Let's leave this here for a while, go back to the slides, because now the problem is, okay, I want to see what that Apple event is.

And how do I know what that Apple event is? Well, I could go in and I could dereference the handle doubly, and then examine memory from there and poke through it, and I might see the event class and the event code, and then the parameters might be in there, and if they happen to be in ASCII, I could probably figure it out. But that's not very helpful.

Andy Bachorski wrote for us a great d command in MacsBug called AEVT, which will dump it all out, but we don't have that in gdb. So what we need to do is write a short gdb script. And one of the good things about gdb, and I know MacsBug can do this too, is that gdb can actually call library functions from a gdb script. So what we're going to do is we're going to call AEPrint, the AE helpers routine that takes an Apple event and turns it to ASCII. And we're going to do some things like calling malloc to allocate temporary memory, which we have to deallocate.

And then we're going to put this all in a script. We're going to put it in our home directory, and then we're going to invoke it from the command line. And this is basically what our script looks like. It's got AEPrintDesk to handle, and then a printf in it, and then a couple of dispose statements.

[Transcript missing]

Let's take this and put it in my home directory.

and then in ProjectBuildeR. Slide down the debugger. This is what the gdb AE Desk looks like, just like on the screen. So let's go back to where my breakpoint is. And I'm going to type source tilde slash gdb aedesk. And then just to confirm that it loaded, I'm going to type help user defined.

Okay, and it says list of commands, AEDesk, and AEGetAppleEventHandler. Those are two very useful things that are defined in that. So now I can type AEDesk $R3. and it'll chug for a while, and it tells me that it's an AEVT, it's an OAPP, and it's being sent to this PSN, and there are no parameters.

So I'm using the AE print function in AE Helpers from a gdb script in order, in the, at a debugging breakpoint in the Apple Event Manager to see what my incoming Apple event is. This is going to be very, very helpful for you in debugging your AppleScript applications.

Similarly, if you want to know where an event handler is installed, if you don't know where your code is, there's some code here. It calls actually the actual Apple event routine, AE get event handler. You pass it two strings for the event class and the event code, and it'll tell you the address of the handler, and then you can set a break point on that, and then you will get a break when that handler fires. This is very handy for figuring out when a particular handler fires, especially if the handler is a wildcard handler. Okay? So, with the preliminaries done, let's talk about the difficult stuff.

Special topics for coding for Mac OS X, things you may not have anticipated or known about in making your application scriptable. We're going to talk about a minor housekeeping note is a change in the Apple event handler prototype, which is going to involve you changing some of your code. We're going to talk about FS specs, we're going to talk about sheets, and we're going to talk about loading scripting additions into your application.

The Apple event prototype changed. This is something really unfortunate, but we felt we had to do it, and the Mac OS X transition was a good time. Historically, Apple event handlers have had a refcon, like Windows have refcons and controls have refcons. Apple event handlers have had refcons, and historically that refcon has been a signed long integer. We noticed in putting together the headers for Mac OS X that all of the other refcons in the system were unsigned long integers. We noticed in putting together the headers for Mac OS X that all of the other refcons in the system were unsigned long integers.

all the other Refcons are signed. Historically, the Apple event handlers have been unsigned. We decided to fix this, and fixing this means that you're going to have to change the prototypes on your Apple event handlers and the headers on the handlers, but not the installation code. Otherwise, you would have to do a lot of casts. It's not pleasant, but it's just something you have to do. It's just some housekeeping, but I just wanted to let you know that that's coming up.

More serious is the issue of FS specs. FS specs have been pretty much the hard coin of talking to the file system since people really started using the new file system calls in Mac OS 8. FS specs have been fairly useful. They let you get at things in the file system without having to deal with paths, but there are some problems in Mac OS X.

FS specs, first of all, are limited. Their path name is a char255, and it's intended to be in the system encoding. And that means that an FS spec can't really refer to a long file name or refer to a Unicode file name very well. We're strongly recommending you work with FS refs that can do this, but FS specs have a fundamental limitation.

But there's a problem. FS refs can't refer to non-existent files, and FS specs can, and the reason why a lot of people like FS specs is that you can use them in creating a new file, and that's important in Apple event, Apple scripting application, where a script may want to tell a application to create a new file. You want some compact, useful way of saying what new file to create, and an FS spec can't do it because it can't use long Unicode file names, and then an FS ref can't do it because it can't refer to non-existent files.

What complicates this is that in the Carbon model, each Carbon application has its own view of the VCB queue. That means each Carbon application has its own list of volumes that may be in a different order. So a volume refnum is not valid across processes. That means that an FS spec and an FS ref, which are volume refnums, are not valid across processes.

So a volume refnum is not valid across processes. So you can't really legally send an FS spec or an FS ref from a script to an application because its volume list may be different, and it may be talking about a different volume. We get these bugs every once in a while.

I wrote this script, and I told this application to save the file, but it's saving it on a different disk. Why is that? Well, it's saving it on disk negative two, and in that application, disk negative two is a different disk than in that one. So it's not valid across processes.

If you're lucky, it works. If you're not lucky and you're going deep down, you probably don't have the same directory structure on each disk. The dir IDs are probably not the same, and it just plain doesn't work. So we need a solution to this. If you want to send a save event to your--if your application wants to process a save event, you need a way of getting the file name that's not FS ref or FS spec based.

Well, the answer is, you know, whenever you can, you use type alias for open or for print events or for anything else dealing with a file. Type aliases are valid across processes, and they can represent long Unicode file names, but they too cannot represent non-existent files. So for non-existent files, we are creating a new type called type file URL. And the date of a type file URL is the same format as the FURL drag flavor.

If the finder drags a file to you, using the drag manager and drops it on you. When you get it out, you probably coerce it to an FS spec or an FS ref, and then you get it in your context. But interprocess, it's being sent as this FURL data type. And this FURL data type is process independent. It can refer to non-existent files, and it doesn't rely on volume refnums, so it's legal to send across processes.

We're going to use the same data format with the same four-character code to specify new files in AppleScript. What's nice about it is that it is file system independent. It encodes special characters, so you can have a file name with colons or slashes, and you don't have to parse them out. It can distinguish between files on volumes that have the same name, which is really important, and it's valid across processes and boxes. And since it's valid across processes and boxes, it's valid across processes and boxes. It's a URL. It's even valid across machines, really.

We're going to provide you with some sample code. George Warner and John Montriand are working on this now. We're providing a few routines to basically create and extract file URL data types from things that you see every day, from nav services replies, from AEDesks to get them in and out of Apple events, and also to create a new file given an FURL in one step, rather than having to take it apart and find the file name portion, the directory name portion, and call the fs create unicode call.

So we're just packaging this up as a bunch of sample code. We're also providing coercions between this FURL and all of the interesting types, all of the interesting character types, type chart, type style text, type unicode text, so that users could see these if they wanted and they could create them. And all the type files, the c file object specifier, the type fss, and type fs ref aliases, and the object specifier, of course, so that people can actually compile these in Apple script.

So how you use these is very straightforward. Say that you are an application that is factored and you send yourself a save event. So after somebody chooses save and they fill out the nav reply record, you want to generate a save event with the file name that's been picked in the nav reply record and send it to yourself to actually save the file.

Well, the nav reply record gives you a directory URL and a CFString of the Unicode file name. Those aren't very useful to you when you want to get them into an AE desk. So we've created one call that takes that nav reply and makes it into a CFURL, the underlying core foundation URL data type.

And then another call that puts that CFURL. Into an Apple event as an F URL parameter and all you have to provide is the key. So in basically three lines of code, you can take your nav services reply and stuff the fully transportable file name from that into an outgoing Apple event.

Similarly, on your save file Apple event handler, you're going to get an in parameter in. And it can be anything. It can be an alias, it can be an FS spec, it can be an FS ref, it can be a type file URL. But you basically want to know two things.

One, does it already exist? And if it doesn't already exist, how can I make a file that does exist so I can write into it and get an FS ref from it? And so basically what you do is you get the in parameter as you try getting it as an FS ref. If that fails, then either there wasn't something there or there was something there that couldn't be resolved to a file that exists.

So you guess, okay, if my FS ref succeeded, I'm writing to an existing file. If it failed, I probably want to create a new file. So then you try to get the in parameter as an URL. And if that succeeds, then you know, hey, I'm going to get an in parameter.

And if that fails, then you know, hey, I'm going to get an in parameter. So now I'll show you how that actually works, once again in simple text.

[Transcript missing]

for my .h file, and add that .h file. And that's going to be this. Here's my file, Earldesk.h.

[Transcript missing]

And basically it defines a create from nav reply, create desk from CFURL, put param and put key CFURL. Create one from a desk, create it from an Apple event parameter, create it from a record key. Create it from an FS spec. Create a new file with a neural. and then a couple of coercion handlers. And one of the things we want to do is we want to install those coercion handlers. So let's get the source.

Okay. Now, I want to add a save event handler, and we're going to call a save event handler and see how this does it. So I'll go open up my add-ins. And I've got a Save Apple Event Handler Julia Child format. I'm already canned, and I'll go to where I should add my save event.

and pasted in and i'll go back and Install my save event handler. This is the code to do that. And then install a couple of coercion handlers so that I can process FURL types coming in. I'll copy those. And I will add them right in here. Okay. And then I will build.

While that's building, what I'll do is-- Go to my home library scripting editions folder, and I'm going to copy in A special version of standard editions which has a version of choose file name, which has been updated to return FURL, and we'll see this in the event log. And we'll quit the script editor just for fun, and we'll quit simple text just to make sure. And our build has succeeded. So now we'll run.

And we'll open up this Save As example. And so this is fairly simple. This is what you might see in any script, set F to choose file name, save document one in F. But normally, choose file name would return you a file specification, and you'd send it to save. But now we're sending it a FURL, and the FURL will result in a new file. So we'll turn on the event logs so we can see exactly what's happening. Run it. Choose file name.

Okay. So notice that it said save document one in file URL, file colon slash slash local host slash users slash sbch slash document slash baz.txt. Okay. So instead of an FS spec, it came across as a URL. Notice that simple text saved it as baz.txt. And when we go back to the finder and go look in my home directory in my documents folder, There it is.

So this is an example of how you can very simply, without really changing your application architecture, just use a different data type, in this case the FURL data type, to get around the problem that fs refs and fs specs can't be sent in apple events as i said we're working on the sample code for this it's going to be released as a tech note and you'll have access to this to incorporate into your applications the coercion handlers that i talked about we will incorporate into a future version uh if you want to ship something now for Mac OS 10.0.3 you can install the coercion handlers yourself in the later version of the operating system when they're already installed you can either continue to install them or just stop installing them and you'll inherit the ones from the system So what this means to you is that with this sample code, it's safe and probably important to remove all type FS specs from your scripting interface.

If you're doing OSACs, OSACs and if you're doing factored applications, if you're doing send to self applications, you should probably not send yourself FS specs. You should probably not expect to get FS specs or FS refs from other applications. You should really migrate to this new data type. It's the same one that we use in the drag manager. It's URL based. There's support for it in core foundation. We're providing sample code for you to do it. It's something you should really look at migrating to.

The second issue in Mac OS X is handling sheets. Sheets are window modal dialog boxes. And as window modal dialog boxes, they behave differently then dialog boxes do on Mac OS 9. Mac OS 9 dialog boxes are application modal. They block the whole application. So that when a script tells you to do something and you put up a dialog box, that script halts.

Because you won't send back a reply Apple event until that dialog box is dismissed. In many cases, this is what users-- what scripters want, because it mimics the user experience. The problem is with window modal dialog box, your application doesn't halt. What happens is that you get a Carbon event in, and you show the sheet, and then your Carbon event handler exits, and then your application is just running, and the sheet is going to be called by a callback, a sheet event handler. And then you install a procedure on that to actually do what the sheet does whenever it's dismissed. So you basically have these two threads of execution. One is the main event handler, and one is whatever happens when your sheet event handler gets called.

Well, if you mimic that with an Apple event handler, what's going to happen is that the Apple event will come in, it will pull down the sheet, and it will return without an answer because the user hasn't filled out and okayed the sheet yet. If this is like a save dialog box, then you won't know whether it actually got saved.

If it's a sheet that says, this document has not been saved, are you sure you want to? When the user presses cancel, maybe you want to return a user canceled error from that rather than just say, oh, it's okay. Because basically your script will continue executing, and it will continue executing on the assumption that the previous event has been fully handled when in fact it hasn't. It's still waiting there for user input. Bad things can happen.

So there are four solutions to this. The first one is the easiest way, and it's probably the best way, is to always provide a way from your scripting interface to let the scripter avoid pulling down the sheet. That's going to be the safest. If the scripter can provide all the information that's supposed to be in the sheet in the event, and you can avoid pulling down the sheet altogether and just process the event, do that. It's the best, safest way.

But what if there are cases where you have to pull down the sheet, or if the scripter actually wants you to pull down the sheet and have user intervention, such as the shut down the entire system event? You don't want to prevent the user from having the opportunity of saving open documents in that. Those sheets really have to execute, and you don't want the shut down the system event to say, okay, I'm done, and shut down the system, while all these sheets are pulled down waiting for the scripter to pull down the sheet. It's not the right thing to do.

So the wrong way is to just return even with the sheets are still down. Your script and your application will get out of sync, your script will continue executing, it'll keep firing events with the assumption that, say, that window has already been closed or that document has already been saved. Or it may go look for that document in the file system now, even though it hasn't been saved yet, and it won't find it, and it'll be an error. You don't want to do that.

The cheap way is to see if you're pulling down the sheet based on a user action or from an Apple event. And if it's being pulled down from a user action, make it a sheet. And if it's not, make it application modal. And if you make it application modal, it will behave like Mac OS 9. It will block the application. That's a cheap way to do it. It's effective. It's really easy to do with sheets. It's fairly easy to do with nav dialogs.

There's just actually one bit you set in nav dialog to say make this app modal rather than making it window modal. And it does the rest for you. It'll work. It doesn't provide the best user experience. The nice way and the way that works if you really want your scripts to be able to pull down sheets and wait for user input before resuming is you have to suspend the incoming Apple event and then resume it when the sheet is dismissed. And let me show you how that works.

*Growl* So somebody sends a close Apple event to your application. You create the sheet. You install the sheet event handler. You show the sheet. And then you suspend the incoming event. And when you return from the Apple event handler, you don't return all the way. It doesn't fill out a reply and send it back to the original application. It leaves that application hanging fire until the reply is actually filled in.

This is the behavior you want because if this is coming in from an external script, that external script will just think, gee, it's taking a long time to process this event. And maybe it'll time out. If it's got a long time out, it'll wait. The right thing happens.

Then your sheet event handler, when somebody actually does click a button, you hide the sheet, you dispose the sheet, you get the information about what they actually did, you maybe put that in the reply, and then you resume the Apple event. And at that point, the reply Apple event is actually delivered to the caller, and then the script says, oh, this is what happens, and then execution proceeds. And then in your process, then you can hide the window and dispose the window and do everything you want. And your sheet handler proceeds.

Well, you want a special case that, because you don't want to hang yourself while waiting for a sheet application to occur. So basically, in your window event handler, if somebody clicks the close box or chooses close, you can create a close Apple event and send it. And then your Apple event handler has to notice, hey, this is sent to self, not sent from somebody else. There's nobody waiting on me. I'm just going to execute and return.

I'm not going to suspend the event. And this is the crucial part. If the event's coming from outside, you suspend it. If it's a send to self event, you don't. And then similarly, in your sheet event handler, if there's a suspended event, you resume it. And if there's not, you don't. Makes kind of sense, because if you try to resume and there is no suspended event, well, it doesn't quite work. So going back to sample code.

I have here, I took the Sheets sample code from DTS and hacked it up fairly majorly to make it scriptable. Added in a bunch of events. But you can see that I've added Apple event handlers for open and reopen and open doc. And I've added an Apple event handler for make a new window. And this creates a new window.

And then in the And I've also added an Apple event handler to close the window. Okay? And in the Carbon event handler... Here is the Carbon event handler for close... Basically what I do is I use the very convenient AE helpers to build an Apple event, and I build An Apple event that says close saving ask, and I send it to myself.

And so when I click the close box or when I choose close from the menu, I'm going to generate an Apple event, which is good for recording purposes. And then I'm going to catch it in my close Apple event handler. OK? Well, let's look at what happens. I have a switch in here. And right now, I'm going to handle it just by returning. And I believe that this is already built.

So if I run this application, it comes up and nothing happens. And let me open-- This little script. And what this script does is it makes two windows, closes one, saving ask, and then uses a speech generator, and I hope the sound's working, to say I'm done with the script. If sound isn't working, just watch the stop button.

So I run the script. I'm done with the script. Now, it's done with the script, but the close operation hasn't finished. And in fact, the application's still live. And I could cancel this, or I could close a different window. But the script is already proceeded on the basis of the understanding that the first window was already closed. This is not the behavior that we want.

So let's quit that, and let's go back to our switch here and say handle by suspending. Let's put a breakpoint at suspend the current event, and a breakpoint... Okay, so... I only have one scriptable sheet, great, and it's bouncing and it's running. So we run. And we hit our suspend breakpoint, okay? Now, let's-- and our script hasn't terminated yet. So we go back to Scriptable Sheets, we click OK. We hit the resume breakpoint. Continue. I'm done with the script. And then we're done with the script.

So this will be releasing a sample code too. It's fairly straightforward. There are a couple of complications though, if we could go back to the slide. The one thing I talked about before is you don't resume the send to self events. That, it just doesn't work. Don't do it.

The second interesting thing is if you have a coding style where you, if there's an Apple event error, you propagate that through up to upper levels. It's really interesting, in your Carbon event handler, if your Carbon event handler sends Apple events, and the Apple events receive errors, don't propagate those errors back out of your Carbon event handler, because what'll happen is that Carbon will dispatch that back up the Carbon calling chain. So, you need to be intelligent about the relationship between your Apple event handlers and your Carbon event handlers, and understand that if an Apple event, because the, when you suspend an Apple event, it actually returns a timeout error to the caller.

So if you're sending, well, you need to work out the relationship between your Carbon event and your Carbon event handler. So, you can't send that back up the chain. You need to go through all your events and Apple events and not send things back up the chain. Because Carbon will redispatch it, and then you will get several invocations of the same event, which is not what you want. In this case, you get two sheets on one window, which is not good.

If you're an attachable application, there are a couple of things to make sure of. You want attached scripts to behave like external ones in that you want the scripts to pause, but when you're executing the script, the events are being sent to self. So your simple case of if it's coming from outside, it's a script I suspend. If it's coming from inside, it's a factored command I just executed. That doesn't work.

But you can't suspend it because it's sent to self. So what do you do? Well, in the case of if you're executing a script that is sending your self commands, what you need to do is execute that script on a thread and have a send function for that script execution for that call to OSA execute or execute event that blocks that thread until the event returns. And that's essentially a way to suspend and resume that event execution under your control rather than the Apple event manager control.

And then you unblock the thread in your sheet handler. There are not a lot of attachable applications that are going to be scripting save or things with sheets. If you happen to be in that case and Mark wanted to know about this because he is, this is how we think we should do it.

So what this means to you, if you adopt window modal dialogues, if you adopt sheets or the sheet style nav dialogues, you have to change the way you deal with event handlers. You pretty much have to go to the Carbon event model. It really helps to go to the Carbon event model. And you need to plan the relationship between your Carbon event handlers and your Apple event equivalents.

Let me do one quick thing about scripting additions in Mac OS X from the programmer's point of view. I talked about it from the user's point of view of where you put them and how you invoke them. From the programmer's point of view, you don't get scripting additions in your application unless you make an OSA call, in which case you do.

But what if you wanted to get scripting additions and execute them in your context, and you don't want to have to go through the overhead of starting up AppleScript in order to do it? Chris Espinosa, Mark Alldritt We have some following magic stuff. Read quickly. Basically, you grope our library for a special symbol called OSA install standard handlers. If it's there, you call it, and then you send yourself a special event called the GDUT event.

This is the event that AppleScript sends to load scripting additions. And when you send this to yourself, then it'll load all the scripting additions into your partition. You only need to do this if you really want scripting additions. Chris Espinosa, Mark Alldritt You don't want to have to go through the overhead of starting up AppleScript in order to execute inside your application space, inside your application process. And you're not going to execute an AppleScript yourself. It's a fairly rare case.

We generally don't recommend groping our frameworks to find private symbols, but this is a fairly safe one. You're only calling it if it's there. And in the future, if we do this differently, we'll remove that symbol, and then your test will fail. You won't do anything. Everything will just work.

In the future, they may be loaded into every application by default. We're still looking at that. There are some security implications to that. We definitely don't want people sending, for example, terminal or login window events that get executed by secret scripting additions that they put in the network scripting additions folder, and all of a sudden they get root privileges and can write all over your machine.

We need to look at that model before we execute scripting additions in every application. There are alternatives. And one of the alternatives is... This is a great third-party product called Script Debugger, and Mark Aldrich is going to come up and show you how you can use Script Debugger to debug scripts executing inside your application. Mark? So I'll start by showing you a few features of Script Debugger as just an AppleScript debugger so you can see how your script might execute. What I have here is a little FileMaker database with a picture and some text and a script that works with it.

So what this script does is essentially read some data out of this FileMaker database and do some stuff with it. Script Debugger gives you all the standard debugging things you need, breakpoints,

[Transcript missing]

Script Debugger shows you the scope of all of your variables as you go. Anything that has a white background is global. Anything that has a yellow background is local to this function or handler in AppleScript terms. And anything with a blue background is built into AppleScript itself.

One of the things you can do is if you don't like the way the browser operates or you want to see something in a larger view, you can open a separate window and you can view it in a print form as the source code from AppleScript or in Best. And one of the cool things about Best is that it allows you to deal with things other than textual data. So for example, as this script reads pictures and text, the debugger shows you the actual live data.

Now if you're trying to debug a script running within your own application, Script Debugger gives you the ability to actually invoke that debugger from your context, not just the Script Debugger application. And the user interface for this is simply switching OSA languages. So you switch from AppleScript to AppleScript Debugger and save your script. In this instance, it's an applet.

And then when I drop a few files on there, The applet launches as it normally would, and then ScriptyBugger comes forward and you can actually see exactly what your applet received from the finder, which in this case is a pair of aliases. And bear with me, it's still beta. You can step along.

The key element here is that if you support what's known as the generic OSA component in your application, if you have a script menu or any other setting in which you would execute a script, the generic component will automatically load the appropriate script. language. And by doing so, you let the author of the script-- You let the author of the script make the choice.

And in this instance, they've chosen the debugger, but they might choose the OS-- The Unix shell OSA component that Chris was demonstrating earlier on. And so with that simple switch of a language, the debugger can be executed inside your application, and you can see exactly what you're doing. And, uh... as sort of the final example of how cool that might be.

So there, you can have the debugging running inside the show. Thank you very much. Great. Thanks, Mark. So, Mark's done a lot of work over a period of years trying to work around, if we could have the slides back, the fact that AppleScript doesn't have any system level debug APIs.

And we're trying to rectify that and work with him and the developers of other debugging applications, if we could have the slides back, to actually put in some debugging APIs into AppleScript so that they could take advantage of them and you could take advantage of them in your applications or invoke them from GDB, for example, to find out what's actually going on inside your application when you're executing scripts. We have a framework for what the APIs look like.

The basic concept is that there's a debug session, which is based on OSA IDs, and there are stack frames, and you can walk the stack chain and look at globals, locals, and properties of a handler or a script in the stack frame. That looks like it'll have a good match with the user interface of the current debuggers and also all the things that you may want to do from inside a script.

Also providing an interface to break and single step and step into, step over, continue, all the things you'd expect from a debugger. The important thing is that it needs a remote mode as well, and that remote mode will, of course, be via Apple Events, so that you could send, from your debugger, you could send an Apple Event to any application executing a script to halt that script application and then have the entire debugging transaction over Apple Events. This would be very exciting when we've got Apple Events over the Internet again because you could do that. You could do remote debugging over the Internet between two machines on Apple Script. We're really looking forward to that and we're working closely with the developers of debugging applications.

So to summarize, everything you need to make a good scriptable application, both an application that implements scriptability and an application that executes Apple scripts is in Carbon Framework now. And there should be nothing holding you back from developing great scriptable applications. If there is, let us know. It's easy to invoke scripts from your code.

I mean, simple text does it in the demo. It's three or four lines of code. And if you do, make sure that you're OSA component agnostic so you can take advantage of things like the OSA script, the shell scripting component, or Mark Aldrich's debugging component, so that if you execute scripts, you don't just execute Apple scripts, but you execute any scripts, because we'll be widening the range of scripts in the future.

Make sure you migrate away from FS specs, because FS specs are not the right way to send things between applications. Please adopt the sheet user interface in Aqua and take the ways we've shown you to make sheets work in scripts. And use the development tools. Project Builder is a very effective development tool for developing, testing, debugging, and deploying your application.

There's only one more thing left to do, and that's the feedback forum, which is going to be across the hall, because we have 16, 15, 14 seconds left. We're not going to take any Q&A here, but we'll take any in the feedback forum. Once again, Jason is the technology manager. Contact him. I'm the engineering manager. Use the lists. George Warner is our fine tech support.