SimpleServer
Volume Number: 10
Issue Number: 8
Column Tag: New Apple Technology
A Simple OLE 2.01 Object Server Revealed
By Michael Fanning, Microsoft
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
Code Reuse or Object Reuse?
Code reuse has often been cited as a significant goal of object-oriented
programming. Successful examples of code reuse have been few, despite the
now-widespread adoption of object-oriented languages and techniques. Applications
frameworks, such as MacApp, TCL, and PowerPlant have been reused extensively-but
they’re about the only case of code reuse to which one can point. These examples
represent not only the success, but also the failure, of object-oriented programming
to date: it focused on the reuse of CODE rather than the reuse of OBJECTS. Up to now, it
has been impossible to reuse an object without access to its source code-or at least, a
static library to which one’s own code (written in the same language) could bind at
link-time.
Microsoft Object Linking and Embedding (OLE), version 2.01, addresses this
fundamental problem of reusing code. Using OLE, any object can make its services
available to any other object at run-time. Objects can be mixed, matched, and reused
without regard to the language in which they were written or the vendor from which
they came.
Using OLE, you can define any arbitrary service-an “interface” -which any
object can then share with any other object. For example, you could easily define an
interface for spell-checking which any other object could bind to at run-time, without
knowing (or caring) who wrote the spell-checking service or in what language.
Similarly, you could define an interface for charting data, for recalculating
spreadsheet data, or for rendering photorealistic images.
OLE provides a set of standard services, which are available to any application
that wants to use them. These standard services include interfaces for many features
that are of interest to software developers today, such as compound documents
(documents that include arbitrary data, like a video clip or a CAD blueprint in a word
processor) and Visual Editing (the ability to edit that arbitrary data within the context
of the containing application, without having to switch to another application). These
OLE services provide a structure within which objects interact to reuse each other’s
unique features, thus improving the end-user experience (which is what it’s all
about, after all).
Goal of This Article
This article describes how basic OLE features were implemented in a simple
Macintosh application. The application does not implement every OLE feature; indeed,
one of OLE’s most important features-Visual Editing-has not been implemented in the
sample to ensure that it is as easy as possible to examine and understand. (Visual
Editing will be added to the sample in a subsequent article). Enough features have been
added to give you the flavor of OLE development and to let you understand its basic
concepts and idioms. The OLE 2.01 SDK CD that accompanies this issue of MacTech will
provide you the means to get started in adding OLE support to your application today.
It is important to note that an OLE application is just that: an application much
like those your firm develops and sells today. SimpleServer is a stand-alone,
double-clickable application designed to provide simple drawing services to any other
object. Just as we did with this sample application, you can easily add OLE support to
your application, without changing its nature, its distribution, or its business model.
Now, let’s put on our code snorkels, and dive in!
SimpleServer, Revealed
SimpleServer is a minimal implementation of an OLE 2.0 embeddable object
server written in C++. The implementation of OLE in this sample code allows
SimpleServer objects to be embedded in OLE containers and saved in compound
documents.
The first section of this article contains a general description of the sample code
source files. The second tracks the communication between SimpleServer and an OLE
container as a SimpleServer object is embedded. Finally, I’ve included a glossary of
important OLE concepts: consult this section if you come across an unfamiliar term. If
you’re new to OLE, or just that kind of person, you might want to go ahead and review
this section before reading on. The rest of us who leave manuals wrapped in plastic
until all hell breaks loose will continue on without you.
The SimpleServer Source Files
The SimpleServer source files (located in “Sample Code
(OLE):SimpleServer:src”on the OLE SDK CD bound in this issue) can be broken out
into three groups: a set of source files which provide the basic structure and
functionality of the application; a set of sources which contain the OLE-specific code,
file utilities, error reporting, and debug information; and sources taken from the
sample OLE UI library.
Note: Every OLE interface is in its own file, for easy review, named CInterface
(where Interface is the name of the OLE interface implemented in the file).
The Basic Application
IUnknown - IUnknown is the abstract base class for all SimpleServer objects.
Defined in the OLE header compobj.h, its methods (like the member functions of all
standard OLE interfaces) are pure virtual and are implemented in SimpleServer’s
derived object classes.
Class Source Files
C application.cp - Methods used to initialize the Mac Toolbox, set up menus,
install AppleEvent handlers, and handle events and menu commands. CApplication
tracks the life of the application object through its reference-counting methods
(AddRef and Release), registers SimpleServer in the Registration Database, and
instantiates the application’s ClassFactory.
These tasks (and other OLE-specific tasks mentioned in the next sections) will be
covered in more detail later in the article.
C window.cp (derived from IUnknown) - A window class containing methods for
handling mouse, keyboard, update, and activate events.
Cdocument.cp (derived from CWindow) - A document class which instantiates the
primary interfaces associated with an embeddable object: IDataObject, IOleObject, and
IPersistStorage The CDocument class acquires all the interface pointers it will use
during a document’s lifetime when a document is initialized. These pointers are stored
as member data and are released when the document is deallocated.
Ctoolbar.cp (derived from C window) - A small toolbar class.
CShape.cp (derived from CObject) - A shape class. Shape objects are drawn upon
a document and maintained in a linked list associated with a CDocument.
Utility Source Files
AEvents.cp - Handlers for the required AppleEvents and one special OLE 2.01
AppleEvent (‘EMBD’) which indicates the application has been launched as a result of
an embedding.
OleErrors.cp - Error routines for evaluating results of Toolbox and OLE function
calls. Also contains functions which return human-readable strings for SCODES.
Debug.h/Debug.c - A set of macros for asserting; includes macros for dumping
messages to the OLE LRPCSpy utility on entering an interface method or calling AddRef
or Release. (LRPCSpy is described in detail later in the article).
Utitilies.cp - Utilities for detecting System 7. Contains two OLE-specific
utilities: one for freeing pointers to blocks of memory allocated by OLE and another for
writing to the OLE registration database.
SimpleServer.r - Resource file for SimpleServer.
OLE-Specific Source Files
CClassFactory.cp - Implementation of the IClassFactory interface.
CDataObject.cp - Implementation of the IDataObject interface, which is used to
transfer data to and from an OLE object. IDataObject methods set and retrieve data
(GetData, SetData), enumerate supported data formats (EnumFormatEtc), and
establish connections between an object and its container.
COleObject.cp - Implementation of IOleObject, the primary means by which an
embedded object provides functionality to its container.
CPersistStorage.cp - Implementation of IPersistStorage. Objects generated by a
server’s IClassFactory::CreateInstance method are initialized by calling either
IPersistStorage::InitNew or IPersistStorage::Load.
OleMessageFilter.cp - Installs a minimal implementation of the IMessageFilter
interface, which allows an application to handle incoming Macintosh events while
waiting for synchronous OLE calls to complete. Adding IMessageFilter is
straightforward: the OLE Sample User Interface Library contains a standard
implementation (in msgfiltr.c) that an application can use without modification.
UI Source Files : The following files included in the SimpleServer project are
taken from the OLE Sample UI source code: busy.c, common.c, geticon.c, msgfiltr.c,
ole2ui.c, ole2ui.r, olestd.c, regdb.c, utility.c.
The OLE Sample User Interface Library contains source code for the standard OLE
dialogs (for example, the Insert Object and Paste Special dialogs), as well as helper
functions for memory management, object feedback (the way an object appears in
various editing states), linking, data transfer, debugging and more. A full discussion of
the UI Library is beyond the scope of this article. For more information, see the “OLE
2.01 UI Help” help file included with the OLE SDK.
Running SimpleServer
SimpleServer is a regular Macintosh application. It can be launched and used like
any other Mac application. Go ahead and try it. It is already built and can be found in
the Applications folder on the OLE SDK CD.
Stepping Through SimpleServer
SimpleServer comes with a ThinkC Project and an MPW makefile. (NOTE: In
order to link SimpleServer in ThinkC, you’ll need to add OutputDebugString to
Ole2Lib.near.debug.v before compiling and linking. To make the change, open the .v file
in any text editor, add a newline, and type out the function name (with matching case).
The function prototype is located in the SimpleServer source file, Debug.h. If you’d
like to use OutputDebugString in your own application, you’ll need to include a
function prototype as well.)
After compiling, launch SimpleServer and set a breakpoint on the first statement
of main() in file main.cp. To see how an OLE server typically handles startup, step
through its initialization routine. Eventually, SimpleServer will fall into its event
loop where you can set a breakpoint on the call to gApp->DisposeApplication and step
through its shutdown code.
Here’s a synopsis of what SimpleServer does on startup and exit (calls
to the OLE API are marked in boldface):
1. main(), main.cp - Instantiates a CApplication object.
• The constructor for CApplication does nothing more than initialize member
variables (this is consistent across all SimpleServer class constructors).
2. CApplication::InitApplication - Initializes the CApplication object
• Calls standard Macintosh initialization routines.
• Retrieves and displays application menus.
• Installs handlers for the required AppleEvents and the OLE ‘EMBD’ event.
• Instantiates a toolbar.
• Calls OleInitApplication.
3. CApplication::OleInitApplication - Initializes OLE
• Calls InitOleManager. This routine loads OLE into memory if it is not already
present, registers application context information and sets up a table which
exposes the OLE API to the application. Passing the OLEMGR_BIND_NORMAL flag
specifies a normal launch, bind, and AddRef of the OLE libraries. A call to
InitOleManager is always balanced by a call to UninitOleManager in an
application’s shutdown code.
• Calls OleInitialize. This API fully initializes OLE. At this point, OLE functions can
be called by an application. An argument of NULL for pMalloc indicates that OLE
should use its default implementation of IMalloc to handle process memory. An
application always calls OleUninitialize in its shutdown code to balance a call to
OleInitialize.
• Calls OleUpdateRegDB.
4. CApplication::OleUpdateRegDB - Updates the Registration Database
• Opens the OLE 1.0 registration database (“Embedding Preferences”) and
registers SimpleServer if references to it do not already exist in the file.
• Opens the OLE 2.01 registration database (“Registration Database”) and
registers SimpleServer, if necessary. A helper function, WriteToOleReg, is used
to handle this procedure and can be found in Utilities.cp.
SimpleServer registers itself in the OLE 1.0 Embedding Preferences because OLE
provides a compatibility layer that allows OLE 1 and 2 applications to interact
with each other transparently. SimpleServer objects can be embedded in all OLE
1.0 client applications.
5. CApplication::RegisterClassFactory - Instantiates and registers the ClassFactory
• Instantiates an instance of CClassFactory and assigns its pointer to a CApplication
member variable.
• Calls CoRegisterClassObject with SimpleServer’s CLSID and a pointer to the
just-instantiated class factory. Another CApplication member variable
(m_CoRegisterResult) will be filled out with a value identifying the object class.
This value is passed to CoRevokeClassObject in order to revoke the class
registration on application shutdown. After calling CoRegisterClassObject, other
OLE applications can now connect to SimpleServer’s object class.
6. CApplication::OleMessageFilterRegister - Registers the MessageFilter
• Instantiates and registers a standard implementation of the IMessageFilter
interface. The OLE libraries continually call an application’s
IMessageFilter::MessagePending method while waiting for a reply from a
synchronous OLE call. This allows an application the opportunity to process
critical events such as update, activate, and os events. Applications call
WaitNextEvent within this method (with a sleep parameter greater than zero)
and act on the ‘urgent’ events listed above.
7. CApplication::OleLockApplication - Locks the CApplication object in memory
• Calls CoLockObjectExternal. This routine sets a strong reference on the
CApplication object which explicitly locks the application in memory on behalf of
the user (a strong reference dictates that an object will remain in memory even
if its reference count falls to zero). Once a context for the launch has been
established (that is, whether it was launched from the Finder or as the result of
an embedding), the CApplication will be unlocked and the life of the application
will be subject to the standard rules of reference counting. Note the ‘artificial’
AddRef immediately preceding the call and the Release following it. This strategy
applies an artificial weak reference to the CApplication object for purposes
similar to CoLockObjectExternal and should only be used within the scope of a
single function.
8. HandleOpenApplication, AEvents.cp - SimpleServer processes an ‘oapp’ AE
• In its ‘oapp’ handler, SimpleServer creates a document
(CApplication::CreateNewDocument).
9. CApplication::CreateNewDocument - Creates and initializes a new document
• Instantiates a new CDocument object.
• Calls CWindow::InitWindow in order to create a window (CDocument is derived
from CWindow). The CDocument this pointer is retained by a call to SetWRefCon .
• Instantiates CDataObject, COleObject, and CPersistStorage; their pointers are
retained as member data.
• Increments the application document count.
10. CApplication::DisposeApplication, CApplication::OleCleanup - Cleanup on
shutdown
• Calls CApplication::OleCleanup to revoke the class object from the class factory
table. Revokes the application IMessageFilter. Calls OleUninitialize and
UninitOleManager.
After launching and exiting the sample, SimpleServer is registered in the
Registration Database. Once registered, containers that support insertable objects can
now be used to embed a SimpleServer shape object, which is what we’ll look at next.
Embedding a SimpleServer Object
LRPCSpy
The OLE SDK includes a utility, called LRPCSpy, which we’ll use to track the
communication between SimpleServer and its container as a SimpleServer object is
embedded. LRPC stands for lightweight remote procedure call, the mechanism by which
OLE transfers data between processes (referred to as “lightweight” because the
communication is currently restricted to processes on one machine). OLE has been
carefully designed so that the LRPC mechanism can be easily replaced with a
fully-distributable RPC based on OSF DCE RPC (Open Software Foundation Distributed