共用方式為


AppleScript and VBA: the Story of Sid and Nancy

In the few spare moments I’ve had over the past several weeks, I’ve followed
some discussions of Word and AppleScript both in the comments to my earlier
post and in the Microsoft newsgroups. A particularly good discussion, with
input from both Matt Neuburg and Paul Berkowitz, can be found here.

Both Matt and Paul have pointed out that there is a very strong
correspondence between AppleScript and Word’s VBA (Visual Basic for
Applications) implementation. The same is true for both PowerPoint and Excel,
though not for Entourage. There is no VBA implementation in Entourage.

I want to talk about why AppleScript so closely resembles VBA in Word, Excel
and PowerPoint, but I first want to take a step back and talk about objects and
object models.  These are terms you’ll
find quite often in any discussion of scripting regardless of the application
or the scripting environment.  It
has a lot to do with how programmers think about the data they manipulate and
how they manipulate it.

An “object” is any entity that can respond to some form of input or command,
and often has some form of data associated with it.  Word, as an application, is an object.  The documents that you have open in
Word are objects.  The windows that
display those documents are objects. 
The sections within those documents are objects.  The paragraphs within the sections are
objects.  In formal terms, an
object is both the data it contains and the operations that can be performed on
the data it contains.  We use the
word “properties” to talk about the data, and the word “methods” (or “commands”
in AppleScript parlance) to talk about the operations that can be performed on
that data.

An “object model” is a conceptual definition, or description, of how the
objects in an application are related to each other.  Quite often this takes shape in the form of a “containment
hierarchy”.  The “containment
hierarchy” is really just a fancy way of saying that some objects are
properties of other objects.  For
example, in Word, a paragraph is a property of the document object.  This relationship can include the notion
of collections (or “plural forms” in AppleScript parlance).  Really, a collection is nothing more
than a group of similar objects within the same container.

Any given object model is very application centric.  Word’s object model, for example, has
some significant differences from, say, BBEdit’s object model, and that’s not
just because Word does some things that BBEdit doesn’t.  The way one describes the “selection”
object is highly dependant upon how the concept of a “selection” is implemented
in that application.  The “selection”
object for a drawing program, for example, is going to be very different from
the “selection” object in a text editor.

Commands and how they relate to objects are also extremely application
centric.  For example, when you
tell an application to “select foo of bar,” what does that really mean?  Are you telling “foo of bar” to select
itself, are you telling the selection object to incorporate “foo of bar,” or
are you telling the application to change its selection to foo of bar?  The answer to that question will depend
largely on the application you’re working with (though, in Word, it generally
means that you’re asking “foo of bar” to become the selection).

The important point in all of this is that, at the conceptual level, the
object model one defines for any given application depends on the application,
and not the scripting environment or scripting language you use to expose that
object model.  Had Word never had a
VBA implementation of its object model, Word’s AppleScript implementation of
that object model would not have differed substantially from its current
implementation.  Any differences
would be more attributable to the way particular individuals approached the
design of the object model than they would be attributable to differences
between VBA and AppleScript.

This is the primary reason why there is such a strong correspondence between
the VBA implementation and the AppleScript implementation of the object models
in Word, Excel and PowerPoint.  One
could, if one wanted to, implement VBA as a pass-through to an AppleScript
implementation of these object models, though there is a fundamental difference
between VBA and AppleScript that would tend to favor doing things the other way
around.

To understand this fundamental difference, we have to understand something
known as “application intercommunication,” or “interprocess communication” (the
word “process” is really just a synonym for “application program”).  Within modern operating systems,
applications have fences built around them.  The idea is to keep bugs in one application from affecting
the behavior of other applications running on the system.  Within this design, if applications are
going to communicate with each other, we need to give them a way to pass
information back and forth over the fences that have been built around them.

With AppleScript, interprocess communication is done via AppleEvents.  Every command that you send to Word via
AppleScript is packaged up into an AppleEvent.  When Word’s done processing that AppleEvent, it packages up
a reply, puts the reply in the AppleEvent, and sends it back “over the fence”
as it were.  With VBA, interprocess
communication is done using the Component Object Model (or COM).  COM provides programming interfaces
that are amenable to being called directly within the programming languages we
use to write the applications themselves. 
COM is often referred to as a “binary interface,” because it lends
itself to direct programmatic manipulation in this way.

In essence, COM was designed so that objects in the system would look an
awful lot like they way objects are implemented in the C++ programming
language.  In fact, you can take a
file written in the COM Object Description Language (ODL) and, using a
command-line tool, generate header files that can be included directly into C++
code.

For interprocess communication, COM has a technical problem that I won’t
describe in detail, but involves the fact that applications have fences built
around them.  Basically, one
program cannot directly call a binary interface that’s provided by another
program.  That would be like
reaching through the fence instead of passing something over the fence.  To solve this, COM uses something
called “marshalling”.  None of this
has to be done for AppleScript, because AppleScript doesn’t provide the same
kind of binary interface.

So, COM is messy for interprocess communication, but it’s really
nice for intraprocess communication.  AppleScript isn’t quite so messy when it comes to interprocess
communication, but it’s a royal PITA when it comes to intraprocess
communication.  What do I mean by intraprocess
communication?  Well intraprocess
communication is how various software components in an application, and in any
shared libraries used by that application, communicate with each other.  In this case, the kind of binary
interface that COM provides is perfect. 
To the programmer, the COM objects look like any other component in the
program (which is where the name “COM” comes from).

This is, in fact, how all of the external wizards that we ship are
implemented.  They’re written in
C++ and they talk to Word, or Excel or PowerPoint, via the binary interfaces
each of the applications expose through the VBA implantations of their
respective object models.

For those of you who are familiar with COM and the standard programming
interfaces that COM defines, I should point out that all of these interfaces
are “dual dispatch” interfaces. 
While every object does implement the standard IDispatch interface,
every method and every property of every object is also available through
direct programmatic manipulation through individual interfaces that descend
from IDispatch.  The point is that
neither the Wizards we ship nor the AppleScript implementation goes through
IDispatch::Invoke.  Rather, both
call the appropriate method or property implementation directly on the
appropriate object itself.

If you don’t understand anything in the above paragraph, don’t worry.  The important point to understand is
that, in light of the previous point about the design of an object model for
any given application, it makes perfect sense to use an existing VBA
implementation of an object model to expose that same object model via
AppleScript.  In fact, not only
does it make perfect sense to do this, it’s the most efficient way you can
possibly do the job.  The VBA
implementation already packages up the object model in a manner that lends
itself to direct programmatic manipulation.

It makes so much sense to do things this way that you can even get lazy
about it.  You can design a
language that describes your object model both in VBA and AppleScript terms,
and then you can write a tool that parses a description file and generates the
code for you.  In only a “few”
(quoted because “few” is relative—there are 215 distinct objects in Word’s
object model, and I won’t even begin to count all the properties and methods on
those objects) instances do you have to get your hands dirty and write some
code yourself.

As an insight into Jim Murphy’s sense of humor, the object description language
is called “Sid,” and the tool that compiles files written in that language is
called “Nancy.”  One of these days,
I’m going to have to go rent that movie.

 

Rick

Comments

  • Anonymous
    August 04, 2004
    Save your money.